diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/nosql/MqttAttributesRequestNoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/nosql/MqttAttributesRequestNoSqlIntegrationTest.java deleted file mode 100644 index 7ed1a0adb4..0000000000 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/nosql/MqttAttributesRequestNoSqlIntegrationTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright © 2016-2021 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 { -} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/nosql/MqttServerSideRpcNoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/rpc/nosql/MqttServerSideRpcNoSqlIntegrationTest.java deleted file mode 100644 index c091bc3eb0..0000000000 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/nosql/MqttServerSideRpcNoSqlIntegrationTest.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright © 2016-2021 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.nosql; - -import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; - -/** - * Created by Valerii Sosliuk on 8/22/2017. - */ -@DaoNoSqlTest -public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest { -} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlIntegrationTest.java deleted file mode 100644 index f29507dcc5..0000000000 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlIntegrationTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright © 2016-2021 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 { -} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlProtoIntegrationTest.java deleted file mode 100644 index 9031ccdb69..0000000000 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlProtoIntegrationTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright © 2016-2021 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.AbstractMqttAttributesProtoIntegrationTest; - -@DaoNoSqlTest -public class MqttAttributesNoSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { -} diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java new file mode 100644 index 0000000000..89ef53b19d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java @@ -0,0 +1,191 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport; + +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.DeviceProfileProvisionType; +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.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration; +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.DeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.controller.AbstractControllerTest; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Slf4j +public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest { + + protected static final String MQTT_URL = "tcp://localhost:1883"; + protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; + + protected static final AtomicInteger atomicInteger = new AtomicInteger(2); + + protected static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + + "\n" + + "package test;\n" + + "\n" + + "message PostTelemetry {\n" + + " string key1 = 1;\n" + + " bool key2 = 2;\n" + + " double key3 = 3;\n" + + " int32 key4 = 4;\n" + + " JsonObject key5 = 5;\n" + + "\n" + + " message JsonObject {\n" + + " int32 someNumber = 6;\n" + + " repeated int32 someArray = 7;\n" + + " NestedJsonObject someNestedObject = 8;\n" + + " message NestedJsonObject {\n" + + " string key = 9;\n" + + " }\n" + + " }\n" + + "}"; + + protected static final String DEVICE_ATTRIBUTES_PROTO_SCHEMA = "syntax =\"proto3\";\n" + + "\n" + + "package test;\n" + + "\n" + + "message PostAttributes {\n" + + " string key1 = 1;\n" + + " bool key2 = 2;\n" + + " double key3 = 3;\n" + + " int32 key4 = 4;\n" + + " JsonObject key5 = 5;\n" + + "\n" + + " message JsonObject {\n" + + " int32 someNumber = 6;\n" + + " repeated int32 someArray = 7;\n" + + " NestedJsonObject someNestedObject = 8;\n" + + " message NestedJsonObject {\n" + + " string key = 9;\n" + + " }\n" + + " }\n" + + "}"; + + protected Tenant savedTenant; + protected User tenantAdmin; + + protected Device savedDevice; + protected String accessToken; + + protected DeviceProfile deviceProfile; + + protected void processAfterTest() throws Exception { + loginSysAdmin(); + if (savedTenant != null) { + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); + } + } + + protected List getKvProtos(List expectedKeys) { + List 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 T doExecuteWithRetriesAndInterval(SupplierWithThrowable 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 get() throws Throwable; + } +} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java similarity index 92% rename from application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java rename to application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java index 16ebfb8c7c..539a1da055 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt; +package org.thingsboard.server.transport; import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; import org.junit.BeforeClass; @@ -28,8 +28,8 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - "org.thingsboard.server.mqtt.*.nosql.*Test"}) -public class MqttNoSqlTestSuite { + "org.thingsboard.server.transport.*.telemetry.timeseries.nosql.*Test"}) +public class TransportNoSqlTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( diff --git a/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java similarity index 70% rename from application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java rename to application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java index 0dd341f3a6..d16bbc3885 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt; +package org.thingsboard.server.transport; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -26,15 +26,15 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - "org.thingsboard.server.mqtt.rpc.sql.*Test", - "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test", - "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", - "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", - "org.thingsboard.server.mqtt.attributes.request.sql.*Test", - "org.thingsboard.server.mqtt.claim.sql.*Test", - "org.thingsboard.server.mqtt.provision.sql.*Test" + "org.thingsboard.server.transport.*.rpc.sql.*Test", + "org.thingsboard.server.transport.*.telemetry.timeseries.sql.*Test", + "org.thingsboard.server.transport.*.telemetry.attributes.sql.*Test", + "org.thingsboard.server.transport.*.attributes.updates.sql.*Test", + "org.thingsboard.server.transport.*.attributes.request.sql.*Test", + "org.thingsboard.server.transport.*.claim.sql.*Test", + "org.thingsboard.server.transport.*.provision.sql.*Test" }) -public class MqttSqlTestSuite { +public class TransportSqlTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java new file mode 100644 index 0000000000..ff64d70423 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java @@ -0,0 +1,175 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.junit.Assert; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +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.AllowCreateNewDevicesDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +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.DeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; +import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.transport.AbstractTransportIntegrationTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@Slf4j +public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest { + + protected void processBeforeTest(String deviceName, CoapDeviceType coapDeviceType, TransportPayloadType payloadType) throws Exception { + this.processBeforeTest(deviceName, coapDeviceType, payloadType, null, null, DeviceProfileProvisionType.DISABLED, null, null); + } + + protected void processBeforeTest(String deviceName, + CoapDeviceType coapDeviceType, + TransportPayloadType payloadType, + String telemetryProtoSchema, + String attributesProtoSchema, + DeviceProfileProvisionType provisionType, + String provisionKey, String provisionSecret + ) 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"); + + if (coapDeviceType != null) { + DeviceProfile coapDeviceProfile = createCoapDeviceProfile(payloadType, coapDeviceType, attributesProtoSchema, provisionType, provisionKey, provisionSecret, telemetryProtoSchema); + deviceProfile = doPost("/api/deviceProfile", coapDeviceProfile, DeviceProfile.class); + device.setType(deviceProfile.getName()); + device.setDeviceProfileId(deviceProfile.getId()); + } + + 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); + + } + + protected DeviceProfile createCoapDeviceProfile(TransportPayloadType transportPayloadType, CoapDeviceType coapDeviceType, + String attributesProtoSchema, DeviceProfileProvisionType provisionType, + String provisionKey, String provisionSecret, String telemetryProtoSchema) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setName(transportPayloadType.name()); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setProvisionType(provisionType); + deviceProfile.setProvisionDeviceKey(provisionKey); + deviceProfile.setDescription(transportPayloadType.name() + " Test"); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); + deviceProfile.setTransportType(DeviceTransportType.COAP); + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = new CoapDeviceProfileTransportConfiguration(); + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration; + if (CoapDeviceType.DEFAULT.equals(coapDeviceType)) { + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = new DefaultCoapDeviceTypeConfiguration(); + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; + if (TransportPayloadType.PROTOBUF.equals(transportPayloadType)) { + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = new ProtoTransportPayloadConfiguration(); + if (StringUtils.isEmpty(telemetryProtoSchema)) { + telemetryProtoSchema = DEVICE_TELEMETRY_PROTO_SCHEMA; + } + if (StringUtils.isEmpty(attributesProtoSchema)) { + attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA; + } + protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema); + protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema); + transportPayloadTypeConfiguration = protoTransportPayloadConfiguration; + } else { + transportPayloadTypeConfiguration = new JsonTransportPayloadConfiguration(); + } + defaultCoapDeviceTypeConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); + coapDeviceTypeConfiguration = defaultCoapDeviceTypeConfiguration; + } else { + coapDeviceTypeConfiguration = new EfentoCoapDeviceTypeConfiguration(); + } + coapDeviceProfileTransportConfiguration.setCoapDeviceTypeConfiguration(coapDeviceTypeConfiguration); + deviceProfileData.setTransportConfiguration(coapDeviceProfileTransportConfiguration); + DeviceProfileProvisionConfiguration provisionConfiguration; + switch (provisionType) { + case ALLOW_CREATE_NEW_DEVICES: + provisionConfiguration = new AllowCreateNewDevicesDeviceProfileProvisionConfiguration(provisionSecret); + break; + case CHECK_PRE_PROVISIONED_DEVICES: + provisionConfiguration = new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(provisionSecret); + break; + case DISABLED: + default: + provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(provisionSecret); + break; + } + deviceProfileData.setProvisionConfiguration(provisionConfiguration); + deviceProfileData.setConfiguration(configuration); + deviceProfile.setProfileData(deviceProfileData); + deviceProfile.setDefault(false); + deviceProfile.setDefaultRuleChainId(null); + return deviceProfile; + } + + protected CoapClient getCoapClient(FeatureType featureType) { + return new CoapClient(getFeatureTokenUrl(accessToken, featureType)); + } + + protected CoapClient getCoapClient(String featureTokenUrl) { + return new CoapClient(featureTokenUrl); + } + + protected String getFeatureTokenUrl(String token, FeatureType featureType) { + return COAP_BASE_URL + token + "/" + featureType.name().toLowerCase(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java new file mode 100644 index 0000000000..53d345ec3d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/AbstractCoapAttributesIntegrationTest.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoapIntegrationTest { + + 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, CoapDeviceType coapDeviceType, TransportPayloadType payloadType) throws Exception { + super.processBeforeTest(deviceName, coapDeviceType, payloadType); + } + + protected void processAfterTest() throws Exception { + super.processAfterTest(); + } + + protected List 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 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(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestIntegrationTest.java new file mode 100644 index 0000000000..0c5a7d1767 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestIntegrationTest.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.request; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.coap.attributes.AbstractCoapAttributesIntegrationTest; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +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 AbstractCoapAttributesRequestIntegrationTest extends AbstractCoapAttributesIntegrationTest { + + protected static final long CLIENT_REQUEST_TIMEOUT = 60000L; + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Request attribute values from the server", null, null); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testRequestAttributesValuesFromTheServer() throws Exception { + processTestRequestAttributesValuesFromTheServer(); + } + + protected void processTestRequestAttributesValuesFromTheServer() throws Exception { + postAttributes(); + + long start = System.currentTimeMillis(); + long end = System.currentTimeMillis() + 5000; + + List savedAttributeKeys = null; + while (start <= end) { + savedAttributeKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {}); + if (savedAttributeKeys.size() == 5) { + break; + } + Thread.sleep(100); + start += 100; + } + assertNotNull(savedAttributeKeys); + + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; + String featureTokenUrl = getFeatureTokenUrl(accessToken, FeatureType.ATTRIBUTES) + "?clientKeys=" + keys + "&sharedKeys=" + keys; + CoapClient client = getCoapClient(featureTokenUrl); + + CoapResponse getAttributesResponse = client.setTimeout(CLIENT_REQUEST_TIMEOUT).get(); + validateResponse(getAttributesResponse); + } + + protected void postAttributes() throws Exception { + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); + CoapClient client = getCoapClient(FeatureType.ATTRIBUTES); + CoapResponse coapResponse = client.setTimeout(CLIENT_REQUEST_TIMEOUT).post(POST_ATTRIBUTES_PAYLOAD.getBytes(), MediaTypeRegistry.APPLICATION_JSON); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + } + + protected void validateResponse(CoapResponse getAttributesResponse) throws InvalidProtocolBufferException { + assertEquals(CoAP.ResponseCode.CONTENT, getAttributesResponse.getCode()); + 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(getAttributesResponse.getPayload(), StandardCharsets.UTF_8))); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestJsonIntegrationTest.java new file mode 100644 index 0000000000..eb479fa2ad --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestJsonIntegrationTest.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.request; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; + +@Slf4j +public abstract class AbstractCoapAttributesRequestJsonIntegrationTest extends AbstractCoapAttributesRequestIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Request attribute values from the server json", CoapDeviceType.DEFAULT, TransportPayloadType.JSON); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testRequestAttributesValuesFromTheServer() throws Exception { + super.testRequestAttributesValuesFromTheServer(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestProtoIntegrationTest.java new file mode 100644 index 0000000000..4fbe103430 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestProtoIntegrationTest.java @@ -0,0 +1,153 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.request; + +import com.github.os72.protobuf.dynamic.DynamicSchema; +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.InvalidProtocolBufferException; +import com.squareup.wire.schema.internal.parser.ProtoFileElement; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.junit.After; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.gen.transport.TransportProtos; + +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; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Slf4j +public abstract class AbstractCoapAttributesRequestProtoIntegrationTest extends AbstractCoapAttributesRequestIntegrationTest { + + public static final String ATTRIBUTES_SCHEMA_STR = "syntax =\"proto3\";\n" + + "\n" + + "package test;\n" + + "\n" + + "message PostAttributes {\n" + + " string attribute1 = 1;\n" + + " bool attribute2 = 2;\n" + + " double attribute3 = 3;\n" + + " int32 attribute4 = 4;\n" + + " JsonObject attribute5 = 5;\n" + + "\n" + + " message JsonObject {\n" + + " int32 someNumber = 6;\n" + + " repeated int32 someArray = 7;\n" + + " NestedJsonObject someNestedObject = 8;\n" + + " message NestedJsonObject {\n" + + " string key = 9;\n" + + " }\n" + + " }\n" + + "}"; + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testRequestAttributesValuesFromTheServer() throws Exception { + super.processBeforeTest("Test Request attribute values from the server proto", CoapDeviceType.DEFAULT, + TransportPayloadType.PROTOBUF, null, ATTRIBUTES_SCHEMA_STR, DeviceProfileProvisionType.DISABLED, null, null); + processTestRequestAttributesValuesFromTheServer(); + } + + protected void postAttributes() throws Exception { + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); + CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapTransportConfiguration.getCoapDeviceTypeConfiguration(); + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(ATTRIBUTES_SCHEMA_STR); + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); + + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); + assertNotNull(nestedJsonObjectBuilderDescriptor); + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); + + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); + assertNotNull(jsonObjectBuilderDescriptor); + DynamicMessage jsonObject = jsonObjectBuilder + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) + .build(); + + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); + assertNotNull(postAttributesMsgDescriptor); + DynamicMessage postAttributesMsg = postAttributesBuilder + .setField(postAttributesMsgDescriptor.findFieldByName("attribute1"), "value1") + .setField(postAttributesMsgDescriptor.findFieldByName("attribute2"), true) + .setField(postAttributesMsgDescriptor.findFieldByName("attribute3"), 42.0) + .setField(postAttributesMsgDescriptor.findFieldByName("attribute4"), 73) + .setField(postAttributesMsgDescriptor.findFieldByName("attribute5"), jsonObject) + .build(); + byte[] payload = postAttributesMsg.toByteArray(); + CoapClient client = getCoapClient(FeatureType.ATTRIBUTES); + CoapResponse coapResponse = client.setTimeout(CLIENT_REQUEST_TIMEOUT).post(payload, MediaTypeRegistry.APPLICATION_JSON); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + } + + protected void validateResponse(CoapResponse getAttributesResponse) throws InvalidProtocolBufferException { + TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg(); + TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(getAttributesResponse.getPayload()); + assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId()); + List expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); + List expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); + List actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); + List actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); + } + + private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { + TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder(); + List tsKvProtoList = getTsKvProtoList(); + result.addAllClientAttributeList(tsKvProtoList); + result.addAllSharedAttributeList(tsKvProtoList); + result.setRequestId(0); + return result.build(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestJsonSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestJsonSqlIntegrationTest.java new file mode 100644 index 0000000000..5e9b788ec0 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestJsonSqlIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.request.sql; + +import org.thingsboard.server.transport.coap.attributes.request.AbstractCoapAttributesRequestJsonIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapAttributesRequestJsonSqlIntegrationTest extends AbstractCoapAttributesRequestJsonIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestProtoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestProtoSqlIntegrationTest.java new file mode 100644 index 0000000000..e08a09bcb2 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestProtoSqlIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.request.sql; + +import org.thingsboard.server.transport.coap.attributes.request.AbstractCoapAttributesRequestProtoIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapAttributesRequestProtoSqlIntegrationTest extends AbstractCoapAttributesRequestProtoIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestSqlIntegrationTest.java new file mode 100644 index 0000000000..bb01f20b11 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/sql/CoapAttributesRequestSqlIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.request.sql; + +import org.thingsboard.server.transport.coap.attributes.request.AbstractCoapAttributesRequestIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapAttributesRequestSqlIntegrationTest extends AbstractCoapAttributesRequestIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesIntegrationTest.java new file mode 100644 index 0000000000..1bf30659b8 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesIntegrationTest.java @@ -0,0 +1,141 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.updates; + +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapHandler; +import org.eclipse.californium.core.CoapObserveRelation; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.Request; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.coap.attributes.AbstractCoapAttributesIntegrationTest; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.util.mapping.JacksonUtil; + +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 AbstractCoapAttributesUpdatesIntegrationTest extends AbstractCoapAttributesIntegrationTest { + + private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}"; + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Subscribe to attribute updates", null, null); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { + processTestSubscribeToAttributesUpdates(); + } + + protected void processTestSubscribeToAttributesUpdates() throws Exception { + + CoapClient client = getCoapClient(FeatureType.ATTRIBUTES); + + CountDownLatch latch = new CountDownLatch(1); + TestCoapCallback testCoapCallback = new TestCoapCallback(latch); + + Request request = Request.newGet().setObserve(); + request.setType(CoAP.Type.CON); + CoapObserveRelation observeRelation = client.observe(request, testCoapCallback); + + Thread.sleep(1000); + + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); + latch.await(3, TimeUnit.SECONDS); + + validateUpdateAttributesResponse(testCoapCallback); + + latch = new CountDownLatch(1); + + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); + latch.await(3, TimeUnit.SECONDS); + + validateDeleteAttributesResponse(testCoapCallback); + + observeRelation.proactiveCancel(); + assertTrue(observeRelation.isCanceled()); + } + + protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { + assertNotNull(callback.getPayloadBytes()); + assertNotNull(callback.getObserve()); + assertEquals(0, callback.getObserve().intValue()); + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); + assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response)); + } + + protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { + assertNotNull(callback.getPayloadBytes()); + assertNotNull(callback.getObserve()); + assertEquals(1, callback.getObserve().intValue()); + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); + assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response)); + } + + protected static class TestCoapCallback implements CoapHandler { + + private final CountDownLatch latch; + + private Integer observe; + private byte[] payloadBytes; + + public byte[] getPayloadBytes() { + return payloadBytes; + } + + public Integer getObserve() { + return observe; + } + + private TestCoapCallback(CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void onLoad(CoapResponse response) { + assertNotNull(response.getPayload()); + assertEquals(response.getCode(), CoAP.ResponseCode.CONTENT); + observe = response.getOptions().getObserve(); + payloadBytes = response.getPayload(); + latch.countDown(); + } + + @Override + public void onError() { + log.warn("Command Response Ack Error, No connect"); + } + + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesJsonIntegrationTest.java new file mode 100644 index 0000000000..adfef9fbef --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesJsonIntegrationTest.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.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.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; + +@Slf4j +public abstract class AbstractCoapAttributesUpdatesJsonIntegrationTest extends AbstractCoapAttributesUpdatesIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Subscribe to attribute updates", CoapDeviceType.DEFAULT, TransportPayloadType.JSON); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { + super.testSubscribeToAttributesUpdatesFromTheServer(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java new file mode 100644 index 0000000000..378354123e --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.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.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.gen.transport.TransportProtos; + +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 AbstractCoapAttributesUpdatesProtoIntegrationTest extends AbstractCoapAttributesUpdatesIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Subscribe to attribute updates", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { + processTestSubscribeToAttributesUpdates(); + } + + protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException { + assertNotNull(callback.getPayloadBytes()); + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); + List tsKvProtoList = getTsKvProtoList(); + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); + + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); + + List actualSharedUpdatedList = actualAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); + List expectedSharedUpdatedList = expectedAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); + + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); + + } + + protected void validateDeleteAttributesResponse(TestCoapCallback 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)); + + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlIntegrationTest.java new file mode 100644 index 0000000000..f0fce3a0c5 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.updates.sql; + +import org.thingsboard.server.transport.coap.attributes.updates.AbstractCoapAttributesUpdatesIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapAttributesUpdatesSqlIntegrationTest extends AbstractCoapAttributesUpdatesIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlJsonIntegrationTest.java new file mode 100644 index 0000000000..9ca721503d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlJsonIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.updates.sql; + +import org.thingsboard.server.transport.coap.attributes.updates.AbstractCoapAttributesUpdatesJsonIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapAttributesUpdatesSqlJsonIntegrationTest extends AbstractCoapAttributesUpdatesJsonIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlProtoIntegrationTest.java new file mode 100644 index 0000000000..cf8e453973 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/sql/CoapAttributesUpdatesSqlProtoIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.attributes.updates.sql; + +import org.thingsboard.server.transport.coap.attributes.updates.AbstractCoapAttributesUpdatesProtoIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapAttributesUpdatesSqlProtoIntegrationTest extends AbstractCoapAttributesUpdatesProtoIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimDeviceTest.java new file mode 100644 index 0000000000..d3334ea150 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimDeviceTest.java @@ -0,0 +1,147 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.claim; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.elements.exception.ConnectorException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +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.security.Authority; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.device.claim.ClaimResponse; +import org.thingsboard.server.dao.device.claim.ClaimResult; + +import java.io.IOException; + +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 AbstractCoapClaimDeviceTest extends AbstractCoapIntegrationTest { + + 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", 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); + } + + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { + log.warn("[testClaimingDevice] Device: {}, Transport type: {}", savedDevice.getName(), savedDevice.getType()); + CoapClient client = getCoapClient(FeatureType.CLAIM); + 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, CoapClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception { + postClaimRequest(client, 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); + + postClaimRequest(client, 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); + } + + private void postClaimRequest(CoapClient client, byte[] payload) throws IOException, ConnectorException { + CoapResponse coapResponse = client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimJsonDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimJsonDeviceTest.java new file mode 100644 index 0000000000..4455feffb0 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimJsonDeviceTest.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.claim; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; + +@Slf4j +public abstract class AbstractCoapClaimJsonDeviceTest extends AbstractCoapClaimDeviceTest { + + @Before + public void beforeTest() throws Exception { + super.processBeforeTest("Test Claim device Json", CoapDeviceType.DEFAULT, TransportPayloadType.JSON); + createCustomerAndUser(); + } + + @After + public void afterTest() throws Exception { + super.afterTest(); + } + + @Test + public void testClaimingDevice() throws Exception { + super.testClaimingDevice(); + } + + @Test + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { + super.testClaimingDeviceWithoutSecretAndDuration(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimProtoDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimProtoDeviceTest.java new file mode 100644 index 0000000000..a9691e6693 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/claim/AbstractCoapClaimProtoDeviceTest.java @@ -0,0 +1,81 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.claim; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.gen.transport.TransportApiProtos; + +@Slf4j +public abstract class AbstractCoapClaimProtoDeviceTest extends AbstractCoapClaimDeviceTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Claim device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); + 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); + } + + @Override + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { + CoapClient client = getCoapClient(FeatureType.CLAIM); + byte[] payloadBytes; + if (emptyPayload) { + TransportApiProtos.ClaimDevice claimDevice = getClaimDevice(0, emptyPayload); + payloadBytes = claimDevice.toByteArray(); + } else { + TransportApiProtos.ClaimDevice claimDevice = getClaimDevice(60000, emptyPayload); + payloadBytes = claimDevice.toByteArray(); + } + TransportApiProtos.ClaimDevice claimDevice = getClaimDevice(1, emptyPayload); + byte[] failurePayloadBytes = claimDevice.toByteArray(); + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); + } + + 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); + } else { + claimDeviceBuilder.setDurationMs(0); + } + return claimDeviceBuilder.build(); + } + + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceJsonSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceJsonSqlTest.java new file mode 100644 index 0000000000..1acec62e8d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceJsonSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.claim.sql; + +import org.thingsboard.server.transport.coap.claim.AbstractCoapClaimJsonDeviceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapClaimDeviceJsonSqlTest extends AbstractCoapClaimJsonDeviceTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceProtoSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceProtoSqlTest.java new file mode 100644 index 0000000000..2b04651f47 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceProtoSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.claim.sql; + +import org.thingsboard.server.transport.coap.claim.AbstractCoapClaimProtoDeviceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapClaimDeviceProtoSqlTest extends AbstractCoapClaimProtoDeviceTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceSqlTest.java new file mode 100644 index 0000000000..9548009ec7 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/claim/sql/CoapClaimDeviceSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.claim.sql; + +import org.thingsboard.server.transport.coap.claim.AbstractCoapClaimDeviceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapClaimDeviceSqlTest extends AbstractCoapClaimDeviceTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/provision/AbstractCoapProvisionJsonDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/provision/AbstractCoapProvisionJsonDeviceTest.java new file mode 100644 index 0000000000..b4a55ae271 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/provision/AbstractCoapProvisionJsonDeviceTest.java @@ -0,0 +1,195 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.provision; + +import com.google.gson.JsonObject; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.elements.exception.ConnectorException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.msg.EncryptionUtil; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.common.transport.util.JsonUtils; +import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +@Slf4j +public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIntegrationTest { + + @Autowired + DeviceCredentialsService deviceCredentialsService; + + @Autowired + DeviceService deviceService; + + @After + public void afterTest() throws Exception { + super.processAfterTest(); + } + + @Test + public void testProvisioningDisabledDevice() throws Exception { + processTestProvisioningDisabledDevice(); + } + + @Test + public void testProvisioningCheckPreProvisionedDevice() throws Exception { + processTestProvisioningCheckPreProvisionedDevice(); + } + + @Test + public void testProvisioningCreateNewDeviceWithoutCredentials() throws Exception { + processTestProvisioningCreateNewDeviceWithoutCredentials(); + } + + @Test + public void testProvisioningCreateNewDeviceWithAccessToken() throws Exception { + processTestProvisioningCreateNewDeviceWithAccessToken(); + } + + @Test + public void testProvisioningCreateNewDeviceWithCert() throws Exception { + processTestProvisioningCreateNewDeviceWithCert(); + } + + @Test + public void testProvisioningWithBadKeyDevice() throws Exception { + processTestProvisioningWithBadKeyDevice(); + } + + + private void processTestProvisioningDisabledDevice() throws Exception { + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.DISABLED, null, null); + byte[] result = createCoapClientAndPublish().getPayload(); + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); + Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").getAsString()); + } + + + private void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); + byte[] result = createCoapClientAndPublish().getPayload(); + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); + + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); + + Assert.assertNotNull(createdDevice); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); + } + + + private void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); + String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\""; + byte[] result = createCoapClientAndPublish(requestCredentials).getPayload(); + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); + + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); + + Assert.assertNotNull(createdDevice); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "ACCESS_TOKEN"); + Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); + } + + + private void processTestProvisioningCreateNewDeviceWithCert() throws Exception { + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); + String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\""; + byte[] result = createCoapClientAndPublish(requestCredentials).getPayload(); + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); + + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); + + Assert.assertNotNull(createdDevice); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "X509_CERTIFICATE"); + + String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); + String sha3Hash = EncryptionUtil.getSha3Hash(cert); + + Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); + + Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); + } + + private void processTestProvisioningCheckPreProvisionedDevice() throws Exception { + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); + byte[] result = createCoapClientAndPublish().getPayload(); + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); + } + + private void processTestProvisioningWithBadKeyDevice() throws Exception { + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); + byte[] result = createCoapClientAndPublish().getPayload(); + JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); + Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").getAsString()); + } + + private CoapResponse createCoapClientAndPublish() throws Exception { + return createCoapClientAndPublish(""); + } + + private CoapResponse createCoapClientAndPublish(String deviceCredentials) throws Exception { + String provisionRequestMsg = createTestProvisionMessage(deviceCredentials); + CoapClient client = getCoapClient(FeatureType.PROVISION); + return postProvision(client, provisionRequestMsg.getBytes()); + } + + + private CoapResponse postProvision(CoapClient client, byte[] payload) throws IOException, ConnectorException { + return client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); + } + + private String createTestProvisionMessage(String deviceCredentials) { + return "{\"deviceName\":\"Test Provision device\",\"provisionDeviceKey\":\"testProvisionKey\", \"provisionDeviceSecret\":\"testProvisionSecret\"" + deviceCredentials + "}"; + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/provision/AbstractCoapProvisionProtoDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/provision/AbstractCoapProvisionProtoDeviceTest.java new file mode 100644 index 0000000000..9f1d8f1f18 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/provision/AbstractCoapProvisionProtoDeviceTest.java @@ -0,0 +1,205 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.provision; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.elements.exception.ConnectorException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.msg.EncryptionUtil; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; +import org.thingsboard.server.gen.transport.TransportProtos.CredentialsDataProto; +import org.thingsboard.server.gen.transport.TransportProtos.CredentialsType; +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceCredentialsMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; + +import java.io.IOException; + +@Slf4j +public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapIntegrationTest { + + @Autowired + DeviceCredentialsService deviceCredentialsService; + + @Autowired + DeviceService deviceService; + + @After + public void afterTest() throws Exception { + super.processAfterTest(); + } + + @Test + public void testProvisioningDisabledDevice() throws Exception { + processTestProvisioningDisabledDevice(); + } + + @Test + public void testProvisioningCheckPreProvisionedDevice() throws Exception { + processTestProvisioningCheckPreProvisionedDevice(); + } + + @Test + public void testProvisioningCreateNewDeviceWithoutCredentials() throws Exception { + processTestProvisioningCreateNewDeviceWithoutCredentials(); + } + + @Test + public void testProvisioningCreateNewDeviceWithAccessToken() throws Exception { + processTestProvisioningCreateNewDeviceWithAccessToken(); + } + + @Test + public void testProvisioningCreateNewDeviceWithCert() throws Exception { + processTestProvisioningCreateNewDeviceWithCert(); + } + + @Test + public void testProvisioningWithBadKeyDevice() throws Exception { + processTestProvisioningWithBadKeyDevice(); + } + + + private void processTestProvisioningDisabledDevice() throws Exception { + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.DISABLED, null, null); + ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); + Assert.assertNotNull(result); + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString()); + } + + private void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); + + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); + + Assert.assertNotNull(createdDevice); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); + } + + private void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); + CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build(); + + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayload()); + + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); + + Assert.assertNotNull(createdDevice); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); + Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.ACCESS_TOKEN); + Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); + } + + private void processTestProvisioningCreateNewDeviceWithCert() throws Exception { + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); + CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build(); + + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayload()); + + Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); + + Assert.assertNotNull(createdDevice); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); + Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.X509_CERTIFICATE); + + String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); + String sha3Hash = EncryptionUtil.getSha3Hash(cert); + + Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); + + Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); + } + + private void processTestProvisioningCheckPreProvisionedDevice() throws Exception { + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); + + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); + + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); + } + + private void processTestProvisioningWithBadKeyDevice() throws Exception { + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); + ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString()); + } + + private CoapResponse createCoapClientAndPublish() throws Exception { + byte[] provisionRequestMsg = createTestProvisionMessage(); + return createCoapClientAndPublish(provisionRequestMsg); + } + + private CoapResponse createCoapClientAndPublish(byte[] provisionRequestMsg) throws Exception { + CoapClient client = getCoapClient(FeatureType.PROVISION); + return postProvision(client, provisionRequestMsg); + } + + private CoapResponse postProvision(CoapClient client, byte[] payload) throws IOException, ConnectorException { + return client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); + } + + private byte[] createTestsProvisionMessage(CredentialsType credentialsType, CredentialsDataProto credentialsData) throws Exception { + return ProvisionDeviceRequestMsg.newBuilder() + .setDeviceName("Test Provision device") + .setCredentialsType(credentialsType != null ? credentialsType : CredentialsType.ACCESS_TOKEN) + .setCredentialsDataProto(credentialsData != null ? credentialsData: CredentialsDataProto.newBuilder().build()) + .setProvisionDeviceCredentialsMsg( + ProvisionDeviceCredentialsMsg.newBuilder() + .setProvisionDeviceKey("testProvisionKey") + .setProvisionDeviceSecret("testProvisionSecret") + ).build() + .toByteArray(); + } + + + private byte[] createTestProvisionMessage() throws Exception { + return createTestsProvisionMessage(null, null); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/provision/sql/CoapProvisionDeviceJsonSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/provision/sql/CoapProvisionDeviceJsonSqlTest.java new file mode 100644 index 0000000000..598041f0ee --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/provision/sql/CoapProvisionDeviceJsonSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.provision.sql; + +import org.thingsboard.server.transport.coap.provision.AbstractCoapProvisionJsonDeviceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapProvisionDeviceJsonSqlTest extends AbstractCoapProvisionJsonDeviceTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/provision/sql/CoapProvisionDeviceProtoSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/provision/sql/CoapProvisionDeviceProtoSqlTest.java new file mode 100644 index 0000000000..cde83c41e7 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/provision/sql/CoapProvisionDeviceProtoSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.provision.sql; + +import org.thingsboard.server.transport.coap.provision.AbstractCoapProvisionProtoDeviceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapProvisionDeviceProtoSqlTest extends AbstractCoapProvisionProtoDeviceTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcDefaultIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcDefaultIntegrationTest.java new file mode 100644 index 0000000000..a4210aa31a --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcDefaultIntegrationTest.java @@ -0,0 +1,89 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.service.security.AccessValidator; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@Slf4j +public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends AbstractCoapServerSideRpcIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("RPC test device", null, null); + } + + @After + public void afterTest() throws Exception { + super.processAfterTest(); + } + + @Test + public void testServerCoapOneWayRpcDeviceOffline() 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 testServerCoapOneWayRpcDeviceDoesNotExist() 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 testServerCoapTwoWayRpcDeviceOffline() 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 testServerCoapTwoWayRpcDeviceDoesNotExist() 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 testServerCoapOneWayRpc() throws Exception { + processOneWayRpcTest(); + } + + @Test + public void testServerCoapTwoWayRpc() throws Exception { + processTwoWayRpcTest(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java new file mode 100644 index 0000000000..a36660267e --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java @@ -0,0 +1,165 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapHandler; +import org.eclipse.californium.core.CoapObserveRelation; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.core.coap.Request; +import org.junit.Assert; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.msg.session.FeatureType; + +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 AbstractCoapServerSideRpcIntegrationTest extends AbstractCoapIntegrationTest { + + protected static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; + + protected Long asyncContextTimeoutToUseRpcPlugin; + + protected void processBeforeTest(String deviceName, CoapDeviceType coapDeviceType, TransportPayloadType payloadType) throws Exception { + super.processBeforeTest(deviceName, coapDeviceType, payloadType); + asyncContextTimeoutToUseRpcPlugin = 10000L; + } + + protected void processOneWayRpcTest() throws Exception { + CoapClient client = getCoapClient(FeatureType.RPC); + client.useCONs(); + + CountDownLatch latch = new CountDownLatch(1); + TestCoapCallback testCoapCallback = new TestCoapCallback(client, latch, true); + + Request request = Request.newGet().setObserve(); + CoapObserveRelation observeRelation = client.observe(request, testCoapCallback); + + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; + String deviceId = savedDevice.getId().getId().toString(); + String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); + Assert.assertTrue(StringUtils.isEmpty(result)); + latch.await(3, TimeUnit.SECONDS); + assertEquals(0, testCoapCallback.getObserve().intValue()); + observeRelation.proactiveCancel(); + assertTrue(observeRelation.isCanceled()); + } + + protected void processTwoWayRpcTest() throws Exception { + CoapClient client = getCoapClient(FeatureType.RPC); + client.useCONs(); + + CountDownLatch latch = new CountDownLatch(1); + TestCoapCallback testCoapCallback = new TestCoapCallback(client, latch, false); + + Request request = Request.newGet().setObserve(); + request.setType(CoAP.Type.CON); + CoapObserveRelation observeRelation = client.observe(request, testCoapCallback); + + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; + String deviceId = savedDevice.getId().getId().toString(); + + String expected = "{\"value1\":\"A\",\"value2\":\"B\"}"; + + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); + latch.await(3, TimeUnit.SECONDS); + + assertEquals(expected, result); + assertEquals(0, testCoapCallback.getObserve().intValue()); + observeRelation.proactiveCancel(); + assertTrue(observeRelation.isCanceled()); + +// // TODO: 3/11/21 Fix test to validate next RPC +// latch = new CountDownLatch(1); +// +// result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); +// latch.await(3, TimeUnit.SECONDS); +// +// assertEquals(expected, result); +// assertEquals(1, testCoapCallback.getObserve().intValue()); + } + + protected void processOnLoadResponse(CoapResponse response, CoapClient client, Integer observe, CountDownLatch latch) { + client.setURI(getRpcResponseFeatureTokenUrl(accessToken, observe)); + client.post(new CoapHandler() { + @Override + public void onLoad(CoapResponse response) { + log.warn("Command Response Ack: {}, {}", response.getCode(), response.getResponseText()); + latch.countDown(); + } + + @Override + public void onError() { + log.warn("Command Response Ack Error, No connect"); + } + }, DEVICE_RESPONSE, MediaTypeRegistry.APPLICATION_JSON); + } + + protected String getRpcResponseFeatureTokenUrl(String token, int requestId) { + return COAP_BASE_URL + token + "/" + FeatureType.RPC.name().toLowerCase() + "/" + requestId; + } + + private class TestCoapCallback implements CoapHandler { + + private final CoapClient client; + private final CountDownLatch latch; + private final boolean isOneWayRpc; + + public Integer getObserve() { + return observe; + } + + private Integer observe; + + private TestCoapCallback(CoapClient client, CountDownLatch latch, boolean isOneWayRpc) { + this.client = client; + this.latch = latch; + this.isOneWayRpc = isOneWayRpc; + } + + @Override + public void onLoad(CoapResponse response) { + log.warn("coap response: {}, {}", response, response.getCode()); + assertNotNull(response.getPayload()); + assertEquals(response.getCode(), CoAP.ResponseCode.CONTENT); + observe = response.getOptions().getObserve(); + if (!isOneWayRpc) { + processOnLoadResponse(response, client, observe, latch); + } else { + latch.countDown(); + } + } + + @Override + public void onError() { + log.warn("Command Response Ack Error, No connect"); + } + + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcJsonIntegrationTest.java new file mode 100644 index 0000000000..5387d3c7bf --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcJsonIntegrationTest.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; + +@Slf4j +public abstract class AbstractCoapServerSideRpcJsonIntegrationTest extends AbstractCoapServerSideRpcIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("RPC test device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON); + } + + @After + public void afterTest() throws Exception { + super.processAfterTest(); + } + + @Test + public void testServerMqttOneWayRpc() throws Exception { + processOneWayRpcTest(); + } + + @Test + public void testServerMqttTwoWayRpc() throws Exception { + processTwoWayRpcTest(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcProtoIntegrationTest.java new file mode 100644 index 0000000000..edbdf69848 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcProtoIntegrationTest.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapHandler; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.concurrent.CountDownLatch; + +@Slf4j +public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends AbstractCoapServerSideRpcIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("RPC test device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); + } + + @After + public void afterTest() throws Exception { + super.processAfterTest(); + } + + @Test + public void testServerMqttOneWayRpc() throws Exception { + processOneWayRpcTest(); + } + + @Test + public void testServerMqttTwoWayRpc() throws Exception { + processTwoWayRpcTest(); + } + + @Override + protected void processOnLoadResponse(CoapResponse response, CoapClient client, Integer observe, CountDownLatch latch) { + client.setURI(getRpcResponseFeatureTokenUrl(accessToken, observe)); + TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() + .setPayload(DEVICE_RESPONSE) + .setRequestId(observe) + .build(); + client.post(new CoapHandler() { + @Override + public void onLoad(CoapResponse response) { + log.warn("Command Response Ack: {}, {}", response.getCode(), response.getResponseText()); + latch.countDown(); + } + + @Override + public void onError() { + log.warn("Command Response Ack Error, No connect"); + } + }, toDeviceRpcResponseMsg.toByteArray(), MediaTypeRegistry.APPLICATION_JSON); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcJsonSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcJsonSqlIntegrationTest.java new file mode 100644 index 0000000000..ec417fa45f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcJsonSqlIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc.sql; + +import org.thingsboard.server.transport.coap.rpc.AbstractCoapServerSideRpcJsonIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapServerSideRpcJsonSqlIntegrationTest extends AbstractCoapServerSideRpcJsonIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcProtoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcProtoSqlIntegrationTest.java new file mode 100644 index 0000000000..9ed0a13019 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcProtoSqlIntegrationTest.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc.sql; + +import org.thingsboard.server.transport.coap.rpc.AbstractCoapServerSideRpcProtoIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + + +@DaoSqlTest +public class CoapServerSideRpcProtoSqlIntegrationTest extends AbstractCoapServerSideRpcProtoIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcSqlIntegrationTest.java new file mode 100644 index 0000000000..b505de2f39 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/rpc/sql/CoapServerSideRpcSqlIntegrationTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.rpc.sql; + +import org.thingsboard.server.transport.coap.rpc.AbstractCoapServerSideRpcDefaultIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class CoapServerSideRpcSqlIntegrationTest extends AbstractCoapServerSideRpcDefaultIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesIntegrationTest.java new file mode 100644 index 0000000000..b01c011b41 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesIntegrationTest.java @@ -0,0 +1,145 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.attributes; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.elements.exception.ConnectorException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.msg.session.FeatureType; + +import java.io.IOException; +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 AbstractCoapAttributesIntegrationTest extends AbstractCoapIntegrationTest { + + private 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", null, null); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testPushAttributes() throws Exception { + List expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); + processAttributesTest(expectedKeys, PAYLOAD_VALUES_STR.getBytes()); + } + + protected void processAttributesTest(List expectedKeys, byte[] payload) throws Exception { + log.warn("[testPushAttributes] Device: {}, Transport type: {}", savedDevice.getName(), savedDevice.getType()); + CoapClient client = getCoapClient(FeatureType.ATTRIBUTES); + + postAttributes(client, payload); + + DeviceId deviceId = savedDevice.getId(); + + long start = System.currentTimeMillis(); + long end = System.currentTimeMillis() + 5000; + + List actualKeys = null; + while (start <= end) { + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {}); + if (actualKeys.size() == expectedKeys.size()) { + break; + } + Thread.sleep(100); + start += 100; + } + assertNotNull(actualKeys); + + Set actualKeySet = new HashSet<>(actualKeys); + + Set expectedKeySet = new HashSet<>(expectedKeys); + + assertEquals(expectedKeySet, actualKeySet); + + String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); + List> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {}); + assertAttributesValues(values, expectedKeySet); + String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); + doDelete(deleteAttributesUrl); + } + + private void postAttributes(CoapClient client, byte[] payload) throws IOException, ConnectorException { + if (payload == null) { + payload = PAYLOAD_VALUES_STR.getBytes(); + } + CoapResponse coapResponse = client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + } + + @SuppressWarnings("unchecked") + protected void assertAttributesValues(List> deviceValues, Set expectedKeySet) throws JsonProcessingException { + for (Map 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 someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); + assertEquals("value", someNestedObject.get("key")); + break; + } + } + } + + private String getAttributesValuesUrl(DeviceId deviceId, Set actualKeySet) { + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesJsonIntegrationTest.java new file mode 100644 index 0000000000..a07d02bcd7 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesJsonIntegrationTest.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.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.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; + +@Slf4j +public abstract class AbstractCoapAttributesJsonIntegrationTest extends AbstractCoapAttributesIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Post Attributes device Json", CoapDeviceType.DEFAULT, TransportPayloadType.JSON); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testPushAttributes() throws Exception { + super.testPushAttributes(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesProtoIntegrationTest.java new file mode 100644 index 0000000000..5f339ccffe --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesProtoIntegrationTest.java @@ -0,0 +1,93 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.attributes; + +import com.github.os72.protobuf.dynamic.DynamicSchema; +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.squareup.wire.schema.internal.parser.ProtoFileElement; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@Slf4j +public abstract class AbstractCoapAttributesProtoIntegrationTest extends AbstractCoapAttributesIntegrationTest { + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testPushAttributes() throws Exception { + super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); + List expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); + CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapTransportConfiguration.getCoapDeviceTypeConfiguration(); + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA); + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); + + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); + assertNotNull(nestedJsonObjectBuilderDescriptor); + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); + + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); + assertNotNull(jsonObjectBuilderDescriptor); + DynamicMessage jsonObject = jsonObjectBuilder + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) + .build(); + + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); + assertNotNull(postAttributesMsgDescriptor); + DynamicMessage postAttributesMsg = postAttributesBuilder + .setField(postAttributesMsgDescriptor.findFieldByName("key1"), "value1") + .setField(postAttributesMsgDescriptor.findFieldByName("key2"), true) + .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 3.0) + .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) + .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) + .build(); + processAttributesTest(expectedKeys, postAttributesMsg.toByteArray()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlIntegrationTest.java new file mode 100644 index 0000000000..4a2b655214 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.attributes.sql; + +import org.thingsboard.server.transport.coap.telemetry.attributes.AbstractCoapAttributesIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class CoapAttributesSqlIntegrationTest extends AbstractCoapAttributesIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlJsonIntegrationTest.java new file mode 100644 index 0000000000..eb4981414e --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlJsonIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.attributes.sql; + +import org.thingsboard.server.transport.coap.telemetry.attributes.AbstractCoapAttributesJsonIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class CoapAttributesSqlJsonIntegrationTest extends AbstractCoapAttributesJsonIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlProtoIntegrationTest.java new file mode 100644 index 0000000000..aabb98cfba --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/sql/CoapAttributesSqlProtoIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.attributes.sql; + +import org.thingsboard.server.transport.coap.telemetry.attributes.AbstractCoapAttributesProtoIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class CoapAttributesSqlProtoIntegrationTest extends AbstractCoapAttributesProtoIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java new file mode 100644 index 0000000000..2c16725f9b --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesIntegrationTest.java @@ -0,0 +1,178 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.timeseries; + +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapClient; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.eclipse.californium.elements.exception.ConnectorException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.common.msg.session.FeatureType; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@Slf4j +public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoapIntegrationTest { + + private 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", null, null); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testPushTelemetry() throws Exception { + processTestPostTelemetry(null, false); + } + + @Test + public void testPushTelemetryWithTs() throws Exception { + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; + processTestPostTelemetry(payloadStr.getBytes(), true); + } + + protected void processTestPostTelemetry(byte[] payloadBytes, boolean withTs) throws Exception { + log.warn("[testPushTelemetry] Device: {}, Transport type: {}", savedDevice.getName(), savedDevice.getType()); + List expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); + CoapClient coapClient = getCoapClient(FeatureType.TELEMETRY); + postTelemetry(coapClient, payloadBytes); + + String deviceId = savedDevice.getId().getId().toString(); + + long start = System.currentTimeMillis(); + long end = System.currentTimeMillis() + 5000; + + List actualKeys = null; + while (start <= end) { + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", new TypeReference<>() {}); + if (actualKeys.size() == expectedKeys.size()) { + break; + } + Thread.sleep(100); + start += 100; + } + assertNotNull(actualKeys); + + Set actualKeySet = new HashSet<>(actualKeys); + Set 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); + } + start = System.currentTimeMillis(); + end = System.currentTimeMillis() + 5000; + Map>> values = null; + while (start <= end) { + values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() {}); + boolean valid = values.size() == expectedKeys.size(); + if (valid) { + for (String key : expectedKeys) { + List> tsValues = values.get(key); + if (tsValues != null && tsValues.size() > 0) { + Object ts = tsValues.get(0).get("ts"); + if (ts == null) { + valid = false; + break; + } + } else { + valid = false; + break; + } + } + } + if (valid) { + break; + } + Thread.sleep(100); + start += 100; + } + assertNotNull(values); + + if (withTs) { + assertTs(values, expectedKeys, 10000, 0); + } + assertValues(values, 0); + } + + private void postTelemetry(CoapClient client, byte[] payload) throws IOException, ConnectorException { + if (payload == null) { + payload = PAYLOAD_VALUES_STR.getBytes(); + } + CoapResponse coapResponse = client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + } + + private void assertTs(Map>> deviceValues, List 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")); + } + + private void assertValues(Map>> deviceValues, int arrayIndex) { + for (Map.Entry>> entry : deviceValues.entrySet()) { + String key = entry.getKey(); + List> tsKv = entry.getValue(); + String value = (String) 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; + } + } + } + + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesJsonIntegrationTest.java new file mode 100644 index 0000000000..ab05773cab --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesJsonIntegrationTest.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.timeseries; + +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.TransportPayloadType; + +@Slf4j +public abstract class AbstractCoapTimeseriesJsonIntegrationTest extends AbstractCoapTimeseriesIntegrationTest { + + @Before + public void beforeTest() throws Exception { + processBeforeTest("Test Post Telemetry device json payload", CoapDeviceType.DEFAULT, TransportPayloadType.JSON); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + + @Test + public void testPushTelemetry() throws Exception { + super.testPushTelemetry(); + } + + @Test + public void testPushTelemetryWithTs() throws Exception { + super.testPushTelemetryWithTs(); + } + + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesProtoIntegrationTest.java new file mode 100644 index 0000000000..2ae686b211 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesProtoIntegrationTest.java @@ -0,0 +1,170 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.timeseries; + +import com.github.os72.protobuf.dynamic.DynamicSchema; +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.squareup.wire.schema.internal.parser.ProtoFileElement; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Test; +import org.thingsboard.server.common.data.CoapDeviceType; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@Slf4j +public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends AbstractCoapTimeseriesIntegrationTest { + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testPushTelemetry() throws Exception { + super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA); + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); + + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); + assertNotNull(nestedJsonObjectBuilderDescriptor); + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); + + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); + assertNotNull(jsonObjectBuilderDescriptor); + DynamicMessage jsonObject = jsonObjectBuilder + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) + .build(); + + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); + assertNotNull(postTelemetryMsgDescriptor); + DynamicMessage postTelemetryMsg = postTelemetryBuilder + .setField(postTelemetryMsgDescriptor.findFieldByName("key1"), "value1") + .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), true) + .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 3.0) + .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) + .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) + .build(); + processTestPostTelemetry(postTelemetryMsg.toByteArray(), false); + } + + @Test + public void testPushTelemetryWithTs() throws Exception { + String schemaStr = "syntax =\"proto3\";\n" + + "\n" + + "package test;\n" + + "\n" + + "message PostTelemetry {\n" + + " int64 ts = 1;\n" + + " Values values = 2;\n" + + " \n" + + " message Values {\n" + + " string key1 = 3;\n" + + " bool key2 = 4;\n" + + " double key3 = 5;\n" + + " int32 key4 = 6;\n" + + " JsonObject key5 = 7;\n" + + " }\n" + + " \n" + + " message JsonObject {\n" + + " int32 someNumber = 8;\n" + + " repeated int32 someArray = 9;\n" + + " NestedJsonObject someNestedObject = 10;\n" + + " message NestedJsonObject {\n" + + " string key = 11;\n" + + " }\n" + + " }\n" + + "}"; + super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, DeviceProfileProvisionType.DISABLED, null, null); + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr); + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); + + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); + assertNotNull(nestedJsonObjectBuilderDescriptor); + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); + + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); + assertNotNull(jsonObjectBuilderDescriptor); + DynamicMessage jsonObject = jsonObjectBuilder + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) + .build(); + + + DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values"); + Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType(); + assertNotNull(valuesDescriptor); + + DynamicMessage valuesMsg = valuesBuilder + .setField(valuesDescriptor.findFieldByName("key1"), "value1") + .setField(valuesDescriptor.findFieldByName("key2"), true) + .setField(valuesDescriptor.findFieldByName("key3"), 3.0) + .setField(valuesDescriptor.findFieldByName("key4"), 4) + .setField(valuesDescriptor.findFieldByName("key5"), jsonObject) + .build(); + + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); + assertNotNull(postTelemetryMsgDescriptor); + DynamicMessage postTelemetryMsg = postTelemetryBuilder + .setField(postTelemetryMsgDescriptor.findFieldByName("ts"), 10000L) + .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) + .build(); + + processTestPostTelemetry(postTelemetryMsg.toByteArray(), true); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlIntegrationTest.java similarity index 71% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlIntegrationTest.java index 2c2a51a614..459e7cae7b 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/nosql/MqttAttributesNoSqlJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlIntegrationTest.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes.nosql; +package org.thingsboard.server.transport.coap.telemetry.timeseries.nosql; +import org.thingsboard.server.transport.coap.telemetry.timeseries.AbstractCoapTimeseriesIntegrationTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; @DaoNoSqlTest -public class MqttAttributesNoSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { +public class CoapTimeseriesNoSqlIntegrationTest extends AbstractCoapTimeseriesIntegrationTest { } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/nosql/MqttAttributesUpdatesNoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlJsonIntegrationTest.java similarity index 70% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/nosql/MqttAttributesUpdatesNoSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlJsonIntegrationTest.java index e4faa4c943..ec4988875b 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/nosql/MqttAttributesUpdatesNoSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlJsonIntegrationTest.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates.nosql; +package org.thingsboard.server.transport.coap.telemetry.timeseries.nosql; +import org.thingsboard.server.transport.coap.telemetry.timeseries.AbstractCoapTimeseriesJsonIntegrationTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; - @DaoNoSqlTest -public class MqttAttributesUpdatesNoSqlIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { +public class CoapTimeseriesNoSqlJsonIntegrationTest extends AbstractCoapTimeseriesJsonIntegrationTest { } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/nosql/MqttClaimDeviceNoSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlProtoIntegrationTest.java similarity index 70% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/nosql/MqttClaimDeviceNoSqlTest.java rename to application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlProtoIntegrationTest.java index a3f15ecf53..46ce05aa7b 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/nosql/MqttClaimDeviceNoSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/nosql/CoapTimeseriesNoSqlProtoIntegrationTest.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim.nosql; +package org.thingsboard.server.transport.coap.telemetry.timeseries.nosql; +import org.thingsboard.server.transport.coap.telemetry.timeseries.AbstractCoapTimeseriesProtoIntegrationTest; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; - @DaoNoSqlTest -public class MqttClaimDeviceNoSqlTest extends AbstractMqttClaimDeviceTest { +public class CoapTimeseriesNoSqlProtoIntegrationTest extends AbstractCoapTimeseriesProtoIntegrationTest { } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlIntegrationTest.java new file mode 100644 index 0000000000..c1f4e4f2c7 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.timeseries.sql; + +import org.thingsboard.server.transport.coap.telemetry.timeseries.AbstractCoapTimeseriesIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class CoapTimeseriesSqlIntegrationTest extends AbstractCoapTimeseriesIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlJsonIntegrationTest.java new file mode 100644 index 0000000000..19b13e2293 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlJsonIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.timeseries.sql; + +import org.thingsboard.server.transport.coap.telemetry.timeseries.AbstractCoapTimeseriesJsonIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class CoapTimeseriesSqlJsonIntegrationTest extends AbstractCoapTimeseriesJsonIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlProtoIntegrationTest.java new file mode 100644 index 0000000000..acec631414 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/sql/CoapTimeseriesSqlProtoIntegrationTest.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.telemetry.timeseries.sql; + +import org.thingsboard.server.transport.coap.telemetry.timeseries.AbstractCoapTimeseriesProtoIntegrationTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +/** + * Created by Valerii Sosliuk on 8/22/2017. + */ +@DaoSqlTest +public class CoapTimeseriesSqlProtoIntegrationTest extends AbstractCoapTimeseriesProtoIntegrationTest { +} diff --git a/application/src/test/java/org/thingsboard/server/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java similarity index 69% rename from application/src/test/java/org/thingsboard/server/mqtt/AbstractMqttIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java index 9882769868..cd074b3197 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/AbstractMqttIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt; +package org.thingsboard.server.transport.mqtt; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; @@ -44,71 +44,17 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; 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 org.thingsboard.server.transport.AbstractTransportIntegrationTest; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Slf4j -public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest { - - protected static final String MQTT_URL = "tcp://localhost:1883"; - - private static final AtomicInteger atomicInteger = new AtomicInteger(2); - - public static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + - "\n" + - "package test;\n" + - "\n" + - "message PostTelemetry {\n" + - " string key1 = 1;\n" + - " bool key2 = 2;\n" + - " double key3 = 3;\n" + - " int32 key4 = 4;\n" + - " JsonObject key5 = 5;\n" + - "\n" + - " message JsonObject {\n" + - " int32 someNumber = 6;\n" + - " repeated int32 someArray = 7;\n" + - " NestedJsonObject someNestedObject = 8;\n" + - " message NestedJsonObject {\n" + - " string key = 9;\n" + - " }\n" + - " }\n" + - "}"; - - public static final String DEVICE_ATTRIBUTES_PROTO_SCHEMA = "syntax =\"proto3\";\n" + - "\n" + - "package test;\n" + - "\n" + - "message PostAttributes {\n" + - " string key1 = 1;\n" + - " bool key2 = 2;\n" + - " double key3 = 3;\n" + - " int32 key4 = 4;\n" + - " JsonObject key5 = 5;\n" + - "\n" + - " message JsonObject {\n" + - " int32 someNumber = 6;\n" + - " repeated int32 someArray = 7;\n" + - " NestedJsonObject someNestedObject = 8;\n" + - " message NestedJsonObject {\n" + - " string key = 9;\n" + - " }\n" + - " }\n" + - "}"; - - protected Tenant savedTenant; - protected User tenantAdmin; - - protected Device savedDevice; - protected String accessToken; +public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest { protected Device savedGateway; protected String gatewayAccessToken; @@ -208,45 +154,6 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest client.publish(topic, message); } - protected List getKvProtos(List expectedKeys) { - List 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, String telemetryProtoSchema, String attributesProtoSchema, @@ -312,34 +219,4 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest builder.addAllKv(kvProtos); return builder.build(); } - - protected T doExecuteWithRetriesAndInterval(SupplierWithThrowable 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 get() throws Throwable; - } } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java similarity index 97% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java index 4065a235c2..edbe2fcd28 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes; +package org.thingsboard.server.transport.mqtt.attributes; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; @@ -21,7 +21,7 @@ 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 org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import java.util.ArrayList; import java.util.List; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java index 002474c59d..03e08b434c 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.request; +package org.thingsboard.server.transport.mqtt.attributes.request; import com.google.protobuf.InvalidProtocolBufferException; import io.netty.handler.codec.mqtt.MqttQoS; @@ -27,7 +27,7 @@ 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 org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest; import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestJsonIntegrationTest.java similarity index 96% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestJsonIntegrationTest.java index ade21db516..c805572e3d 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestJsonIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.request; +package org.thingsboard.server.transport.mqtt.attributes.request; import lombok.extern.slf4j.Slf4j; import org.junit.After; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestProtoIntegrationTest.java similarity index 95% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestProtoIntegrationTest.java index 0547d73f59..ff8b54c79c 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/AbstractMqttAttributesRequestProtoIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.request; +package org.thingsboard.server.transport.mqtt.attributes.request; import com.github.os72.protobuf.dynamic.DynamicSchema; import com.google.protobuf.Descriptors; @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest; import java.util.ArrayList; import java.util.Arrays; @@ -93,7 +94,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends } 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()); + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", AbstractMqttAttributesIntegrationTest.POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; @@ -148,7 +149,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes)); } - protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, AbstractMqttAttributesIntegrationTest.TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder(); attributesRequestBuilder.setClientKeys(keys); @@ -170,7 +171,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); } - protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { + protected void validateClientResponseGateway(MqttAsyncClient client, AbstractMqttAttributesIntegrationTest.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())); @@ -189,7 +190,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); } - protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { + protected void validateSharedResponseGateway(MqttAsyncClient client, AbstractMqttAttributesIntegrationTest.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())); diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestJsonSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestJsonSqlIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestJsonSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestJsonSqlIntegrationTest.java index 4f177d2c80..b6540832be 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestJsonSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestJsonSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.request.sql; +package org.thingsboard.server.transport.mqtt.attributes.request.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; +import org.thingsboard.server.transport.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; @DaoSqlTest public class MqttAttributesRequestJsonSqlIntegrationTest extends AbstractMqttAttributesRequestJsonIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestProtoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestProtoSqlIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestProtoSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestProtoSqlIntegrationTest.java index e90f8e6ad6..4615b75824 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestProtoSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestProtoSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.request.sql; +package org.thingsboard.server.transport.mqtt.attributes.request.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; +import org.thingsboard.server.transport.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; @DaoSqlTest public class MqttAttributesRequestProtoSqlIntegrationTest extends AbstractMqttAttributesRequestProtoIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestSqlIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestSqlIntegrationTest.java index ed8fc563eb..89253462f9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/request/sql/MqttAttributesRequestSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/sql/MqttAttributesRequestSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.request.sql; +package org.thingsboard.server.transport.mqtt.attributes.request.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; +import org.thingsboard.server.transport.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; @DaoSqlTest public class MqttAttributesRequestSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java similarity index 97% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java index 7045e28f62..716c527f05 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates; +package org.thingsboard.server.transport.mqtt.attributes.updates; import com.google.protobuf.InvalidProtocolBufferException; import io.netty.handler.codec.mqtt.MqttQoS; @@ -26,7 +26,7 @@ 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 org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesJsonIntegrationTest.java similarity index 95% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesJsonIntegrationTest.java index 81021cd085..54b2abccc3 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesJsonIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates; +package org.thingsboard.server.transport.mqtt.attributes.updates; import lombok.extern.slf4j.Slf4j; import org.junit.After; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesProtoIntegrationTest.java similarity index 99% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesProtoIntegrationTest.java index f0458be746..6c2565a016 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/AbstractMqttAttributesUpdatesProtoIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates; +package org.thingsboard.server.transport.mqtt.attributes.updates; import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlIntegrationTest.java index 2bceaec71d..0f5dfdda46 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates.sql; +package org.thingsboard.server.transport.mqtt.attributes.updates.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; +import org.thingsboard.server.transport.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; @DaoSqlTest public class MqttAttributesUpdatesSqlIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlJsonIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlJsonIntegrationTest.java index 3abab9f052..54b5060886 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlJsonIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates.sql; +package org.thingsboard.server.transport.mqtt.attributes.updates.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; +import org.thingsboard.server.transport.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; @DaoSqlTest public class MqttAttributesUpdatesSqlJsonIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlProtoIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlProtoIntegrationTest.java index 4d46f3865b..68267bbd49 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/sql/MqttAttributesUpdatesSqlProtoIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.attributes.updates.sql; +package org.thingsboard.server.transport.mqtt.attributes.updates.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; +import org.thingsboard.server.transport.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; @DaoSqlTest public class MqttAttributesUpdatesSqlProtoIntegrationTest extends AbstractMqttAttributesUpdatesProtoIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimDeviceTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimDeviceTest.java index 36da5466cb..6bbcf82e39 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimDeviceTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim; +package org.thingsboard.server.transport.mqtt.claim; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttAsyncClient; @@ -29,7 +29,7 @@ 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 org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java similarity index 97% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java index 2b4e0d14df..97e177aef5 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim; +package org.thingsboard.server.transport.mqtt.claim; import lombok.extern.slf4j.Slf4j; import org.junit.After; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java index 8f21c70f98..c3a5fb8cbb 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim; +package org.thingsboard.server.transport.mqtt.claim; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttAsyncClient; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java similarity index 84% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java index a3d9ed886a..18d504f94c 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim.sql; +package org.thingsboard.server.transport.mqtt.claim.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; +import org.thingsboard.server.transport.mqtt.claim.AbstractMqttClaimJsonDeviceTest; @DaoSqlTest public class MqttClaimDeviceJsonSqlTest extends AbstractMqttClaimJsonDeviceTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java similarity index 84% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java index 7a62ed897d..fd3595f543 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim.sql; +package org.thingsboard.server.transport.mqtt.claim.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest; +import org.thingsboard.server.transport.mqtt.claim.AbstractMqttClaimProtoDeviceTest; @DaoSqlTest public class MqttClaimDeviceProtoSqlTest extends AbstractMqttClaimProtoDeviceTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceSqlTest.java similarity index 84% rename from application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceSqlTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceSqlTest.java index 64cd7f5481..666c51394d 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/claim/sql/MqttClaimDeviceSqlTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.claim.sql; +package org.thingsboard.server.transport.mqtt.claim.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; +import org.thingsboard.server.transport.mqtt.claim.AbstractMqttClaimDeviceTest; @DaoSqlTest public class MqttClaimDeviceSqlTest extends AbstractMqttClaimDeviceTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/provision/AbstractMqttProvisionJsonDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/AbstractMqttProvisionJsonDeviceTest.java similarity index 99% rename from application/src/test/java/org/thingsboard/server/mqtt/provision/AbstractMqttProvisionJsonDeviceTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/provision/AbstractMqttProvisionJsonDeviceTest.java index 0fce47fc84..aa95165349 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/provision/AbstractMqttProvisionJsonDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/AbstractMqttProvisionJsonDeviceTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.provision; +package org.thingsboard.server.transport.mqtt.provision; import com.google.gson.JsonObject; import io.netty.handler.codec.mqtt.MqttQoS; @@ -38,7 +38,7 @@ import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.util.mapping.JacksonUtil; -import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; +import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/provision/AbstractMqttProvisionProtoDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/AbstractMqttProvisionProtoDeviceTest.java similarity index 99% rename from application/src/test/java/org/thingsboard/server/mqtt/provision/AbstractMqttProvisionProtoDeviceTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/provision/AbstractMqttProvisionProtoDeviceTest.java index 0478a402d9..23a27e1cfe 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/provision/AbstractMqttProvisionProtoDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/AbstractMqttProvisionProtoDeviceTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.provision; +package org.thingsboard.server.transport.mqtt.provision; import io.netty.handler.codec.mqtt.MqttQoS; import lombok.extern.slf4j.Slf4j; @@ -45,9 +45,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRespo import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; -import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; +import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; -import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/provision/sql/MqttProvisionDeviceJsonSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/sql/MqttProvisionDeviceJsonSqlTest.java similarity index 83% rename from application/src/test/java/org/thingsboard/server/mqtt/provision/sql/MqttProvisionDeviceJsonSqlTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/provision/sql/MqttProvisionDeviceJsonSqlTest.java index 82a797bcf6..bca75c87b2 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/provision/sql/MqttProvisionDeviceJsonSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/sql/MqttProvisionDeviceJsonSqlTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.provision.sql; +package org.thingsboard.server.transport.mqtt.provision.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.provision.AbstractMqttProvisionJsonDeviceTest; +import org.thingsboard.server.transport.mqtt.provision.AbstractMqttProvisionJsonDeviceTest; @DaoSqlTest public class MqttProvisionDeviceJsonSqlTest extends AbstractMqttProvisionJsonDeviceTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/provision/sql/MqttProvisionDeviceProtoSqlTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/sql/MqttProvisionDeviceProtoSqlTest.java similarity index 83% rename from application/src/test/java/org/thingsboard/server/mqtt/provision/sql/MqttProvisionDeviceProtoSqlTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/provision/sql/MqttProvisionDeviceProtoSqlTest.java index 792c41c7ce..62cb3d4779 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/provision/sql/MqttProvisionDeviceProtoSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/provision/sql/MqttProvisionDeviceProtoSqlTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.provision.sql; +package org.thingsboard.server.transport.mqtt.provision.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.provision.AbstractMqttProvisionProtoDeviceTest; +import org.thingsboard.server.transport.mqtt.provision.AbstractMqttProvisionProtoDeviceTest; @DaoSqlTest public class MqttProvisionDeviceProtoSqlTest extends AbstractMqttProvisionProtoDeviceTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java index 7ccc14b65d..32f770a41c 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc; +package org.thingsboard.server.transport.mqtt.rpc; import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.extern.slf4j.Slf4j; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java index 4ae0072029..112d773ee4 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc; +package org.thingsboard.server.transport.mqtt.rpc; import com.fasterxml.jackson.databind.JsonNode; import com.google.protobuf.InvalidProtocolBufferException; @@ -31,7 +31,7 @@ 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.AbstractMqttIntegrationTest; +import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import java.util.Arrays; import java.util.concurrent.CountDownLatch; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcJsonIntegrationTest.java similarity index 97% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcJsonIntegrationTest.java index fefae730ae..4b2f38cbd9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcJsonIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc; +package org.thingsboard.server.transport.mqtt.rpc; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttAsyncClient; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java index 4f73ed0b0b..575dfdb052 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc; +package org.thingsboard.server.transport.mqtt.rpc; import com.google.protobuf.InvalidProtocolBufferException; import lombok.extern.slf4j.Slf4j; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcJsonSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcJsonSqlIntegrationTest.java similarity index 84% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcJsonSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcJsonSqlIntegrationTest.java index 45be4b88b6..c17570b5ca 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcJsonSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcJsonSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc.sql; +package org.thingsboard.server.transport.mqtt.rpc.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest; +import org.thingsboard.server.transport.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest; @DaoSqlTest public class MqttServerSideRpcJsonSqlIntegrationTest extends AbstractMqttServerSideRpcJsonIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcProtoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcProtoSqlIntegrationTest.java similarity index 84% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcProtoSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcProtoSqlIntegrationTest.java index 6819f87dce..fa21510bb1 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcProtoSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcProtoSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc.sql; +package org.thingsboard.server.transport.mqtt.rpc.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest; +import org.thingsboard.server.transport.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest; @DaoSqlTest diff --git a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java similarity index 84% rename from application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java index 114fa67380..816ac7780d 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/sql/MqttServerSideRpcSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.rpc.sql; +package org.thingsboard.server.transport.mqtt.rpc.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; +import org.thingsboard.server.transport.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; /** * Created by Valerii Sosliuk on 8/22/2017. diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java index 874d55606d..7c646f2276 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes; +package org.thingsboard.server.transport.mqtt.telemetry.attributes; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; @@ -25,7 +25,7 @@ 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 org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import java.util.Arrays; import java.util.HashSet; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesJsonIntegrationTest.java similarity index 96% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesJsonIntegrationTest.java index b6b9a745d8..7a9d52e2ca 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesJsonIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes; +package org.thingsboard.server.transport.mqtt.telemetry.attributes; import lombok.extern.slf4j.Slf4j; import org.junit.After; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java similarity index 99% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java index 4670946a60..2fb594a62c 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes; +package org.thingsboard.server.transport.mqtt.telemetry.attributes; import com.github.os72.protobuf.dynamic.DynamicSchema; import com.google.protobuf.Descriptors; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlIntegrationTest.java index 0d3b9ee235..c3dafc4eef 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes.sql; +package org.thingsboard.server.transport.mqtt.telemetry.attributes.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; @DaoSqlTest public class MqttAttributesSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlJsonIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlJsonIntegrationTest.java index 9cd4659271..306b89ad23 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlJsonIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes.sql; +package org.thingsboard.server.transport.mqtt.telemetry.attributes.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; @DaoSqlTest public class MqttAttributesSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlProtoIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlProtoIntegrationTest.java index 38ba5c4d18..57376961db 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/sql/MqttAttributesSqlProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/sql/MqttAttributesSqlProtoIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.attributes.sql; +package org.thingsboard.server.transport.mqtt.telemetry.attributes.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; @DaoSqlTest public class MqttAttributesSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java similarity index 98% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java index 9ecc3415c2..f3cb3789b9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries; import com.fasterxml.jackson.core.type.TypeReference; import io.netty.handler.codec.mqtt.MqttQoS; @@ -26,12 +26,11 @@ 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.Ignore; 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 org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import java.util.Arrays; import java.util.HashSet; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java similarity index 97% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java index a006d15ac6..2e685415b9 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttAsyncClient; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java similarity index 99% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java index 12898a4a68..93a004318d 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries; import com.github.os72.protobuf.dynamic.DynamicSchema; import com.google.protobuf.Descriptors; diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java similarity index 82% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java index 2dbd8aaf41..67946ffc6b 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries.nosql; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; /** * Created by Valerii Sosliuk on 8/22/2017. diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlJsonIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlJsonIntegrationTest.java index eca09478db..6b9987091e 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlJsonIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries.nosql; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; @DaoNoSqlTest public class MqttTimeseriesNoSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlProtoIntegrationTest.java similarity index 81% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlProtoIntegrationTest.java index 67f7c61352..dfdae8d3f8 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlProtoIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries.nosql; import org.thingsboard.server.dao.service.DaoNoSqlTest; -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; @DaoNoSqlTest public class MqttTimeseriesNoSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java similarity index 82% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java index 844ae272f5..e89b45fc32 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries.sql; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; /** * Created by Valerii Sosliuk on 8/22/2017. diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlJsonIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlJsonIntegrationTest.java similarity index 82% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlJsonIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlJsonIntegrationTest.java index 943f5b7877..4f4fd84168 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlJsonIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlJsonIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries.sql; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; /** * Created by Valerii Sosliuk on 8/22/2017. diff --git a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlProtoIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlProtoIntegrationTest.java similarity index 82% rename from application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlProtoIntegrationTest.java rename to application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlProtoIntegrationTest.java index bf609bf9be..cf227e6af4 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlProtoIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlProtoIntegrationTest.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt.telemetry.timeseries.sql; +package org.thingsboard.server.transport.mqtt.telemetry.timeseries.sql; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; +import org.thingsboard.server.transport.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; /** * Created by Valerii Sosliuk on 8/22/2017. diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java new file mode 100644 index 0000000000..64b6c1f946 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CoapDeviceType.java @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2021 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 CoapDeviceType { + DEFAULT, + EFENTO +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java index 9b9a021fcf..6409ba4e90 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java @@ -18,5 +18,6 @@ package org.thingsboard.server.common.data; public enum DeviceTransportType { DEFAULT, MQTT, - LWM2M + LWM2M, + COAP } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/CoapDeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/CoapDeviceTransportConfiguration.java new file mode 100644 index 0000000000..96aef40788 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/CoapDeviceTransportConfiguration.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2021 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.data; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import org.thingsboard.server.common.data.DeviceTransportType; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class CoapDeviceTransportConfiguration implements DeviceTransportConfiguration { + + @JsonIgnore + private Map properties = new HashMap<>(); + + @JsonAnyGetter + public Map properties() { + return this.properties; + } + + @JsonAnySetter + public void put(String name, Object value) { + this.properties.put(name, value); + } + + @Override + public DeviceTransportType getType() { + return DeviceTransportType.COAP; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java index 5409dcd339..2df6bf5413 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java @@ -29,7 +29,8 @@ import org.thingsboard.server.common.data.DeviceTransportType; @JsonSubTypes({ @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), - @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M")}) + @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), + @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP")}) public interface DeviceTransportConfiguration { @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java new file mode 100644 index 0000000000..4445f8e149 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceProfileTransportConfiguration.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 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 org.thingsboard.server.common.data.DeviceTransportType; + +@Data +public class CoapDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { + + private CoapDeviceTypeConfiguration coapDeviceTypeConfiguration; + + @Override + public DeviceTransportType getType() { + return DeviceTransportType.COAP; + } + + public CoapDeviceTypeConfiguration getCoapDeviceTypeConfiguration() { + if (coapDeviceTypeConfiguration != null) { + return coapDeviceTypeConfiguration; + } else { + return new DefaultCoapDeviceTypeConfiguration(); + } + } +} \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java new file mode 100644 index 0000000000..c47dd79368 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/CoapDeviceTypeConfiguration.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2021 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; +import org.thingsboard.server.common.data.CoapDeviceType; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "coapDeviceType") +@JsonSubTypes({ + @JsonSubTypes.Type(value = DefaultCoapDeviceTypeConfiguration.class, name = "DEFAULT"), + @JsonSubTypes.Type(value = EfentoCoapDeviceTypeConfiguration.class, name = "EFENTO")}) +public interface CoapDeviceTypeConfiguration { + + @JsonIgnore + CoapDeviceType getCoapDeviceType(); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultCoapDeviceTypeConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultCoapDeviceTypeConfiguration.java new file mode 100644 index 0000000000..dc6759a214 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DefaultCoapDeviceTypeConfiguration.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 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 org.thingsboard.server.common.data.CoapDeviceType; + +@Data +public class DefaultCoapDeviceTypeConfiguration implements CoapDeviceTypeConfiguration { + + private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; + + @Override + public CoapDeviceType getCoapDeviceType() { + return CoapDeviceType.DEFAULT; + } + + public TransportPayloadTypeConfiguration getTransportPayloadTypeConfiguration() { + if (transportPayloadTypeConfiguration != null) { + return transportPayloadTypeConfiguration; + } else { + return new JsonTransportPayloadConfiguration(); + } + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java index 2feba45d34..3558ef5349 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java @@ -30,7 +30,8 @@ import org.thingsboard.server.common.data.DeviceTransportType; @JsonSubTypes({ @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), - @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M")}) + @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), + @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP")}) public interface DeviceProfileTransportConfiguration { @JsonIgnore diff --git a/application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/EfentoCoapDeviceTypeConfiguration.java similarity index 65% rename from application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java rename to common/data/src/main/java/org/thingsboard/server/common/data/device/profile/EfentoCoapDeviceTypeConfiguration.java index e9bf33340b..cc1832e2e6 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/DbConfigurationTestRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/EfentoCoapDeviceTypeConfiguration.java @@ -13,19 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.mqtt; +package org.thingsboard.server.common.data.device.profile; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; +import lombok.Data; +import org.thingsboard.server.common.data.CoapDeviceType; -/** - * Created by ashvayka on 11.05.18. - */ -public class DbConfigurationTestRule implements TestRule { +@Data +public class EfentoCoapDeviceTypeConfiguration implements CoapDeviceTypeConfiguration { @Override - public Statement apply(Statement base, Description description) { - return null; + public CoapDeviceType getCoapDeviceType() { + return CoapDeviceType.EFENTO; } } diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 8fa8cceb2a..b350e5661a 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -83,6 +83,19 @@ mockito-core test + + com.google.protobuf + protobuf-java + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/AbstractCoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/AbstractCoapTransportResource.java new file mode 100644 index 0000000000..85a4f5ba70 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/AbstractCoapTransportResource.java @@ -0,0 +1,140 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapResource; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.transport.TransportContext; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.common.transport.auth.SessionInfoCreator; +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.UUID; +import java.util.function.BiConsumer; + +@Slf4j +public abstract class AbstractCoapTransportResource extends CoapResource { + + protected final CoapTransportContext transportContext; + protected final TransportService transportService; + + public AbstractCoapTransportResource(CoapTransportContext context, String name) { + super(name); + this.transportContext = context; + this.transportService = context.getTransportService(); + } + + @Override + public void handleGET(CoapExchange exchange) { + processHandleGet(exchange); + } + + @Override + public void handlePOST(CoapExchange exchange) { + processHandlePost(exchange); + } + + protected abstract void processHandleGet(CoapExchange exchange); + + protected abstract void processHandlePost(CoapExchange exchange); + + protected void reportActivity(TransportProtos.SessionInfoProto sessionInfo, boolean hasAttributeSubscription, boolean hasRpcSubscription) { + transportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() + .setAttributeSubscription(hasAttributeSubscription) + .setRpcSubscription(hasRpcSubscription) + .setLastActivityTime(System.currentTimeMillis()) + .build(), TransportServiceCallback.EMPTY); + } + + protected static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { + return TransportProtos.SessionEventMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC) + .setEvent(event).build(); + } + + public static class CoapDeviceAuthCallback implements TransportServiceCallback { + private final TransportContext transportContext; + private final CoapExchange exchange; + private final BiConsumer onSuccess; + + public CoapDeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, BiConsumer onSuccess) { + this.transportContext = transportContext; + this.exchange = exchange; + this.onSuccess = onSuccess; + } + + @Override + public void onSuccess(ValidateDeviceCredentialsResponse msg) { + DeviceProfile deviceProfile = msg.getDeviceProfile(); + if (msg.hasDeviceInfo() && deviceProfile != null) { + TransportProtos.SessionInfoProto sessionInfoProto = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()); + onSuccess.accept(sessionInfoProto, deviceProfile); + } else { + exchange.respond(CoAP.ResponseCode.UNAUTHORIZED); + } + } + + @Override + public void onError(Throwable e) { + log.warn("Failed to process request", e); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + public static class CoapOkCallback implements TransportServiceCallback { + private final CoapExchange exchange; + private final CoAP.ResponseCode onSuccessResponse; + private final CoAP.ResponseCode onFailureResponse; + + public CoapOkCallback(CoapExchange exchange, CoAP.ResponseCode onSuccessResponse, CoAP.ResponseCode onFailureResponse) { + this.exchange = exchange; + this.onSuccessResponse = onSuccessResponse; + this.onFailureResponse = onFailureResponse; + } + + @Override + public void onSuccess(Void msg) { + exchange.respond(onSuccessResponse); + } + + @Override + public void onError(Throwable e) { + exchange.respond(onFailureResponse); + } + } + + public static class CoapNoOpCallback implements TransportServiceCallback { + private final CoapExchange exchange; + + CoapNoOpCallback(CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onSuccess(Void msg) { + } + + @Override + public void onError(Throwable e) { + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java index 78b79e0b8e..bf6c7c994d 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java @@ -22,7 +22,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.TransportContext; -import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; +import org.thingsboard.server.transport.coap.efento.adaptor.EfentoCoapAdaptor; +import org.thingsboard.server.transport.coap.adaptors.JsonCoapAdaptor; +import org.thingsboard.server.transport.coap.adaptors.ProtoCoapAdaptor; /** * Created by ashvayka on 18.10.18. @@ -46,6 +48,14 @@ public class CoapTransportContext extends TransportContext { @Getter @Autowired - private CoapTransportAdaptor adaptor; + private JsonCoapAdaptor jsonCoapAdaptor; + + @Getter + @Autowired + private ProtoCoapAdaptor protoCoapAdaptor; + + @Getter + @Autowired + private EfentoCoapAdaptor efentoCoapAdaptor; } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 7ba4667dfb..7f3212e7ce 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -15,32 +15,40 @@ */ package org.thingsboard.server.transport.coap; +import com.google.gson.JsonParseException; +import com.google.protobuf.Descriptors; +import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.CoapResource; -import org.eclipse.californium.core.coap.CoAP.ResponseCode; +import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.network.Exchange; -import org.eclipse.californium.core.network.ExchangeObserver; +import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; -import org.springframework.util.ReflectionUtils; +import org.eclipse.californium.core.server.resources.ResourceObserver; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.TransportPayloadType; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.transport.SessionMsgListener; -import org.thingsboard.server.common.transport.TransportContext; -import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.auth.SessionInfoCreator; -import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; +import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; -import java.lang.reflect.Field; import java.util.List; import java.util.Optional; import java.util.Set; @@ -48,52 +56,60 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; @Slf4j -public class CoapTransportResource extends CoapResource { - // coap://localhost:port/api/v1/DEVICE_TOKEN/[attributes|telemetry|rpc[/requestId]] +public class CoapTransportResource extends AbstractCoapTransportResource { private static final int ACCESS_TOKEN_POSITION = 3; private static final int FEATURE_TYPE_POSITION = 4; private static final int REQUEST_ID_POSITION = 5; - private final CoapTransportContext transportContext; - private final TransportService transportService; - private final Field observerField; - private final long timeout; private final ConcurrentMap tokenToSessionIdMap = new ConcurrentHashMap<>(); + private final ConcurrentMap tokenToNotificationCounterMap = new ConcurrentHashMap<>(); private final Set rpcSubscriptions = ConcurrentHashMap.newKeySet(); private final Set attributeSubscriptions = ConcurrentHashMap.newKeySet(); - public CoapTransportResource(CoapTransportContext context, String name) { - super(name); - this.transportContext = context; - this.transportService = context.getTransportService(); - this.timeout = context.getTimeout(); - // This is important to turn off existing observable logic in - // CoapResource. We will have our own observe monitoring due to 1:1 - // observe relationship. - this.setObservable(false); - observerField = ReflectionUtils.findField(Exchange.class, "observer"); - observerField.setAccessible(true); + public CoapTransportResource(CoapTransportContext coapTransportContext, String name) { + super(coapTransportContext, name); + this.setObservable(true); // enable observing + this.addObserver(new CoapResourceObserver()); +// this.setObservable(false); // disable observing +// this.setObserveType(CoAP.Type.CON); // configure the notification type to CONs +// this.getAttributes().setObservable(); // mark observable in the Link-Format + } + + public void checkObserveRelation(Exchange exchange, Response response) { + String token = getTokenFromRequest(exchange.getRequest()); + final ObserveRelation relation = exchange.getRelation(); + if (relation == null || relation.isCanceled()) { + return; // because request did not try to establish a relation + } + if (CoAP.ResponseCode.isSuccess(response.getCode())) { + + if (!relation.isEstablished()) { + relation.setEstablished(); + addObserveRelation(relation); + } + AtomicInteger notificationCounter = tokenToNotificationCounterMap.computeIfAbsent(token, s -> new AtomicInteger(0)); + response.getOptions().setObserve(notificationCounter.getAndIncrement()); + } // ObserveLayer takes care of the else case } @Override - public void handleGET(CoapExchange exchange) { + protected void processHandleGet(CoapExchange exchange) { Optional featureType = getFeatureType(exchange.advanced().getRequest()); - if (!featureType.isPresent()) { + if (featureType.isEmpty()) { log.trace("Missing feature type parameter"); - exchange.respond(ResponseCode.BAD_REQUEST); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } else if (featureType.get() == FeatureType.TELEMETRY) { log.trace("Can't fetch/subscribe to timeseries updates"); - exchange.respond(ResponseCode.BAD_REQUEST); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } else if (exchange.getRequestOptions().hasObserve()) { processExchangeGetRequest(exchange, featureType.get()); } else if (featureType.get() == FeatureType.ATTRIBUTES) { processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); } else { log.trace("Invalid feature type parameter"); - exchange.respond(ResponseCode.BAD_REQUEST); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } } @@ -109,11 +125,11 @@ public class CoapTransportResource extends CoapResource { } @Override - public void handlePOST(CoapExchange exchange) { + protected void processHandlePost(CoapExchange exchange) { Optional featureType = getFeatureType(exchange.advanced().getRequest()); - if (!featureType.isPresent()) { + if (featureType.isEmpty()) { log.trace("Missing feature type parameter"); - exchange.respond(ResponseCode.BAD_REQUEST); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } else { switch (featureType.get()) { case ATTRIBUTES: @@ -141,14 +157,27 @@ public class CoapTransportResource extends CoapResource { } private void processProvision(CoapExchange exchange) { - log.trace("Processing {}", exchange.advanced().getRequest()); exchange.accept(); try { - transportService.process(transportContext.getAdaptor().convertToProvisionRequestMsg(UUID.randomUUID(), exchange.advanced().getRequest()), - new DeviceProvisionCallback(exchange)); + UUID sessionId = UUID.randomUUID(); + log.trace("[{}] Processing provision publish msg [{}]!", sessionId, exchange.advanced().getRequest()); + TransportProtos.ProvisionDeviceRequestMsg provisionRequestMsg; + TransportPayloadType payloadType; + try { + provisionRequestMsg = transportContext.getJsonCoapAdaptor().convertToProvisionRequestMsg(sessionId, exchange.advanced().getRequest()); + payloadType = TransportPayloadType.JSON; + } catch (Exception e) { + if (e instanceof JsonParseException || (e.getCause() != null && e.getCause() instanceof JsonParseException)) { + provisionRequestMsg = transportContext.getProtoCoapAdaptor().convertToProvisionRequestMsg(sessionId, exchange.advanced().getRequest()); + payloadType = TransportPayloadType.PROTOBUF; + } else { + throw new AdaptorException(e); + } + } + transportService.process(provisionRequestMsg, new DeviceProvisionCallback(exchange, payloadType)); } catch (AdaptorException e) { log.trace("Failed to decode message: ", e); - exchange.respond(ResponseCode.BAD_REQUEST); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } } @@ -159,123 +188,123 @@ public class CoapTransportResource extends CoapResource { Request request = advanced.getRequest(); Optional credentials = decodeCredentials(request); - if (!credentials.isPresent()) { - exchange.respond(ResponseCode.BAD_REQUEST); + if (credentials.isEmpty()) { + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); return; } - transportService.process(DeviceTransportType.DEFAULT, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), - new DeviceAuthCallback(transportContext, exchange, sessionInfo -> { + transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(credentials.get().getCredentialsId()).build(), + new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> { UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); try { + TransportConfigurationContainer transportConfigurationContainer = getTransportConfigurationContainer(deviceProfile); + CoapTransportAdaptor coapTransportAdaptor = getCoapTransportAdaptor(transportConfigurationContainer.isJsonPayload()); switch (type) { case POST_ATTRIBUTES_REQUEST: transportService.process(sessionInfo, - transportContext.getAdaptor().convertToPostAttributes(sessionId, request), - new CoapOkCallback(exchange)); - reportActivity(sessionId, sessionInfo); + coapTransportAdaptor.convertToPostAttributes(sessionId, request, + transportConfigurationContainer.getAttributesMsgDescriptor()), + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId)); break; case POST_TELEMETRY_REQUEST: transportService.process(sessionInfo, - transportContext.getAdaptor().convertToPostTelemetry(sessionId, request), - new CoapOkCallback(exchange)); - reportActivity(sessionId, sessionInfo); + coapTransportAdaptor.convertToPostTelemetry(sessionId, request, + transportConfigurationContainer.getTelemetryMsgDescriptor()), + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId)); break; case CLAIM_REQUEST: transportService.process(sessionInfo, - transportContext.getAdaptor().convertToClaimDevice(sessionId, request, sessionInfo), - new CoapOkCallback(exchange)); + coapTransportAdaptor.convertToClaimDevice(sessionId, request, sessionInfo), + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); break; case SUBSCRIBE_ATTRIBUTES_REQUEST: - attributeSubscriptions.add(sessionId); - advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced), - registerAsyncCoapSession(exchange, request, sessionInfo, sessionId))); - transportService.process(sessionInfo, - TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), - new CoapNoOpCallback(exchange)); + TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionIdMap.get(getTokenFromRequest(request)); + if (currentAttrSession == null) { + attributeSubscriptions.add(sessionId); + registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request)); + transportService.process(sessionInfo, + TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange)); + } break; case UNSUBSCRIBE_ATTRIBUTES_REQUEST: - attributeSubscriptions.remove(sessionId); - TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(request); + TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(getTokenFromRequest(request)); if (attrSession != null) { + UUID attrSessionId = new UUID(attrSession.getSessionIdMSB(), attrSession.getSessionIdLSB()); + attributeSubscriptions.remove(attrSessionId); transportService.process(attrSession, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(), - new CoapOkCallback(exchange)); - closeAndDeregister(sessionInfo); + new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + closeAndDeregister(sessionInfo, sessionId); } break; case SUBSCRIBE_RPC_COMMANDS_REQUEST: - rpcSubscriptions.add(sessionId); - advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced), - registerAsyncCoapSession(exchange, request, sessionInfo, sessionId))); - transportService.process(sessionInfo, - TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), - new CoapNoOpCallback(exchange)); + TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionIdMap.get(getTokenFromRequest(request)); + if (currentRpcSession == null) { + rpcSubscriptions.add(sessionId); + registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request)); + transportService.process(sessionInfo, + TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), + new CoapNoOpCallback(exchange)); + } else { + UUID rpcSessionId = new UUID(currentRpcSession.getSessionIdMSB(), currentRpcSession.getSessionIdLSB()); + reportActivity(currentRpcSession, attributeSubscriptions.contains(rpcSessionId), rpcSubscriptions.contains(rpcSessionId)); + } break; case UNSUBSCRIBE_RPC_COMMANDS_REQUEST: - rpcSubscriptions.remove(sessionId); - TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(request); + TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(getTokenFromRequest(request)); if (rpcSession != null) { + UUID rpcSessionId = new UUID(rpcSession.getSessionIdMSB(), rpcSession.getSessionIdLSB()); + rpcSubscriptions.remove(rpcSessionId); transportService.process(rpcSession, TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(), - new CoapOkCallback(exchange)); - closeAndDeregister(sessionInfo); + new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + closeAndDeregister(sessionInfo, sessionId); } break; case TO_DEVICE_RPC_RESPONSE: transportService.process(sessionInfo, - transportContext.getAdaptor().convertToDeviceRpcResponse(sessionId, request), - new CoapOkCallback(exchange)); + coapTransportAdaptor.convertToDeviceRpcResponse(sessionId, request), + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); break; case TO_SERVER_RPC_REQUEST: - transportService.registerSyncSession(sessionInfo, new CoapSessionListener(sessionId, exchange), transportContext.getTimeout()); + transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), transportContext.getTimeout()); transportService.process(sessionInfo, - transportContext.getAdaptor().convertToServerRpcRequest(sessionId, request), + coapTransportAdaptor.convertToServerRpcRequest(sessionId, request), new CoapNoOpCallback(exchange)); break; case GET_ATTRIBUTES_REQUEST: - transportService.registerSyncSession(sessionInfo, new CoapSessionListener(sessionId, exchange), transportContext.getTimeout()); + transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), transportContext.getTimeout()); transportService.process(sessionInfo, - transportContext.getAdaptor().convertToGetAttributes(sessionId, request), + coapTransportAdaptor.convertToGetAttributes(sessionId, request), new CoapNoOpCallback(exchange)); break; } } catch (AdaptorException e) { log.trace("[{}] Failed to decode message: ", sessionId, e); - exchange.respond(ResponseCode.BAD_REQUEST); - } catch (IllegalAccessException e) { - log.trace("[{}] Failed to process message: ", sessionId, e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } })); } - private void reportActivity(UUID sessionId, TransportProtos.SessionInfoProto sessionInfo) { - transportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() - .setAttributeSubscription(attributeSubscriptions.contains(sessionId)) - .setRpcSubscription(rpcSubscriptions.contains(sessionId)) - .setLastActivityTime(System.currentTimeMillis()) - .build(), TransportServiceCallback.EMPTY); - } - - private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(Request request) { - String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString(); + private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { + tokenToNotificationCounterMap.remove(token); return tokenToSessionIdMap.remove(token); } - private String registerAsyncCoapSession(CoapExchange exchange, Request request, TransportProtos.SessionInfoProto sessionInfo, UUID sessionId) { - String token = request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString(); + private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, String token) { tokenToSessionIdMap.putIfAbsent(token, sessionInfo); - CoapSessionListener attrListener = new CoapSessionListener(sessionId, exchange); - transportService.registerAsyncSession(sessionInfo, attrListener); + transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor)); transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); - return token; } - private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { - return TransportProtos.SessionEventMsg.newBuilder() - .setSessionType(TransportProtos.SessionType.ASYNC) - .setEvent(event).build(); + private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor) { + return new CoapSessionListener(exchange, coapTransportAdaptor); + } + + private String getTokenFromRequest(Request request) { + return request.getSource().getHostAddress() + ":" + request.getSourcePort() + ":" + request.getTokenString(); } private Optional decodeCredentials(Request request) { @@ -318,174 +347,192 @@ public class CoapTransportResource extends CoapResource { return this; } - private static class DeviceAuthCallback implements TransportServiceCallback { - private final TransportContext transportContext; + private static class DeviceProvisionCallback implements TransportServiceCallback { private final CoapExchange exchange; - private final Consumer onSuccess; + private final TransportPayloadType payloadType; - DeviceAuthCallback(TransportContext transportContext, CoapExchange exchange, Consumer onSuccess) { - this.transportContext = transportContext; + DeviceProvisionCallback(CoapExchange exchange, TransportPayloadType payloadType) { this.exchange = exchange; - this.onSuccess = onSuccess; + this.payloadType = payloadType; } @Override - public void onSuccess(ValidateDeviceCredentialsResponse msg) { - if (msg.hasDeviceInfo()) { - onSuccess.accept(SessionInfoCreator.create(msg, transportContext, UUID.randomUUID())); + public void onSuccess(TransportProtos.ProvisionDeviceResponseMsg msg) { + CoAP.ResponseCode responseCode = CoAP.ResponseCode.CREATED; + if (!msg.getStatus().equals(TransportProtos.ProvisionResponseStatus.SUCCESS)) { + responseCode = CoAP.ResponseCode.BAD_REQUEST; + } + if (payloadType.equals(TransportPayloadType.JSON)) { + exchange.respond(responseCode, JsonConverter.toJson(msg).toString()); } else { - exchange.respond(ResponseCode.UNAUTHORIZED); + exchange.respond(responseCode, msg.toByteArray()); } } @Override public void onError(Throwable e) { log.warn("Failed to process request", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } - private static class DeviceProvisionCallback implements TransportServiceCallback { - private final CoapExchange exchange; + private static class CoapSessionListener implements SessionMsgListener { - DeviceProvisionCallback(CoapExchange exchange) { - this.exchange = exchange; - } - - @Override - public void onSuccess(TransportProtos.ProvisionDeviceResponseMsg msg) { - exchange.respond(JsonConverter.toJson(msg).toString()); - } - - @Override - public void onError(Throwable e) { - log.warn("Failed to process request", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); - } - } - - private static class CoapOkCallback implements TransportServiceCallback { private final CoapExchange exchange; + private final CoapTransportAdaptor coapTransportAdaptor; - CoapOkCallback(CoapExchange exchange) { - this.exchange = exchange; - } - - @Override - public void onSuccess(Void msg) { - exchange.respond(ResponseCode.VALID); - } - - @Override - public void onError(Throwable e) { - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); - } - } - - private static class CoapNoOpCallback implements TransportServiceCallback { - private final CoapExchange exchange; - - CoapNoOpCallback(CoapExchange exchange) { - this.exchange = exchange; - } - - @Override - public void onSuccess(Void msg) { - - } - - @Override - public void onError(Throwable e) { - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); - } - } - - public class CoapSessionListener implements SessionMsgListener { - - private final CoapExchange exchange; - private final AtomicInteger seqNumber = new AtomicInteger(2); - - CoapSessionListener(UUID sessionId, CoapExchange exchange) { + CoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor) { this.exchange = exchange; + this.coapTransportAdaptor = coapTransportAdaptor; } @Override public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) { try { - exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + exchange.respond(coapTransportAdaptor.convertToPublish(msg)); } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } @Override public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg) { try { - exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg)); } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } @Override public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { - exchange.respond(ResponseCode.SERVICE_UNAVAILABLE); + exchange.respond(CoAP.ResponseCode.SERVICE_UNAVAILABLE); } @Override public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) { try { - exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg)); } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } @Override public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) { try { - exchange.respond(transportContext.getAdaptor().convertToPublish(this, msg)); + exchange.respond(coapTransportAdaptor.convertToPublish(msg)); } catch (AdaptorException e) { log.trace("Failed to reply due to error", e); - exchange.respond(ResponseCode.INTERNAL_SERVER_ERROR); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } - public int getNextSeqNumber() { - return seqNumber.getAndIncrement(); + private boolean isConRequest() { + return exchange.advanced().getRequest().isConfirmable(); } } - public class CoapExchangeObserverProxy implements ExchangeObserver { + public class CoapResourceObserver implements ResourceObserver { + + @Override + public void changedName(String old) { + } + + @Override + public void changedPath(String old) { + } + + @Override + public void addedChild(Resource child) { + } - private final ExchangeObserver proxy; - private final String token; + @Override + public void removedChild(Resource child) { + } - CoapExchangeObserverProxy(ExchangeObserver proxy, String token) { - super(); - this.proxy = proxy; - this.token = token; + @Override + public void addedObserveRelation(ObserveRelation relation) { + if (log.isTraceEnabled()) { + Request request = relation.getExchange().getRequest(); + log.trace("Added Observe relation for token: {}", getTokenFromRequest(request)); + } } @Override - public void completed(Exchange exchange) { - proxy.completed(exchange); - TransportProtos.SessionInfoProto session = tokenToSessionIdMap.remove(token); - if (session != null) { - closeAndDeregister(session); + public void removedObserveRelation(ObserveRelation relation) { + Request request = relation.getExchange().getRequest(); + String tokenFromRequest = getTokenFromRequest(request); + log.trace("Relation removed for token: {}", tokenFromRequest); + TransportProtos.SessionInfoProto sessionInfoToRemove = lookupAsyncSessionInfo(tokenFromRequest); + if (sessionInfoToRemove != null) { + closeAndDeregister(sessionInfoToRemove, new UUID(sessionInfoToRemove.getSessionIdMSB(), sessionInfoToRemove.getDeviceIdLSB())); } } } - private void closeAndDeregister(TransportProtos.SessionInfoProto session) { + private void closeAndDeregister(TransportProtos.SessionInfoProto session, UUID sessionId) { transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null); transportService.deregisterSession(session); - UUID sessionId = new UUID(session.getSessionIdMSB(), session.getSessionIdLSB()); rpcSubscriptions.remove(sessionId); attributeSubscriptions.remove(sessionId); } + private TransportConfigurationContainer getTransportConfigurationContainer(DeviceProfile deviceProfile) throws AdaptorException { + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + if (transportConfiguration instanceof DefaultDeviceProfileTransportConfiguration) { + return new TransportConfigurationContainer(true); + } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = + (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = + coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); + if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) { + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = + (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = + defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + if (transportPayloadTypeConfiguration instanceof JsonTransportPayloadConfiguration) { + return new TransportConfigurationContainer(true); + } else { + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = + (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + String deviceTelemetryProtoSchema = protoTransportPayloadConfiguration.getDeviceTelemetryProtoSchema(); + String deviceAttributesProtoSchema = protoTransportPayloadConfiguration.getDeviceAttributesProtoSchema(); + return new TransportConfigurationContainer(false, + protoTransportPayloadConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema), + protoTransportPayloadConfiguration.getAttributesDynamicMessageDescriptor(deviceAttributesProtoSchema)); + } + } else { + throw new AdaptorException("Invalid CoapDeviceTypeConfiguration type: " + coapDeviceTypeConfiguration.getClass().getSimpleName() + "!"); + } + } else { + throw new AdaptorException("Invalid DeviceProfileTransportConfiguration type" + transportConfiguration.getClass().getSimpleName() + "!"); + } + } + + private CoapTransportAdaptor getCoapTransportAdaptor(boolean jsonPayloadType) { + return jsonPayloadType ? transportContext.getJsonCoapAdaptor() : transportContext.getProtoCoapAdaptor(); + } + + @Data + private static class TransportConfigurationContainer { + + private boolean jsonPayload; + private Descriptors.Descriptor telemetryMsgDescriptor; + private Descriptors.Descriptor attributesMsgDescriptor; + + public TransportConfigurationContainer(boolean jsonPayload, Descriptors.Descriptor telemetryMsgDescriptor, Descriptors.Descriptor attributesMsgDescriptor) { + this.jsonPayload = jsonPayload; + this.telemetryMsgDescriptor = telemetryMsgDescriptor; + this.attributesMsgDescriptor = attributesMsgDescriptor; + } + + public TransportConfigurationContainer(boolean jsonPayload) { + this.jsonPayload = jsonPayload; + } + } } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java index c982f01603..452f661a80 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java @@ -19,10 +19,11 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.network.CoapEndpoint; -import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.core.server.resources.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; +import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -37,6 +38,8 @@ public class CoapTransportService { private static final String V1 = "v1"; private static final String API = "api"; + private static final String EFENTO = "efento"; + private static final String MEASUREMENTS = "m"; @Autowired private CoapTransportContext coapTransportContext; @@ -47,11 +50,21 @@ public class CoapTransportService { public void init() throws UnknownHostException { log.info("Starting CoAP transport..."); log.info("Starting CoAP transport server"); - this.server = new CoapServer(NetworkConfig.createStandardWithoutFile()); + + this.server = new CoapServer(); createResources(); + Resource root = this.server.getRoot(); + TbCoapServerMessageDeliverer messageDeliverer = new TbCoapServerMessageDeliverer(root); + this.server.setMessageDeliverer(messageDeliverer); + InetAddress addr = InetAddress.getByName(coapTransportContext.getHost()); InetSocketAddress sockAddr = new InetSocketAddress(addr, coapTransportContext.getPort()); - server.addEndpoint(new CoapEndpoint(sockAddr)); + + CoapEndpoint.Builder coapEndpoitBuilder = new CoapEndpoint.Builder(); + coapEndpoitBuilder.setInetSocketAddress(sockAddr); + CoapEndpoint coapEndpoint = coapEndpoitBuilder.build(); + + server.addEndpoint(coapEndpoint); server.start(); log.info("CoAP transport started!"); } @@ -59,7 +72,13 @@ public class CoapTransportService { private void createResources() { CoapResource api = new CoapResource(API); api.add(new CoapTransportResource(coapTransportContext, V1)); + + CoapResource efento = new CoapResource(EFENTO); + CoapEfentoTransportResource efentoMeasurementsTransportResource = new CoapEfentoTransportResource(coapTransportContext, MEASUREMENTS); + efento.add(efentoMeasurementsTransportResource); + server.add(api); + server.add(efento); } @PreDestroy diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapServerMessageDeliverer.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapServerMessageDeliverer.java new file mode 100644 index 0000000000..fa189e35f1 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapServerMessageDeliverer.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.OptionSet; +import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.server.ServerMessageDeliverer; +import org.eclipse.californium.core.server.resources.Resource; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +@Slf4j +public class TbCoapServerMessageDeliverer extends ServerMessageDeliverer { + + public TbCoapServerMessageDeliverer(Resource root) { + super(root); + } + + @Override + protected Resource findResource(Exchange exchange) { + validateUriPath(exchange); + return findResource(exchange.getRequest().getOptions().getUriPath()); + } + + private void validateUriPath(Exchange exchange) { + OptionSet options = exchange.getRequest().getOptions(); + List uriPathList = options.getUriPath(); + String path = toPath(uriPathList); + if (path != null) { + options.setUriPath(path); + exchange.getRequest().setOptions(options); + } + } + + private String toPath(List list) { + if (!CollectionUtils.isEmpty(list) && list.size() == 1) { + final String slash = "/"; + String path = list.get(0); + if (path.startsWith(slash)) { + path = path.substring(slash.length()); + } + return path; + } + return null; + } + +} \ No newline at end of file diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java new file mode 100644 index 0000000000..f7572d0506 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.adaptors; + +import org.eclipse.californium.core.coap.Request; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class CoapAdaptorUtils { + + public static TransportProtos.GetAttributeRequestMsg toGetAttributeRequestMsg(Request inbound) throws AdaptorException { + List queryElements = inbound.getOptions().getUriQuery(); + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); + if (queryElements != null && queryElements.size() > 0) { + Set clientKeys = toKeys(queryElements, "clientKeys"); + Set sharedKeys = toKeys(queryElements, "sharedKeys"); + if (clientKeys != null) { + result.addAllClientAttributeNames(clientKeys); + } + if (sharedKeys != null) { + result.addAllSharedAttributeNames(sharedKeys); + } + } + return result.build(); + } + + private static Set toKeys(List queryElements, String attributeName) throws AdaptorException { + String keys = null; + for (String queryElement : queryElements) { + String[] queryItem = queryElement.split("="); + if (queryItem.length == 2 && queryItem[0].equals(attributeName)) { + keys = queryItem[1]; + } + } + if (keys != null && !StringUtils.isEmpty(keys)) { + return new HashSet<>(Arrays.asList(keys.split(","))); + } else { + return null; + } + } +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java index 677784d2cb..1d4703ead4 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java @@ -15,20 +15,20 @@ */ package org.thingsboard.server.transport.coap.adaptors; +import com.google.protobuf.Descriptors; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; -import org.thingsboard.server.transport.coap.CoapTransportResource; import java.util.UUID; public interface CoapTransportAdaptor { - TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException; + TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound, Descriptors.Descriptor telemetryMsgDescriptor) throws AdaptorException; - TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException; + TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound, Descriptors.Descriptor attributesMsgDescriptor) throws AdaptorException; TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException; @@ -38,13 +38,13 @@ public interface CoapTransportAdaptor { TransportProtos.ClaimDeviceMsg convertToClaimDevice(UUID sessionId, Request inbound, TransportProtos.SessionInfoProto sessionInfo) throws AdaptorException; - Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; + Response convertToPublish(TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; - Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; + Response convertToPublish(boolean isConfirmable, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; - Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; + Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; - Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException; + Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException; ProvisionDeviceRequestMsg convertToProvisionRequestMsg(UUID sessionId, Request inbound) throws AdaptorException; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java index af391dedde..e382b2a3ee 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java @@ -19,31 +19,27 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; +import com.google.protobuf.Descriptors; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.CoapTransportResource; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; -@Component("JsonCoapAdaptor") +@Component @Slf4j public class JsonCoapAdaptor implements CoapTransportAdaptor { @Override - public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException { + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound, Descriptors.Descriptor telemetryMsgDescriptor) throws AdaptorException { String payload = validatePayload(sessionId, inbound, false); try { return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload)); @@ -53,7 +49,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { } @Override - public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException { + public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound, Descriptors.Descriptor attributesMsgDescriptor) throws AdaptorException { String payload = validatePayload(sessionId, inbound, false); try { return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); @@ -64,19 +60,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @Override public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException { - List queryElements = inbound.getOptions().getUriQuery(); - TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); - if (queryElements != null && queryElements.size() > 0) { - Set clientKeys = toKeys(queryElements, "clientKeys"); - Set sharedKeys = toKeys(queryElements, "sharedKeys"); - if (clientKeys != null) { - result.addAllClientAttributeNames(clientKeys); - } - if (sharedKeys != null) { - result.addAllSharedAttributeNames(sharedKeys); - } - } - return result.build(); + return CoapAdaptorUtils.toGetAttributeRequestMsg(inbound); } @Override @@ -106,17 +90,17 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { } @Override - public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException { - return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg)); + public Response convertToPublish(boolean isConfirmable, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException { + return getObserveNotification(isConfirmable, JsonConverter.toJson(msg)); } @Override - public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException { - return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg, true)); + public Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException { + return getObserveNotification(isConfirmable, JsonConverter.toJson(msg, true)); } @Override - public Response convertToPublish(CoapTransportResource.CoapSessionListener coapSessionListener, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException { + public Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException { Response response = new Response(CoAP.ResponseCode.CONTENT); JsonElement result = JsonConverter.toJson(msg); response.setPayload(result.toString()); @@ -134,7 +118,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { } @Override - public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { + public Response convertToPublish(TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) { return new Response(CoAP.ResponseCode.NOT_FOUND); } else { @@ -145,10 +129,10 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { } } - private Response getObserveNotification(int seqNumber, JsonElement json) { + private Response getObserveNotification(boolean confirmable, JsonElement json) { Response response = new Response(CoAP.ResponseCode.CONTENT); - response.getOptions().setObserve(seqNumber); response.setPayload(json.toString()); + response.setConfirmable(confirmable); return response; } @@ -163,19 +147,4 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { return payload; } - private Set toKeys(List queryElements, String attributeName) throws AdaptorException { - String keys = null; - for (String queryElement : queryElements) { - String[] queryItem = queryElement.split("="); - if (queryItem.length == 2 && queryItem[0].equals(attributeName)) { - keys = queryItem[1]; - } - } - if (keys != null && !StringUtils.isEmpty(keys)) { - return new HashSet<>(Arrays.asList(keys.split(","))); - } else { - return null; - } - } - } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java new file mode 100644 index 0000000000..d4c53fdc9a --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java @@ -0,0 +1,149 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.adaptors; + +import com.google.gson.JsonParser; +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.coap.Response; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.transport.adaptor.ProtoConverter; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.coap.CoapTransportResource; + +import java.util.Optional; +import java.util.UUID; + +@Component +@Slf4j +public class ProtoCoapAdaptor implements CoapTransportAdaptor { + + @Override + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound, Descriptors.Descriptor telemetryMsgDescriptor) throws AdaptorException { + try { + return JsonConverter.convertToTelemetryProto(new JsonParser().parse(dynamicMsgToJson(inbound.getPayload(), telemetryMsgDescriptor))); + } catch (Exception e) { + throw new AdaptorException(e); + } + } + + @Override + public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound, Descriptors.Descriptor attributesMsgDescriptor) throws AdaptorException { + try { + return JsonConverter.convertToAttributesProto(new JsonParser().parse(dynamicMsgToJson(inbound.getPayload(), attributesMsgDescriptor))); + } catch (Exception e) { + throw new AdaptorException(e); + } + } + + @Override + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException { + return CoapAdaptorUtils.toGetAttributeRequestMsg(inbound); + } + + @Override + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException { + Optional requestId = CoapTransportResource.getRequestId(inbound); + if (requestId.isEmpty()) { + throw new AdaptorException("Request id is missing!"); + } else { + try { + String payload = TransportProtos.ToDeviceRpcResponseMsg.parseFrom(inbound.getPayload()).getPayload(); + return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.get()) + .setPayload(payload).build(); + } catch (InvalidProtocolBufferException e) { + throw new AdaptorException(e); + } + } + } + + @Override + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException { + try { + return ProtoConverter.convertToServerRpcRequest(inbound.getPayload(), 0); + } catch (InvalidProtocolBufferException ex) { + throw new AdaptorException(ex); + } + } + + @Override + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(UUID sessionId, Request inbound, TransportProtos.SessionInfoProto sessionInfo) throws AdaptorException { + DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); + try { + return ProtoConverter.convertToClaimDeviceProto(deviceId, inbound.getPayload()); + } catch (InvalidProtocolBufferException ex) { + throw new AdaptorException(ex); + } + } + + @Override + public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(UUID sessionId, Request inbound) throws AdaptorException { + try { + return ProtoConverter.convertToProvisionRequestMsg(inbound.getPayload()); + } catch (InvalidProtocolBufferException ex) { + throw new AdaptorException(ex); + } + } + + @Override + public Response convertToPublish(boolean isConfirmable, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException { + return getObserveNotification(isConfirmable, msg.toByteArray()); + } + + @Override + public Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException { + return getObserveNotification(isConfirmable, msg.toByteArray()); + } + + @Override + public Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException { + Response response = new Response(CoAP.ResponseCode.CONTENT); + response.setPayload(msg.toByteArray()); + return response; + } + + @Override + public Response convertToPublish(TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { + if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) { + return new Response(CoAP.ResponseCode.NOT_FOUND); + } else { + Response response = new Response(CoAP.ResponseCode.CONTENT); + response.setPayload(msg.toByteArray()); + return response; + } + } + + private Response getObserveNotification(boolean confirmable, byte[] notification) { + Response response = new Response(CoAP.ResponseCode.CONTENT); + response.setPayload(notification); + response.setConfirmable(confirmable); + return response; + } + + private String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException { + DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, bytes); + return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage); + } + +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java deleted file mode 100644 index cf761efd3e..0000000000 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DeviceEmulator.java +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.transport.coap.client; - -import java.io.IOException; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.CoapClient; -import org.eclipse.californium.core.CoapHandler; -import org.eclipse.californium.core.CoapResponse; -import org.eclipse.californium.core.coap.MediaTypeRegistry; -import org.thingsboard.server.common.msg.session.FeatureType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -@Slf4j -public class DeviceEmulator { - - public static final String SN = "SN-" + new Random().nextInt(1000); - public static final String MODEL = "Model " + new Random().nextInt(1000); - private static final ObjectMapper mapper = new ObjectMapper(); - - private final String host; - private final int port; - private final String token; - - private CoapClient attributesClient; - private CoapClient telemetryClient; - private CoapClient rpcClient; - private String[] keys; - private ExecutorService executor = Executors.newFixedThreadPool(1); - private AtomicInteger seq = new AtomicInteger(100); - - private DeviceEmulator(String host, int port, String token, String keys) { - this.host = host; - this.port = port; - this.token = token; - this.attributesClient = new CoapClient(getFeatureTokenUrl(host, port, token, FeatureType.ATTRIBUTES)); - this.telemetryClient = new CoapClient(getFeatureTokenUrl(host, port, token, FeatureType.TELEMETRY)); - this.rpcClient = new CoapClient(getFeatureTokenUrl(host, port, token, FeatureType.RPC)); - this.keys = keys.split(","); - } - - public void start() { - executor.submit(new Runnable() { - - @Override - public void run() { - try { - sendObserveRequest(rpcClient); - while (!Thread.interrupted()) { - - - sendRequest(attributesClient, createAttributesRequest()); - sendRequest(telemetryClient, createTelemetryRequest()); - - Thread.sleep(1000); - } - } catch (Exception e) { - log.error("Error occurred while sending COAP requests", e); - } - } - - private void sendRequest(CoapClient client, JsonNode request) throws JsonProcessingException { - CoapResponse telemetryResponse = client.setTimeout(60000).post(mapper.writeValueAsString(request), - MediaTypeRegistry.APPLICATION_JSON); - log.info("Response: {}, {}", telemetryResponse.getCode(), telemetryResponse.getResponseText()); - } - - private void sendObserveRequest(CoapClient client) throws JsonProcessingException { - client.observe(new CoapHandler() { - @Override - public void onLoad(CoapResponse coapResponse) { - log.info("Command: {}, {}", coapResponse.getCode(), coapResponse.getResponseText()); - try { - JsonNode node = mapper.readTree(coapResponse.getResponseText()); - int requestId = node.get("id").asInt(); - String method = node.get("method").asText(); - ObjectNode params = (ObjectNode) node.get("params"); - ObjectNode response = mapper.createObjectNode(); - response.put("id", requestId); - response.set("response", params); - log.info("Command Response: {}, {}", requestId, mapper.writeValueAsString(response)); - CoapClient commandResponseClient = new CoapClient(getFeatureTokenUrl(host, port, token, FeatureType.RPC)); - commandResponseClient.post(new CoapHandler() { - @Override - public void onLoad(CoapResponse response) { - log.info("Command Response Ack: {}, {}", response.getCode(), response.getResponseText()); - } - - @Override - public void onError() { - //Do nothing - } - }, mapper.writeValueAsString(response), MediaTypeRegistry.APPLICATION_JSON); - - } catch (IOException e) { - log.error("Error occurred while processing COAP response", e); - } - } - - @Override - public void onError() { - //Do nothing - } - }); - } - - }); - } - - private ObjectNode createAttributesRequest() { - ObjectNode element = mapper.createObjectNode(); - element.put("serialNumber", SN); - element.put("model", MODEL); - return element; - } - - private ArrayNode createTelemetryRequest() { - ArrayNode rootNode = mapper.createArrayNode(); - for (String key : keys) { - ObjectNode element = mapper.createObjectNode(); - element.put(key, seq.incrementAndGet()); - rootNode.add(element); - } - return rootNode; - } - - protected void stop() { - executor.shutdownNow(); - } - - public static void main(String args[]) { - if (args.length != 4) { - System.out.println("Usage: java -jar " + DeviceEmulator.class.getSimpleName() + ".jar host port device_token keys"); - } - final DeviceEmulator emulator = new DeviceEmulator(args[0], Integer.parseInt(args[1]), args[2], args[3]); - emulator.start(); - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - emulator.stop(); - } - }); - } - - - private String getFeatureTokenUrl(String host, int port, String token, FeatureType featureType) { - return getBaseUrl(host, port) + token + "/" + featureType.name().toLowerCase(); - } - - private String getBaseUrl(String host, int port) { - return "coap://" + host + ":" + port + "/api/v1/"; - } - -} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java new file mode 100644 index 0000000000..03ed92da58 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java @@ -0,0 +1,239 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.efento; + +import com.google.gson.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.californium.core.server.resources.Resource; +import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos; +import org.thingsboard.server.gen.transport.coap.MeasurementsProtos; +import org.thingsboard.server.transport.coap.AbstractCoapTransportResource; +import org.thingsboard.server.transport.coap.CoapTransportContext; +import org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class CoapEfentoTransportResource extends AbstractCoapTransportResource { + + private static final int MEASUREMENTS_POSITION = 2; + private static final String MEASUREMENTS = "m"; + + public CoapEfentoTransportResource(CoapTransportContext context, String name) { + super(context, name); + this.setObservable(true); // enable observing + this.setObserveType(CoAP.Type.CON); // configure the notification type to CONs +// this.getAttributes().setObservable(); // mark observable in the Link-Format + } + + @Override + protected void processHandleGet(CoapExchange exchange) { + exchange.respond(CoAP.ResponseCode.METHOD_NOT_ALLOWED); + } + + @Override + protected void processHandlePost(CoapExchange exchange) { + Exchange advanced = exchange.advanced(); + Request request = advanced.getRequest(); + List uriPath = request.getOptions().getUriPath(); + boolean validPath = uriPath.size() == MEASUREMENTS_POSITION && uriPath.get(1).equals(MEASUREMENTS); + if (!validPath) { + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + return; + } + byte[] bytes = request.getPayload(); + try { + MeasurementsProtos.ProtoMeasurements protoMeasurements = MeasurementsProtos.ProtoMeasurements.parseFrom(bytes); + log.trace("Successfully parsed Efento ProtoMeasurements: [{}]", protoMeasurements.getCloudToken()); + String token = protoMeasurements.getCloudToken(); + transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(), + new CoapDeviceAuthCallback(transportContext, exchange, (sessionInfo, deviceProfile) -> { + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + try { + validateEfentoTransportConfiguration(deviceProfile); + List efentoMeasurements = getEfentoMeasurements(protoMeasurements, sessionId); + transportService.process(sessionInfo, + transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, efentoMeasurements), + new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + reportActivity(sessionInfo, false, false); + } catch (AdaptorException e) { + log.error("[{}] Failed to decode Efento ProtoMeasurements: ", sessionId, e); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } + })); + } catch (Exception e) { + log.error("Failed to decode Efento ProtoMeasurements: ", e); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + @Override + public Resource getChild(String name) { + return this; + } + + private void validateEfentoTransportConfiguration(DeviceProfile deviceProfile) throws AdaptorException { + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); + if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = + (CoapDeviceProfileTransportConfiguration) transportConfiguration; + if (!(coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration() instanceof EfentoCoapDeviceTypeConfiguration)) { + throw new AdaptorException("Invalid CoapDeviceTypeConfiguration type: " + coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration().getClass().getSimpleName() + "!"); + } + } else { + throw new AdaptorException("Invalid DeviceProfileTransportConfiguration type" + transportConfiguration.getClass().getSimpleName() + "!"); + } + } + + private List getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) { + String serialNumber = CoapEfentoUtils.convertByteArrayToString(protoMeasurements.getSerialNum().toByteArray()); + boolean batteryStatus = protoMeasurements.getBatteryStatus(); + int measurementPeriodBase = protoMeasurements.getMeasurementPeriodBase(); + int measurementPeriodFactor = protoMeasurements.getMeasurementPeriodFactor(); + int signal = protoMeasurements.getSignal(); + List channelsList = protoMeasurements.getChannelsList(); + Map valuesMap = new TreeMap<>(); + if (!CollectionUtils.isEmpty(channelsList)) { + int channel = 0; + JsonObject values; + for (MeasurementsProtos.ProtoChannel protoChannel : channelsList) { + channel++; + boolean isBinarySensor = false; + MeasurementTypeProtos.MeasurementType measurementType = protoChannel.getType(); + String measurementTypeName = measurementType.name(); + if (measurementType.equals(MeasurementTypeProtos.MeasurementType.OK_ALARM) + || measurementType.equals(MeasurementTypeProtos.MeasurementType.FLOODING)) { + isBinarySensor = true; + } + if (measurementPeriodFactor == 0 && isBinarySensor) { + measurementPeriodFactor = 14; + } else { + measurementPeriodFactor = 1; + } + int measurementPeriod = measurementPeriodBase * measurementPeriodFactor; + long measurementPeriodMillis = TimeUnit.SECONDS.toMillis(measurementPeriod); + long nextTransmissionAtMillis = TimeUnit.SECONDS.toMillis(protoMeasurements.getNextTransmissionAt()); + int startPoint = protoChannel.getStartPoint(); + int startTimestamp = protoChannel.getTimestamp(); + long startTimestampMillis = TimeUnit.SECONDS.toMillis(startTimestamp); + List sampleOffsetsList = protoChannel.getSampleOffsetsList(); + if (!CollectionUtils.isEmpty(sampleOffsetsList)) { + int sampleOfssetsListSize = sampleOffsetsList.size(); + for (int i = 0; i < sampleOfssetsListSize; i++) { + int sampleOffset = sampleOffsetsList.get(i); + Integer previousSampleOffset = isBinarySensor && i > 0 ? sampleOffsetsList.get(i - 1) : null; + if (sampleOffset == -32768) { + log.warn("[{}],[{}] Sensor error value! Ignoring.", sessionId, sampleOffset); + } else { + switch (measurementType) { + case TEMPERATURE: + values = valuesMap.computeIfAbsent(startTimestampMillis, k -> + CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); + values.addProperty("temperature_" + channel, ((double) (startPoint + sampleOffset)) / 10f); + startTimestampMillis = startTimestampMillis + measurementPeriodMillis; + break; + case HUMIDITY: + values = valuesMap.computeIfAbsent(startTimestampMillis, k -> + CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); + values.addProperty("humidity_" + channel, (double) (startPoint + sampleOffset)); + startTimestampMillis = startTimestampMillis + measurementPeriodMillis; + break; + case ATMOSPHERIC_PRESSURE: + values = valuesMap.computeIfAbsent(startTimestampMillis, k -> + CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); + values.addProperty("pressure_" + channel, (double) (startPoint + sampleOffset) / 10f); + startTimestampMillis = startTimestampMillis + measurementPeriodMillis; + break; + case DIFFERENTIAL_PRESSURE: + values = valuesMap.computeIfAbsent(startTimestampMillis, k -> + CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); + values.addProperty("pressure_diff_" + channel, (double) (startPoint + sampleOffset)); + startTimestampMillis = startTimestampMillis + measurementPeriodMillis; + break; + case OK_ALARM: + boolean currentIsOk = sampleOffset < 0; + if (previousSampleOffset != null) { + boolean previousIsOk = previousSampleOffset < 0; + boolean isOk = previousIsOk && currentIsOk; + boolean isAlarm = !previousIsOk && !currentIsOk; + if (isOk || isAlarm) { + break; + } + } + String data = currentIsOk ? "OK" : "ALARM"; + long sampleOffsetMillis = TimeUnit.SECONDS.toMillis(sampleOffset); + long measurementTimestamp = startTimestampMillis + Math.abs(sampleOffsetMillis); + values = valuesMap.computeIfAbsent(measurementTimestamp - 1000, k -> + CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); + values.addProperty("ok_alarm_" + channel, data); + break; + case NO_SENSOR: + case UNRECOGNIZED: + log.trace("[{}][{}] Sensor error value! Ignoring.", sessionId, measurementTypeName); + break; + default: + log.trace("[{}],[{}] Unsupported measurementType! Ignoring.", sessionId, measurementTypeName); + break; + } + } + } + } else { + log.trace("[{}][{}] sampleOffsetsList list is empty!", sessionId, measurementTypeName); + } + } + } else { + throw new IllegalStateException("[" + sessionId + "]: Failed to get Efento measurements, reason: channels list is empty!"); + } + if (!CollectionUtils.isEmpty(valuesMap)) { + List efentoMeasurements = new ArrayList<>(); + for (Long ts : valuesMap.keySet()) { + EfentoMeasurements measurement = new EfentoMeasurements(ts, valuesMap.get(ts)); + efentoMeasurements.add(measurement); + } + return efentoMeasurements; + } else { + throw new IllegalStateException("[" + sessionId + "]: Failed to collect Efento measurements, reason, values map is empty!"); + } + } + + @Data + @AllArgsConstructor + public static class EfentoMeasurements { + + private long ts; + private JsonObject values; + + } +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java new file mode 100644 index 0000000000..fdb2fa5443 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.efento.adaptor; + +import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; + +import java.util.List; +import java.util.UUID; + +@Component +@Slf4j +public class EfentoCoapAdaptor { + + private static final Gson gson = new Gson(); + + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, List measurements) throws AdaptorException { + try { + return JsonConverter.convertToTelemetryProto(gson.toJsonTree(measurements)); + } catch (Exception ex) { + log.warn("[{}] Failed to convert EfentoMeasurements to PostTelemetry request!", sessionId); + throw new AdaptorException(ex); + } + } +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/utils/CoapEfentoUtils.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/utils/CoapEfentoUtils.java new file mode 100644 index 0000000000..7eb08efc93 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/utils/CoapEfentoUtils.java @@ -0,0 +1,53 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.efento.utils; + +import com.google.gson.JsonObject; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class CoapEfentoUtils { + + public static String convertByteArrayToString(byte[] a) { + StringBuilder out = new StringBuilder(); + for (byte b : a) { + out.append(String.format("%02X", b)); + } + return out.toString(); + } + + public static String convertTimestampToUtcString(long timestampInMillis) { + String dateFormat = "yyyy-MM-dd HH:mm:ss"; + String utcZone = "UTC"; + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone(utcZone)); + return String.format("%s UTC", simpleDateFormat.format(new Date(timestampInMillis))); + } + + public static JsonObject setDefaultMeasurements(String serialNumber, boolean batteryStatus, long measurementPeriod, long nextTransmissionAtMillis, long signal, long startTimestampMillis) { + JsonObject values = new JsonObject(); + values.addProperty("serial", serialNumber); + values.addProperty("battery", batteryStatus ? "ok" : "low"); + values.addProperty("measured_at", convertTimestampToUtcString(startTimestampMillis)); + values.addProperty("next_transmission_at", convertTimestampToUtcString(nextTransmissionAtMillis)); + values.addProperty("signal", signal); + values.addProperty("measurement_interval", measurementPeriod); + return values; + } + +} diff --git a/common/transport/coap/src/main/proto/proto_measurement_types.proto b/common/transport/coap/src/main/proto/proto_measurement_types.proto new file mode 100644 index 0000000000..eec5aa0586 --- /dev/null +++ b/common/transport/coap/src/main/proto/proto_measurement_types.proto @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +option java_package = "org.thingsboard.server.gen.transport.coap"; +option java_outer_classname = "MeasurementTypeProtos"; + +enum MeasurementType { + NO_SENSOR = 0; + + /* [°C] - Celsius degree. Resolution 0.1°C. Range [-273.2-4000.0]. Type: Continuous */ + TEMPERATURE = 1; + + /* [% RH] - Relative humidity. Resolution 1%. Range [0-100]. Type: Continuous */ + HUMIDITY = 2; + + /* [hPa] - Hectopascal (1hPa = 100Pa). Resolution 0.1hPa. Range: [1.0-2000.0]. Type: Continuous */ + ATMOSPHERIC_PRESSURE = 3; + + /* [Pa] - Pascal. Resolution 1Pa. Range [-10000-10000]Type: Continuous */ + DIFFERENTIAL_PRESSURE = 4; + + /* Sign indicates state: (+) ALARM, (-) OK. Type: Binary */ + OK_ALARM = 5; + + /* [IAQ] - Iaq index. Resolution 1IAQ. Range [0-500]. Sensor return also calibration status */ + /* as offset to measured value: */ + /* - offset 3000: Sensor not stabilized (always returns 25 IAQ value) */ + /* - offset 2000: Calibration required (sensor returns not accurate values) */ + /* - offset 1000: Calibration on-going (sensor returns not accurate values) */ + /* - offset 0: Calibration done (best accuracy of IAQ sensor) */ + /* Type: Continuous */ + IAQ = 6; + + /* Sign indicates water presence: (+) water not detected, (-) water detected. Type: Binary */ + FLOODING = 7; + + /* [NB] Number of pulses. Resolution 1 pulse. Range [0-16711679]. Type: Continuous */ + PULSE_CNT = 8; + + /* [Wh] - Watthour; Resolution 1Wh. Range [0-16711679]. Number of Watthours in a single period. Type: Continuous */ + ELECTRICITY_METER = 9; + + /* [l] - Liter. Resolution 1l. Range [0-16711679]. Number of litres in a single period. Type: Continuous */ + WATER_METER = 10; + + /* [kPa] - Kilopascal (1kPa = 1000Pa); Resolution 1kPa. Range [-1000-0]. Soil moisture (tension). Type: Continuous */ + SOIL_MOISTURE = 11; + + /* [ppm] - Parts per million. Resolution 1ppm. Range [0-1000000]. Carbon monoxide concentration. Type: Continuous */ + CO_GAS = 12; + + /* [ppm] - Parts per million. Resolution 0.01ppm. Range [0-1000000.00]. Nitrogen dioxide concentration. Type: Continuous*/ + NO2_GAS = 13; + + /* [ppm] - Parts per million. Resolution 1ppm. Range [0-1000000]. Hydrogen sulfide concentration. Type: Continuous */ + H2S_GAS = 14; + + /* [lx] - Illuminance. Resolution 0.1lx. Range [0-100000.0]. Type: Continuous */ + AMBIENT_LIGHT = 15; + + /* [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0-1000]. */ + /* particles with an aerodynamic diameter less than 1 micrometer. Type: Continuous */ + PM_1_0 = 16; // µg/m^3 + + /* [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0-1000]. */ + /* particles with an aerodynamic diameter less than 2.5 micrometers. Type: Continuous */ + PM_2_5 = 17; // µg/m^3 + + /* [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0-1000]. */ + /* particles with an aerodynamic diameter less than 10 micrometers. Type: Continuous */ + PM_10_0 = 18; // µg/m^3 + + /* [dB] - Decibels. Resolution 0.1 dB. Range: [0-130.0]. Noise level. Type: Continuous */ + NOISE_LEVEL = 19; // 0.1 dB + + /* [ppm] - Parts per million. Resolution 1ppm. Range [0-1000000]. Ammonia concentration. Type: Continuous */ + NH3_GAS = 20; + + /* [ppm] - Parts per million. Resolution 1ppm. Range [0-1000000]. Methane concentration. Type: Continuous */ + CH4_GAS = 21; +} + + diff --git a/common/transport/coap/src/main/proto/proto_measurements.proto b/common/transport/coap/src/main/proto/proto_measurements.proto new file mode 100644 index 0000000000..8d894cdf3f --- /dev/null +++ b/common/transport/coap/src/main/proto/proto_measurements.proto @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; +import "proto_measurement_types.proto"; + +option java_package = "org.thingsboard.server.gen.transport.coap"; +option java_outer_classname = "MeasurementsProtos"; + +message ProtoChannel{ + /* Type of channel */ + MeasurementType type = 1; + + /* Timestamp of the first sample (the oldest one) in seconds since UNIX EPOCH 01-01-1970 */ + int32 timestamp = 2; + + /* Only used for 'Continuous' sensor types. Value used as the starting point for calculating the values of all */ + /* measurements in the package. */ + /* Format defined by 'MeasurementType' field */ + sint32 start_point = 4; + + /* 'Continuous' sensor types */ + /* Value of the offset from the 'start_point' for each measurement in the package. The oldest sample first ([0]). */ + /* 'sample_offsets' format defined by 'MeasurementType' field. */ + /* Example: MeasurementType = 1 (temperature), start_point = 100, sample_offsets[0] = 15, sample_offsets[1] = 20 */ + /* 1st sample in the package temperature value = 11.5 °C, 2nd sample in the package temperature value = 12 °C */ + /* Calculating timestamps of the measurements: timestamp = 1606391700, measurement_period_base = 60, */ + /* measurement_period_factor = 1. Timestamp of the 1st sample = 1606391700, timestamp of the 2nd sample = 1606391760 */ + /* 'Binary' sensor types: */ + /* Absolute value of the 'sample_offsets' field indicates the offset in seconds from 'timestamp' field. */ + /* Sign (- or +) indicates the state of measurements depend of sensor type. */ + /* Value of this field equals to '1' or '-1' indicates the state at the 'timestamp'. Other values */ + /* indicate the state of the relay at the time (in seconds) equal to 'timestamp' + value. */ + /* Values of this field are incremented starting from 1 (1->0: state at the time */ + /* of 'timestamp', 2->1: state at the time equal to 'timestamp' + 1 s, 3->2 : */ + /* state at the time equal to 'timestamp' + 2 s, etc.). The first and the last sample define the time range of the */ + /* measurements. Only state changes in the time range are included in the 'sample_offsets' field */ + /* Examples: if 'timestamp' value is 1553518060 and 'sample_offsets' equals '1', it means that at 1553518060 the state */ + /* was high, if 'timestamp' value is 1553518060 and 'sample_offsets' equals '-9', it means at 1553518068 the state was low */ + repeated sint32 sample_offsets = 5 [packed=true]; + + /* Deprecated - configuration is sent to endpoint 'c' */ + //int32 lo_threshold = 6; + + /* Deprecated - configuration is sent to endpoint 'c' */ + //int32 hi_threshold = 7; + + /* Deprecated - configurationis sent to endpoint 'c' */ + //int32 diff_threshold = 8; + } + +message ProtoMeasurements { + + /* serial number of the device */ + bytes serial_num = 1; + + /* true - battery ok, false - battery low */ + bool battery_status = 2; + + /* 'Measurement_period_base' and 'measurement_period_factor' define how often the measurements are taken. */ + /* Sensors of 'Continuous' type take measurement each Measurement_period_base * measurement_period_factor. */ + /* Sensors of 'Binary' type take measurement each Measurement_period_base. */ + /* For backward compatibility with versions 5.x in case of binary/mixed sensors, if the 'measurement_period_factor' is */ + /* not sent (equal to 0), then the default value '14' shall be used for period calculation. */ + /* For backward compatibility with versions 5.x in case of continues sensors, if the measurement_period_factor is */ + /* not sent (equal to 0), then the default value '1' shall be used for period calculation. */ + /* measurement period base in seconds */ + uint32 measurement_period_base = 3; + + /* Measurement period factor */ + uint32 measurement_period_factor = 8; + + repeated ProtoChannel channels = 4; + + /* Timestamp of the next scheduled transmission. If the device will not send data until this time, */ + /* it should be considered as 'lost' */ + uint32 next_transmission_at = 5; + + /* reason of transmission - unsigned integer where each bit indicates different */ + /* possible communication reason. Can be more than one */ + /* - bit 0: first message after sensor reset */ + /* - bit 1: user button triggered */ + /* - bit 2: user BLE triggered */ + /* - bit 3-7: number of retries -> incremented after each unsuccessful transmission. Max value 4. */ + /* Set to 0 after a successful transmission. */ + /* - bit 8: channel 1 lower threshold exceeded */ + /* - bit 9: channel 1 lower threshold returned */ + /* - bit 10: channel 1 higher threshold exceeded */ + /* - bit 11: channel 1 higher threshold returned */ + /* - bit 12: channel 1 differential threshold crossed */ + /* - bits 13-17: channel 2 thresholds (same as for channel 1) */ + /* - bits 18-22: channel 3 thresholds (same as for channel 1) */ + /* - bits 23-27: channel 4 or 5 or 6 thresholds (same as for channel 1) */ + uint32 transfer_reason = 6; + + /* Signal strength level mapped from RSSI */ + /* - 0 : 113 dBm or less */ + /* - 1 : 111 dBm */ + /* - 2...30 : 109...-53 dBm */ + /* - 31 : -51 dBm or greater */ + /* - 99 : Not known or not detectable */ + uint32 signal = 7; + + /* Hash of the current configuration. Hash value changes each time a device receives a new configuration */ + uint32 hash = 9; + + /* Optional string up to 36 bytes long. Can be set to any user define value or hold device's IMEI */ + string cloud_token = 16; +} \ No newline at end of file diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java index 458d29766e..fc82f78633 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java @@ -73,6 +73,9 @@ public class ProtoConverter { } public static TransportProtos.ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, byte[] bytes) throws InvalidProtocolBufferException { + if (bytes == null) { + return buildClaimDeviceMsg(deviceId, DataConstants.DEFAULT_SECRET_KEY, 0); + } TransportApiProtos.ClaimDevice proto = TransportApiProtos.ClaimDevice.parseFrom(bytes); String secretKey = proto.getSecretKey() != null ? proto.getSecretKey() : DataConstants.DEFAULT_SECRET_KEY; long durationMs = proto.getDurationMs(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 95f52cf55e..bff523d956 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -41,6 +41,9 @@ import org.thingsboard.server.common.data.DeviceProfileProvisionType; 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.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; @@ -49,6 +52,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -355,11 +359,17 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) { ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration = (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); - try { - validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA); - validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA); - } catch (Exception exception) { - throw new DataValidationException(exception.getMessage()); + validateProtoSchemas(protoTransportPayloadTypeConfiguration); + } + } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); + if (coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration) { + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); + if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; + validateProtoSchemas(protoTransportPayloadConfiguration); } } } @@ -403,6 +413,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } } + private void validateProtoSchemas(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { + try { + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA); + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA); + } catch (Exception exception) { + throw new DataValidationException(exception.getMessage()); + } + } + private void validateTransportProtoSchema(String schema, String schemaName) throws IllegalArgumentException { ProtoParser schemaParser = new ProtoParser(LOCATION, schema.toCharArray()); ProtoFileElement protoFileElement; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 9cb7c5d0d9..512b8166cb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; +import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; @@ -242,6 +243,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe case LWM2M: deviceData.setTransportConfiguration(new Lwm2mDeviceTransportConfiguration()); break; + case COAP: + deviceData.setTransportConfiguration(new CoapDeviceTransportConfiguration()); + break; } } return deviceData; diff --git a/pom.xml b/pom.xml index bbf967941a..3f1bf9faea 100755 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 2.12.1 2.12.1 2.2.6 - 1.0.2 + 2.6.1 2.6.2 2.3.30 1.6.2 diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 3f25e62078..3a6476ea1b 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -97,6 +97,7 @@ import { DeviceProfileDialogComponent } from '@home/components/profile/device-pr import { DeviceProfileAutocompleteComponent } from '@home/components/profile/device-profile-autocomplete.component'; import { MqttDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/mqtt-device-profile-transport-configuration.component'; import { Lwm2mDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/lwm2m-device-profile-transport-configuration.component'; +import {CoapDeviceProfileTransportConfigurationComponent} from '@home/components/profile/device/coap-device-profile-transport-configuration.component'; import { DeviceProfileAlarmsComponent } from '@home/components/profile/alarm/device-profile-alarms.component'; import { DeviceProfileAlarmComponent } from '@home/components/profile/alarm/device-profile-alarm.component'; import { CreateAlarmRulesComponent } from '@home/components/profile/alarm/create-alarm-rules.component'; @@ -213,6 +214,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag DefaultDeviceProfileTransportConfigurationComponent, MqttDeviceProfileTransportConfigurationComponent, Lwm2mDeviceProfileTransportConfigurationComponent, + CoapDeviceProfileTransportConfigurationComponent, DeviceProfileTransportConfigurationComponent, CreateAlarmRulesComponent, AlarmRuleComponent, @@ -315,6 +317,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag DefaultDeviceProfileTransportConfigurationComponent, MqttDeviceProfileTransportConfigurationComponent, Lwm2mDeviceProfileTransportConfigurationComponent, + CoapDeviceProfileTransportConfigurationComponent, DeviceProfileTransportConfigurationComponent, CreateAlarmRulesComponent, AlarmRuleComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.html new file mode 100644 index 0000000000..e04ecfb258 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.html @@ -0,0 +1,69 @@ + +
+
+
+ device-profile.coap-device-type +
+ + + + {{coapTransportDeviceTypeTranslations.get(type) | translate}} + + + + {{ 'device-profile.coap-device-type-required' | translate }} + + +
+
+
+
+ device-profile.coap-device-payload-type +
+ + + + {{transportPayloadTypeTranslations.get(type) | translate}} + + + + {{ 'device-profile.mqtt-payload-type-required' | translate }} + + +
+ + device-profile.telemetry-proto-schema + + + {{ 'device-profile.telemetry-proto-schema-required' | translate}} + + + + device-profile.attributes-proto-schema + + + {{ 'device-profile.attributes-proto-schema-required' | translate}} + + +
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.scss new file mode 100644 index 0000000000..d1a270602e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.scss @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2021 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. + */ +:host{ + .fields-group { + padding: 8px; + margin: 10px 0; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + + legend { + color: rgba(0, 0, 0, .7); + } + + .tb-hint{ + padding: 0; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts new file mode 100644 index 0000000000..4a686d39bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts @@ -0,0 +1,159 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { + CoapDeviceProfileTransportConfiguration, + coapDeviceTypeTranslationMap, + CoapTransportDeviceType, + defaultAttributesSchema, + defaultTelemetrySchema, + DeviceProfileTransportConfiguration, + DeviceTransportType, + TransportPayloadType, + transportPayloadTypeTranslationMap, +} from '@shared/models/device.models'; +import { isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-coap-device-profile-transport-configuration', + templateUrl: './coap-device-profile-transport-configuration.component.html', + styleUrls: ['./coap-device-profile-transport-configuration.component.scss'], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CoapDeviceProfileTransportConfigurationComponent), + multi: true + }] +}) +export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { + + coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType); + + coapTransportDeviceTypeTranslations = coapDeviceTypeTranslationMap; + + transportPayloadTypes = Object.keys(TransportPayloadType); + + transportPayloadTypeTranslations = transportPayloadTypeTranslationMap; + + coapDeviceProfileTransportConfigurationFormGroup: FormGroup; + + private requiredValue: boolean; + + private transportPayloadTypeConfiguration = this.fb.group({ + transportPayloadType: [TransportPayloadType.JSON, Validators.required], + deviceTelemetryProtoSchema: [defaultTelemetrySchema, Validators.required], + deviceAttributesProtoSchema: [defaultAttributesSchema, Validators.required] + }); + + get required(): boolean { + return this.requiredValue; + } + + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() + disabled: boolean; + + private propagateChange = (v: any) => { }; + + constructor(private store: Store, + private fb: FormBuilder) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.coapDeviceProfileTransportConfigurationFormGroup = this.fb.group({ + coapDeviceTypeConfiguration: this.fb.group({ + coapDeviceType: [CoapTransportDeviceType.DEFAULT, Validators.required], + transportPayloadTypeConfiguration: this.transportPayloadTypeConfiguration + }) + } + ); + this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType') + .valueChanges.subscribe(coapDeviceType => { + this.updateCoapDeviceTypeBasedControls(coapDeviceType, true); + }); + this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + get coapDeviceTypeDefault(): boolean { + const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value; + return coapDeviceType === CoapTransportDeviceType.DEFAULT; + } + + get protoPayloadType(): boolean { + const transportPayloadTypePath = 'coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.transportPayloadType'; + const transportPayloadType = this.coapDeviceProfileTransportConfigurationFormGroup.get(transportPayloadTypePath).value; + return transportPayloadType === TransportPayloadType.PROTOBUF; + } + + + private updateCoapDeviceTypeBasedControls(type: CoapTransportDeviceType, forceUpdated = false) { + const coapDeviceTypeConfigurationFormGroup = this.coapDeviceProfileTransportConfigurationFormGroup + .get('coapDeviceTypeConfiguration') as FormGroup; + if (forceUpdated) { + coapDeviceTypeConfigurationFormGroup.patchValue({ + transportPayloadTypeConfiguration: this.transportPayloadTypeConfiguration + }, {emitEvent: false}); + } + if (type === CoapTransportDeviceType.DEFAULT && !this.disabled) { + coapDeviceTypeConfigurationFormGroup.get('transportPayloadTypeConfiguration').enable({emitEvent: false}); + } else { + coapDeviceTypeConfigurationFormGroup.get('transportPayloadTypeConfiguration').disable({emitEvent: false}); + } + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.coapDeviceProfileTransportConfigurationFormGroup.disable({emitEvent: false}); + } else { + this.coapDeviceProfileTransportConfigurationFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: CoapDeviceProfileTransportConfiguration | null): void { + if (isDefinedAndNotNull(value)) { + this.coapDeviceProfileTransportConfigurationFormGroup.patchValue(value, {emitEvent: false}); + this.updateCoapDeviceTypeBasedControls(value.coapDeviceTypeConfiguration?.coapDeviceType); + } + } + + private updateModel() { + let configuration: DeviceProfileTransportConfiguration = null; + if (this.coapDeviceProfileTransportConfigurationFormGroup.valid) { + configuration = this.coapDeviceProfileTransportConfigurationFormGroup.value; + configuration.type = DeviceTransportType.COAP; + } + this.propagateChange(configuration); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html index 78707a9221..e01c21ff96 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html @@ -35,5 +35,11 @@ formControlName="configuration"> + + + + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html index ebbe10b481..670e6fbb72 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.html @@ -65,8 +65,8 @@
- - {{mqttTransportPayloadTypeTranslations.get(type) | translate}} + + {{transportPayloadTypeTranslations.get(type) | translate}} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index c01eeae4e5..a61ad841aa 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -28,11 +28,13 @@ import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { + defaultAttributesSchema, + defaultTelemetrySchema, DeviceProfileTransportConfiguration, DeviceTransportType, MqttDeviceProfileTransportConfiguration, - MqttTransportPayloadType, - mqttTransportPayloadTypeTranslationMap + TransportPayloadType, + transportPayloadTypeTranslationMap } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; @@ -48,40 +50,12 @@ import { isDefinedAndNotNull } from '@core/utils'; }) export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { - mqttTransportPayloadTypes = Object.keys(MqttTransportPayloadType); + transportPayloadTypes = Object.keys(TransportPayloadType); - mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; + transportPayloadTypeTranslations = transportPayloadTypeTranslationMap; mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; - private defaultTelemetrySchema = - 'syntax ="proto3";\n' + - 'package telemetry;\n' + - '\n' + - 'message SensorDataReading {\n' + - '\n' + - ' double temperature = 1;\n' + - ' double humidity = 2;\n' + - ' InnerObject innerObject = 3;\n' + - '\n' + - ' message InnerObject {\n' + - ' string key1 = 1;\n' + - ' bool key2 = 2;\n' + - ' double key3 = 3;\n' + - ' int32 key4 = 4;\n' + - ' string key5 = 5;\n' + - ' }\n' + - '}\n'; - - private defaultAttributesSchema = - 'syntax ="proto3";\n' + - 'package attributes;\n' + - '\n' + - 'message SensorConfiguration {\n' + - ' string firmwareVersion = 1;\n' + - ' string serialNumber = 2;\n' + - '}'; - private requiredValue: boolean; get required(): boolean { @@ -114,9 +88,9 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], transportPayloadTypeConfiguration: this.fb.group({ - transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required], - deviceTelemetryProtoSchema: [this.defaultTelemetrySchema, Validators.required], - deviceAttributesProtoSchema: [this.defaultAttributesSchema, Validators.required] + transportPayloadType: [TransportPayloadType.JSON, Validators.required], + deviceTelemetryProtoSchema: [defaultTelemetrySchema, Validators.required], + deviceAttributesProtoSchema: [defaultAttributesSchema, Validators.required] }) }, {validator: this.uniqueDeviceTopicValidator} ); @@ -140,7 +114,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control get protoPayloadType(): boolean { const transportPayloadType = this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType').value; - return transportPayloadType === MqttTransportPayloadType.PROTOBUF; + return transportPayloadType === TransportPayloadType.PROTOBUF; } writeValue(value: MqttDeviceProfileTransportConfiguration | null): void { @@ -159,16 +133,16 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control this.propagateChange(configuration); } - private updateTransportPayloadBasedControls(type: MqttTransportPayloadType, forceUpdated = false) { + private updateTransportPayloadBasedControls(type: TransportPayloadType, forceUpdated = false) { const transportPayloadTypeForm = this.mqttDeviceProfileTransportConfigurationFormGroup .get('transportPayloadTypeConfiguration') as FormGroup; if (forceUpdated) { transportPayloadTypeForm.patchValue({ - deviceTelemetryProtoSchema: this.defaultTelemetrySchema, - deviceAttributesProtoSchema: this.defaultAttributesSchema + deviceTelemetryProtoSchema: defaultTelemetrySchema, + deviceAttributesProtoSchema: defaultAttributesSchema }, {emitEvent: false}); } - if (type === MqttTransportPayloadType.PROTOBUF && !this.disabled) { + if (type === TransportPayloadType.PROTOBUF && !this.disabled) { transportPayloadTypeForm.get('deviceTelemetryProtoSchema').enable({emitEvent: false}); transportPayloadTypeForm.get('deviceAttributesProtoSchema').enable({emitEvent: false}); } else { diff --git a/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.html b/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.html new file mode 100644 index 0000000000..c0f47c989f --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.html @@ -0,0 +1,24 @@ + +
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts new file mode 100644 index 0000000000..b41d289f76 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/device/data/coap-device-transport-configuration.component.ts @@ -0,0 +1,97 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { + CoapDeviceTransportConfiguration, + DeviceTransportConfiguration, + DeviceTransportType +} from '@shared/models/device.models'; + +@Component({ + selector: 'tb-coap-device-transport-configuration', + templateUrl: './coap-device-transport-configuration.component.html', + styleUrls: [], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CoapDeviceTransportConfigurationComponent), + multi: true + }] +}) +export class CoapDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit { + + coapDeviceTransportConfigurationFormGroup: FormGroup; + + private requiredValue: boolean; + get required(): boolean { + return this.requiredValue; + } + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() + disabled: boolean; + + private propagateChange = (v: any) => { }; + + constructor(private store: Store, + private fb: FormBuilder) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.coapDeviceTransportConfigurationFormGroup = this.fb.group({ + configuration: [null, Validators.required] + }); + this.coapDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.coapDeviceTransportConfigurationFormGroup.disable({emitEvent: false}); + } else { + this.coapDeviceTransportConfigurationFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: CoapDeviceTransportConfiguration | null): void { + this.coapDeviceTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false}); + } + + private updateModel() { + let configuration: DeviceTransportConfiguration = null; + if (this.coapDeviceTransportConfigurationFormGroup.valid) { + configuration = this.coapDeviceTransportConfigurationFormGroup.getRawValue().configuration; + configuration.type = DeviceTransportType.COAP; + } + this.propagateChange(configuration); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html index 73cceba5bc..86f6ccb97d 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html @@ -35,5 +35,11 @@ formControlName="configuration"> + + + +
diff --git a/ui-ngx/src/app/modules/home/pages/device/device.module.ts b/ui-ngx/src/app/modules/home/pages/device/device.module.ts index 7ba9a5c0fe..174a3ddb7f 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.module.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.module.ts @@ -31,6 +31,7 @@ import { DefaultDeviceTransportConfigurationComponent } from './data/default-dev import { DeviceTransportConfigurationComponent } from './data/device-transport-configuration.component'; import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-transport-configuration.component'; import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component'; +import { CoapDeviceTransportConfigurationComponent } from './data/coap-device-transport-configuration.component'; @NgModule({ declarations: [ @@ -39,6 +40,7 @@ import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device- DefaultDeviceTransportConfigurationComponent, MqttDeviceTransportConfigurationComponent, Lwm2mDeviceTransportConfigurationComponent, + CoapDeviceTransportConfigurationComponent, DeviceTransportConfigurationComponent, DeviceDataComponent, DeviceComponent, diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index dcf3f34093..0373f79737 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -36,13 +36,19 @@ export enum DeviceTransportType { DEFAULT = 'DEFAULT', MQTT = 'MQTT', // LWM2M = 'LWM2M' + COAP = 'COAP' } -export enum MqttTransportPayloadType { +export enum TransportPayloadType { JSON = 'JSON', PROTOBUF = 'PROTOBUF' } +export enum CoapTransportDeviceType { + DEFAULT = 'DEFAULT', + EFENTO = 'EFENTO' +} + export enum DeviceProvisionType { DISABLED = 'DISABLED', ALLOW_CREATE_NEW_DEVICES = 'ALLOW_CREATE_NEW_DEVICES', @@ -77,6 +83,7 @@ export const deviceTransportTypeTranslationMap = new Map( [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'], [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'], // [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint'] + [DeviceTransportType.COAP, 'device-profile.transport-type-coap-hint'] ] ); -export const mqttTransportPayloadTypeTranslationMap = new Map( +export const transportPayloadTypeTranslationMap = new Map( [ - [MqttTransportPayloadType.JSON, 'device-profile.mqtt-device-payload-type-json'], - [MqttTransportPayloadType.PROTOBUF, 'device-profile.mqtt-device-payload-type-proto'] + [TransportPayloadType.JSON, 'device-profile.transport-device-payload-type-json'], + [TransportPayloadType.PROTOBUF, 'device-profile.transport-device-payload-type-proto'] + ] +); + +export const defaultTelemetrySchema = + 'syntax ="proto3";\n' + + 'package telemetry;\n' + + '\n' + + 'message SensorDataReading {\n' + + '\n' + + ' double temperature = 1;\n' + + ' double humidity = 2;\n' + + ' InnerObject innerObject = 3;\n' + + '\n' + + ' message InnerObject {\n' + + ' string key1 = 1;\n' + + ' bool key2 = 2;\n' + + ' double key3 = 3;\n' + + ' int32 key4 = 4;\n' + + ' string key5 = 5;\n' + + ' }\n' + + '}\n'; + +export const defaultAttributesSchema = + 'syntax ="proto3";\n' + + 'package attributes;\n' + + '\n' + + 'message SensorConfiguration {\n' + + ' string firmwareVersion = 1;\n' + + ' string serialNumber = 2;\n' + + '}'; + +export const coapDeviceTypeTranslationMap = new Map( + [ + [CoapTransportDeviceType.DEFAULT, 'device-profile.coap-device-type-default'], + [CoapTransportDeviceType.EFENTO, 'device-profile.coap-device-type-efento'] ] ); @@ -128,6 +171,13 @@ export const deviceTransportTypeConfigurationInfoMap = new Map[+] and multi-level [#] wildcards supported.", "telemetry-topic-filter": "Telemetry topic filter", "telemetry-topic-filter-required": "Telemetry topic filter is required.",