Commit 321fb731a6537399a6e4572837967fe41cdaf58a

Authored by ShvaykaD
Committed by GitHub
1 parent 51b0d505

Support of server-side RPC requests via protobuf for CoAP and MQTT

* rpc response schema added

* rpc request proto

* fixed device profile validation

* fix error messages
Showing 32 changed files with 568 additions and 165 deletions
@@ -15,35 +15,11 @@ @@ -15,35 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.transport; 16 package org.thingsboard.server.transport;
17 17
18 -import com.fasterxml.jackson.databind.node.ObjectNode;  
19 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
20 -import org.eclipse.paho.client.mqttv3.MqttAsyncClient;  
21 -import org.eclipse.paho.client.mqttv3.MqttConnectOptions;  
22 -import org.eclipse.paho.client.mqttv3.MqttException;  
23 -import org.eclipse.paho.client.mqttv3.MqttMessage;  
24 -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;  
25 -import org.junit.Assert;  
26 -import org.springframework.util.StringUtils;  
27 import org.thingsboard.server.common.data.Device; 19 import org.thingsboard.server.common.data.Device;
28 import org.thingsboard.server.common.data.DeviceProfile; 20 import org.thingsboard.server.common.data.DeviceProfile;
29 -import org.thingsboard.server.common.data.DeviceProfileProvisionType;  
30 -import org.thingsboard.server.common.data.DeviceProfileType;  
31 -import org.thingsboard.server.common.data.DeviceTransportType;  
32 import org.thingsboard.server.common.data.Tenant; 21 import org.thingsboard.server.common.data.Tenant;
33 -import org.thingsboard.server.common.data.TransportPayloadType;  
34 import org.thingsboard.server.common.data.User; 22 import org.thingsboard.server.common.data.User;
35 -import org.thingsboard.server.common.data.device.profile.AllowCreateNewDevicesDeviceProfileProvisionConfiguration;  
36 -import org.thingsboard.server.common.data.device.profile.CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration;  
37 -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;  
38 -import org.thingsboard.server.common.data.device.profile.DeviceProfileData;  
39 -import org.thingsboard.server.common.data.device.profile.DeviceProfileProvisionConfiguration;  
40 -import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;  
41 -import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;  
42 -import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;  
43 -import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;  
44 -import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;  
45 -import org.thingsboard.server.common.data.security.Authority;  
46 -import org.thingsboard.server.common.data.security.DeviceCredentials;  
47 import org.thingsboard.server.controller.AbstractControllerTest; 23 import org.thingsboard.server.controller.AbstractControllerTest;
48 import org.thingsboard.server.gen.transport.TransportProtos; 24 import org.thingsboard.server.gen.transport.TransportProtos;
49 25
@@ -51,8 +27,6 @@ import java.util.ArrayList; @@ -51,8 +27,6 @@ import java.util.ArrayList;
51 import java.util.List; 27 import java.util.List;
52 import java.util.concurrent.atomic.AtomicInteger; 28 import java.util.concurrent.atomic.AtomicInteger;
53 29
54 -import static org.junit.Assert.assertEquals;  
55 -import static org.junit.Assert.assertNotNull;  
56 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 30 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
57 31
58 @Slf4j 32 @Slf4j
@@ -105,6 +79,22 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle @@ -105,6 +79,22 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle
105 " }\n" + 79 " }\n" +
106 "}"; 80 "}";
107 81
  82 + protected static final String DEVICE_RPC_RESPONSE_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
  83 + "package rpc;\n" +
  84 + "\n" +
  85 + "message RpcResponseMsg {\n" +
  86 + " string payload = 1;\n" +
  87 + "}";
  88 +
  89 + protected static final String DEVICE_RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
  90 + "package rpc;\n" +
  91 + "\n" +
  92 + "message RpcRequestMsg {\n" +
  93 + " string method = 1;\n" +
  94 + " int32 requestId = 2;\n" +
  95 + " string params = 3;\n" +
  96 + "}";
  97 +
108 protected Tenant savedTenant; 98 protected Tenant savedTenant;
109 protected User tenantAdmin; 99 protected User tenantAdmin;
110 100
@@ -53,7 +53,7 @@ import static org.junit.Assert.assertNotNull; @@ -53,7 +53,7 @@ import static org.junit.Assert.assertNotNull;
53 public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest { 53 public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest {
54 54
55 protected void processBeforeTest(String deviceName, CoapDeviceType coapDeviceType, TransportPayloadType payloadType) throws Exception { 55 protected void processBeforeTest(String deviceName, CoapDeviceType coapDeviceType, TransportPayloadType payloadType) throws Exception {
56 - this.processBeforeTest(deviceName, coapDeviceType, payloadType, null, null, DeviceProfileProvisionType.DISABLED, null, null); 56 + this.processBeforeTest(deviceName, coapDeviceType, payloadType, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
57 } 57 }
58 58
59 protected void processBeforeTest(String deviceName, 59 protected void processBeforeTest(String deviceName,
@@ -61,8 +61,11 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg @@ -61,8 +61,11 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg
61 TransportPayloadType payloadType, 61 TransportPayloadType payloadType,
62 String telemetryProtoSchema, 62 String telemetryProtoSchema,
63 String attributesProtoSchema, 63 String attributesProtoSchema,
64 - DeviceProfileProvisionType provisionType,  
65 - String provisionKey, String provisionSecret 64 + String rpcResponseProtoSchema,
  65 + String rpcRequestProtoSchema,
  66 + String provisionKey,
  67 + String provisionSecret,
  68 + DeviceProfileProvisionType provisionType
66 ) throws Exception { 69 ) throws Exception {
67 loginSysAdmin(); 70 loginSysAdmin();
68 71
@@ -85,7 +88,7 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg @@ -85,7 +88,7 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg
85 device.setType("default"); 88 device.setType("default");
86 89
87 if (coapDeviceType != null) { 90 if (coapDeviceType != null) {
88 - DeviceProfile coapDeviceProfile = createCoapDeviceProfile(payloadType, coapDeviceType, attributesProtoSchema, provisionType, provisionKey, provisionSecret, telemetryProtoSchema); 91 + DeviceProfile coapDeviceProfile = createCoapDeviceProfile(payloadType, coapDeviceType, provisionSecret, provisionType, provisionKey, attributesProtoSchema, telemetryProtoSchema, rpcResponseProtoSchema, rpcRequestProtoSchema);
89 deviceProfile = doPost("/api/deviceProfile", coapDeviceProfile, DeviceProfile.class); 92 deviceProfile = doPost("/api/deviceProfile", coapDeviceProfile, DeviceProfile.class);
90 device.setType(deviceProfile.getName()); 93 device.setType(deviceProfile.getName());
91 device.setDeviceProfileId(deviceProfile.getId()); 94 device.setDeviceProfileId(deviceProfile.getId());
@@ -103,8 +106,9 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg @@ -103,8 +106,9 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg
103 } 106 }
104 107
105 protected DeviceProfile createCoapDeviceProfile(TransportPayloadType transportPayloadType, CoapDeviceType coapDeviceType, 108 protected DeviceProfile createCoapDeviceProfile(TransportPayloadType transportPayloadType, CoapDeviceType coapDeviceType,
106 - String attributesProtoSchema, DeviceProfileProvisionType provisionType,  
107 - String provisionKey, String provisionSecret, String telemetryProtoSchema) { 109 + String provisionSecret, DeviceProfileProvisionType provisionType,
  110 + String provisionKey, String attributesProtoSchema,
  111 + String telemetryProtoSchema, String rpcResponseProtoSchema, String rpcRequestProtoSchema) {
108 DeviceProfile deviceProfile = new DeviceProfile(); 112 DeviceProfile deviceProfile = new DeviceProfile();
109 deviceProfile.setName(transportPayloadType.name()); 113 deviceProfile.setName(transportPayloadType.name());
110 deviceProfile.setType(DeviceProfileType.DEFAULT); 114 deviceProfile.setType(DeviceProfileType.DEFAULT);
@@ -127,8 +131,16 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg @@ -127,8 +131,16 @@ public abstract class AbstractCoapIntegrationTest extends AbstractTransportInteg
127 if (StringUtils.isEmpty(attributesProtoSchema)) { 131 if (StringUtils.isEmpty(attributesProtoSchema)) {
128 attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA; 132 attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA;
129 } 133 }
  134 + if (StringUtils.isEmpty(rpcResponseProtoSchema)) {
  135 + rpcResponseProtoSchema = DEVICE_RPC_RESPONSE_PROTO_SCHEMA;
  136 + }
  137 + if (StringUtils.isEmpty(rpcRequestProtoSchema)) {
  138 + rpcRequestProtoSchema = DEVICE_RPC_REQUEST_PROTO_SCHEMA;
  139 + }
130 protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema); 140 protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema);
131 protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema); 141 protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema);
  142 + protoTransportPayloadConfiguration.setDeviceRpcResponseProtoSchema(rpcResponseProtoSchema);
  143 + protoTransportPayloadConfiguration.setDeviceRpcRequestProtoSchema(rpcRequestProtoSchema);
132 transportPayloadTypeConfiguration = protoTransportPayloadConfiguration; 144 transportPayloadTypeConfiguration = protoTransportPayloadConfiguration;
133 } else { 145 } else {
134 transportPayloadTypeConfiguration = new JsonTransportPayloadConfiguration(); 146 transportPayloadTypeConfiguration = new JsonTransportPayloadConfiguration();
@@ -79,7 +79,7 @@ public abstract class AbstractCoapAttributesRequestProtoIntegrationTest extends @@ -79,7 +79,7 @@ public abstract class AbstractCoapAttributesRequestProtoIntegrationTest extends
79 @Test 79 @Test
80 public void testRequestAttributesValuesFromTheServer() throws Exception { 80 public void testRequestAttributesValuesFromTheServer() throws Exception {
81 super.processBeforeTest("Test Request attribute values from the server proto", CoapDeviceType.DEFAULT, 81 super.processBeforeTest("Test Request attribute values from the server proto", CoapDeviceType.DEFAULT,
82 - TransportPayloadType.PROTOBUF, null, ATTRIBUTES_SCHEMA_STR, DeviceProfileProvisionType.DISABLED, null, null); 82 + TransportPayloadType.PROTOBUF, null, ATTRIBUTES_SCHEMA_STR, null, null, null, null, DeviceProfileProvisionType.DISABLED);
83 processTestRequestAttributesValuesFromTheServer(); 83 processTestRequestAttributesValuesFromTheServer();
84 } 84 }
85 85
@@ -88,7 +88,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn @@ -88,7 +88,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn
88 88
89 89
90 private void processTestProvisioningDisabledDevice() throws Exception { 90 private void processTestProvisioningDisabledDevice() throws Exception {
91 - super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.DISABLED, null, null); 91 + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
92 byte[] result = createCoapClientAndPublish().getPayload(); 92 byte[] result = createCoapClientAndPublish().getPayload();
93 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 93 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
94 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); 94 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
@@ -97,7 +97,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn @@ -97,7 +97,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn
97 97
98 98
99 private void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { 99 private void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
100 - super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 100 + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
101 byte[] result = createCoapClientAndPublish().getPayload(); 101 byte[] result = createCoapClientAndPublish().getPayload();
102 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 102 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
103 103
@@ -113,7 +113,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn @@ -113,7 +113,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn
113 113
114 114
115 private void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { 115 private void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
116 - super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 116 + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
117 String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\""; 117 String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\"";
118 byte[] result = createCoapClientAndPublish(requestCredentials).getPayload(); 118 byte[] result = createCoapClientAndPublish(requestCredentials).getPayload();
119 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 119 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
@@ -132,7 +132,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn @@ -132,7 +132,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn
132 132
133 133
134 private void processTestProvisioningCreateNewDeviceWithCert() throws Exception { 134 private void processTestProvisioningCreateNewDeviceWithCert() throws Exception {
135 - super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 135 + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
136 String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\""; 136 String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\"";
137 byte[] result = createCoapClientAndPublish(requestCredentials).getPayload(); 137 byte[] result = createCoapClientAndPublish(requestCredentials).getPayload();
138 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 138 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
@@ -156,7 +156,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn @@ -156,7 +156,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn
156 } 156 }
157 157
158 private void processTestProvisioningCheckPreProvisionedDevice() throws Exception { 158 private void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
159 - super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); 159 + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
160 byte[] result = createCoapClientAndPublish().getPayload(); 160 byte[] result = createCoapClientAndPublish().getPayload();
161 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 161 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
162 162
@@ -167,7 +167,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn @@ -167,7 +167,7 @@ public abstract class AbstractCoapProvisionJsonDeviceTest extends AbstractCoapIn
167 } 167 }
168 168
169 private void processTestProvisioningWithBadKeyDevice() throws Exception { 169 private void processTestProvisioningWithBadKeyDevice() throws Exception {
170 - super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); 170 + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.JSON, null, null, null, null, "testProvisionKeyOrig", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
171 byte[] result = createCoapClientAndPublish().getPayload(); 171 byte[] result = createCoapClientAndPublish().getPayload();
172 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 172 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
173 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); 173 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
@@ -92,14 +92,14 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI @@ -92,14 +92,14 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI
92 92
93 93
94 private void processTestProvisioningDisabledDevice() throws Exception { 94 private void processTestProvisioningDisabledDevice() throws Exception {
95 - super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.DISABLED, null, null); 95 + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
96 ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); 96 ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload());
97 Assert.assertNotNull(result); 97 Assert.assertNotNull(result);
98 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString()); 98 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString());
99 } 99 }
100 100
101 private void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { 101 private void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
102 - super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 102 + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
103 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); 103 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload());
104 104
105 Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); 105 Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device");
@@ -113,7 +113,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI @@ -113,7 +113,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI
113 } 113 }
114 114
115 private void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { 115 private void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
116 - super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 116 + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
117 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build(); 117 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build();
118 118
119 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayload()); 119 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayload());
@@ -131,7 +131,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI @@ -131,7 +131,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI
131 } 131 }
132 132
133 private void processTestProvisioningCreateNewDeviceWithCert() throws Exception { 133 private void processTestProvisioningCreateNewDeviceWithCert() throws Exception {
134 - super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 134 + super.processBeforeTest("Test Provision device3", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
135 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build(); 135 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build();
136 136
137 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayload()); 137 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayload());
@@ -155,7 +155,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI @@ -155,7 +155,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI
155 } 155 }
156 156
157 private void processTestProvisioningCheckPreProvisionedDevice() throws Exception { 157 private void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
158 - super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); 158 + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
159 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); 159 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload());
160 160
161 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); 161 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId());
@@ -165,7 +165,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI @@ -165,7 +165,7 @@ public abstract class AbstractCoapProvisionProtoDeviceTest extends AbstractCoapI
165 } 165 }
166 166
167 private void processTestProvisioningWithBadKeyDevice() throws Exception { 167 private void processTestProvisioningWithBadKeyDevice() throws Exception {
168 - super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); 168 + super.processBeforeTest("Test Provision device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, null, "testProvisionKeyOrig", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
169 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload()); 169 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createCoapClientAndPublish().getPayload());
170 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString()); 170 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString());
171 } 171 }
@@ -124,7 +124,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -124,7 +124,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
124 return COAP_BASE_URL + token + "/" + FeatureType.RPC.name().toLowerCase() + "/" + requestId; 124 return COAP_BASE_URL + token + "/" + FeatureType.RPC.name().toLowerCase() + "/" + requestId;
125 } 125 }
126 126
127 - private class TestCoapCallback implements CoapHandler { 127 + protected class TestCoapCallback implements CoapHandler {
128 128
129 private final CoapClient client; 129 private final CoapClient client;
130 private final CountDownLatch latch; 130 private final CountDownLatch latch;
@@ -136,7 +136,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -136,7 +136,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
136 136
137 private Integer observe; 137 private Integer observe;
138 138
139 - private TestCoapCallback(CoapClient client, CountDownLatch latch, boolean isOneWayRpc) { 139 + TestCoapCallback(CoapClient client, CountDownLatch latch, boolean isOneWayRpc) {
140 this.client = client; 140 this.client = client;
141 this.latch = latch; 141 this.latch = latch;
142 this.isOneWayRpc = isOneWayRpc; 142 this.isOneWayRpc = isOneWayRpc;
@@ -144,7 +144,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC @@ -144,7 +144,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
144 144
145 @Override 145 @Override
146 public void onLoad(CoapResponse response) { 146 public void onLoad(CoapResponse response) {
147 - log.warn("coap response: {}, {}", response, response.getCode()); 147 + log.warn("coap response: {}, {}", response.getResponseText(), response.getCode());
148 assertNotNull(response.getPayload()); 148 assertNotNull(response.getPayload());
149 assertEquals(response.getCode(), CoAP.ResponseCode.CONTENT); 149 assertEquals(response.getCode(), CoAP.ResponseCode.CONTENT);
150 observe = response.getOptions().getObserve(); 150 observe = response.getOptions().getObserve();
@@ -36,12 +36,12 @@ public abstract class AbstractCoapServerSideRpcJsonIntegrationTest extends Abstr @@ -36,12 +36,12 @@ public abstract class AbstractCoapServerSideRpcJsonIntegrationTest extends Abstr
36 } 36 }
37 37
38 @Test 38 @Test
39 - public void testServerMqttOneWayRpc() throws Exception { 39 + public void testServerCoapOneWayRpc() throws Exception {
40 processOneWayRpcTest(); 40 processOneWayRpcTest();
41 } 41 }
42 42
43 @Test 43 @Test
44 - public void testServerMqttTwoWayRpc() throws Exception { 44 + public void testServerCoapTwoWayRpc() throws Exception {
45 processTwoWayRpcTest(); 45 processTwoWayRpcTest();
46 } 46 }
47 47
@@ -15,26 +15,62 @@ @@ -15,26 +15,62 @@
15 */ 15 */
16 package org.thingsboard.server.transport.coap.rpc; 16 package org.thingsboard.server.transport.coap.rpc;
17 17
  18 +import com.github.os72.protobuf.dynamic.DynamicSchema;
  19 +import com.google.protobuf.Descriptors;
  20 +import com.google.protobuf.DynamicMessage;
  21 +import com.google.protobuf.InvalidProtocolBufferException;
  22 +import com.squareup.wire.schema.internal.parser.ProtoFileElement;
18 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
19 import org.eclipse.californium.core.CoapClient; 24 import org.eclipse.californium.core.CoapClient;
20 import org.eclipse.californium.core.CoapHandler; 25 import org.eclipse.californium.core.CoapHandler;
  26 +import org.eclipse.californium.core.CoapObserveRelation;
21 import org.eclipse.californium.core.CoapResponse; 27 import org.eclipse.californium.core.CoapResponse;
  28 +import org.eclipse.californium.core.coap.CoAP;
22 import org.eclipse.californium.core.coap.MediaTypeRegistry; 29 import org.eclipse.californium.core.coap.MediaTypeRegistry;
  30 +import org.eclipse.californium.core.coap.Request;
23 import org.junit.After; 31 import org.junit.After;
24 import org.junit.Before; 32 import org.junit.Before;
25 import org.junit.Test; 33 import org.junit.Test;
26 import org.thingsboard.server.common.data.CoapDeviceType; 34 import org.thingsboard.server.common.data.CoapDeviceType;
  35 +import org.thingsboard.server.common.data.DeviceProfileProvisionType;
27 import org.thingsboard.server.common.data.TransportPayloadType; 36 import org.thingsboard.server.common.data.TransportPayloadType;
28 -import org.thingsboard.server.gen.transport.TransportProtos; 37 +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
  38 +import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration;
  39 +import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration;
  40 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  41 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  42 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
  43 +import org.thingsboard.server.common.msg.session.FeatureType;
29 44
  45 +import java.util.List;
30 import java.util.concurrent.CountDownLatch; 46 import java.util.concurrent.CountDownLatch;
  47 +import java.util.concurrent.TimeUnit;
  48 +
  49 +import static org.junit.Assert.assertEquals;
  50 +import static org.junit.Assert.assertNotNull;
  51 +import static org.junit.Assert.assertTrue;
  52 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
31 53
32 @Slf4j 54 @Slf4j
33 public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends AbstractCoapServerSideRpcIntegrationTest { 55 public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends AbstractCoapServerSideRpcIntegrationTest {
34 56
  57 + private static final String RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
  58 + "package rpc;\n" +
  59 + "\n" +
  60 + "message RpcRequestMsg {\n" +
  61 + " string method = 1;\n" +
  62 + " int32 requestId = 2;\n" +
  63 + " Params params = 3;\n" +
  64 + "\n" +
  65 + " message Params {\n" +
  66 + " string pin = 1;\n" +
  67 + " int32 value = 2;\n" +
  68 + " }\n" +
  69 + "}";
  70 +
35 @Before 71 @Before
36 public void beforeTest() throws Exception { 72 public void beforeTest() throws Exception {
37 - processBeforeTest("RPC test device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); 73 + processBeforeTest("RPC test device", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, null, null, null, RPC_REQUEST_PROTO_SCHEMA, null, null, DeviceProfileProvisionType.DISABLED);
38 } 74 }
39 75
40 @After 76 @After
@@ -43,33 +79,91 @@ public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends Abst @@ -43,33 +79,91 @@ public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends Abst
43 } 79 }
44 80
45 @Test 81 @Test
46 - public void testServerMqttOneWayRpc() throws Exception { 82 + public void testServerCoapOneWayRpc() throws Exception {
47 processOneWayRpcTest(); 83 processOneWayRpcTest();
48 } 84 }
49 85
50 @Test 86 @Test
51 - public void testServerMqttTwoWayRpc() throws Exception { 87 + public void testServerCoapTwoWayRpc() throws Exception {
52 processTwoWayRpcTest(); 88 processTwoWayRpcTest();
53 } 89 }
54 90
  91 + protected void processTwoWayRpcTest() throws Exception {
  92 + CoapClient client = getCoapClient(FeatureType.RPC);
  93 + client.useCONs();
  94 +
  95 + CountDownLatch latch = new CountDownLatch(1);
  96 + TestCoapCallback testCoapCallback = new TestCoapCallback(client, latch, false);
  97 +
  98 + Request request = Request.newGet().setObserve();
  99 + request.setType(CoAP.Type.CON);
  100 + CoapObserveRelation observeRelation = client.observe(request, testCoapCallback);
  101 +
  102 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
  103 + String deviceId = savedDevice.getId().getId().toString();
  104 +
  105 + String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
  106 +
  107 + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  108 + latch.await(3, TimeUnit.SECONDS);
  109 +
  110 + assertEquals(expected, result);
  111 + assertEquals(0, testCoapCallback.getObserve().intValue());
  112 + observeRelation.proactiveCancel();
  113 + assertTrue(observeRelation.isCanceled());
  114 + }
  115 +
55 @Override 116 @Override
56 protected void processOnLoadResponse(CoapResponse response, CoapClient client, Integer observe, CountDownLatch latch) { 117 protected void processOnLoadResponse(CoapResponse response, CoapClient client, Integer observe, CountDownLatch latch) {
57 client.setURI(getRpcResponseFeatureTokenUrl(accessToken, observe)); 118 client.setURI(getRpcResponseFeatureTokenUrl(accessToken, observe));
58 - TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()  
59 - .setPayload(DEVICE_RESPONSE)  
60 - .setRequestId(observe)  
61 - .build();  
62 - client.post(new CoapHandler() {  
63 - @Override  
64 - public void onLoad(CoapResponse response) {  
65 - log.warn("Command Response Ack: {}, {}", response.getCode(), response.getResponseText());  
66 - latch.countDown();  
67 - } 119 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = getProtoTransportPayloadConfiguration();
  120 + ProtoFileElement rpcRequestProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(RPC_REQUEST_PROTO_SCHEMA);
  121 + DynamicSchema rpcRequestProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(rpcRequestProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_REQUEST_PROTO_SCHEMA);
68 122
69 - @Override  
70 - public void onError() {  
71 - log.warn("Command Response Ack Error, No connect"); 123 + byte[] requestPayload = response.getPayload();
  124 + DynamicMessage.Builder rpcRequestMsg = rpcRequestProtoSchema.newMessageBuilder("RpcRequestMsg");
  125 + Descriptors.Descriptor rpcRequestMsgDescriptor = rpcRequestMsg.getDescriptorForType();
  126 + assertNotNull(rpcRequestMsgDescriptor);
  127 + try {
  128 + DynamicMessage dynamicMessage = DynamicMessage.parseFrom(rpcRequestMsgDescriptor, requestPayload);
  129 + List<Descriptors.FieldDescriptor> fields = rpcRequestMsgDescriptor.getFields();
  130 + for (Descriptors.FieldDescriptor fieldDescriptor: fields) {
  131 + assertTrue(dynamicMessage.hasField(fieldDescriptor));
72 } 132 }
73 - }, toDeviceRpcResponseMsg.toByteArray(), MediaTypeRegistry.APPLICATION_JSON); 133 + ProtoFileElement rpcResponseProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_RPC_RESPONSE_PROTO_SCHEMA);
  134 + DynamicSchema rpcResponseProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(rpcResponseProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_RESPONSE_PROTO_SCHEMA);
  135 + DynamicMessage.Builder rpcResponseBuilder = rpcResponseProtoSchema.newMessageBuilder("RpcResponseMsg");
  136 + Descriptors.Descriptor rpcResponseMsgDescriptor = rpcResponseBuilder.getDescriptorForType();
  137 + assertNotNull(rpcResponseMsgDescriptor);
  138 + DynamicMessage rpcResponseMsg = rpcResponseBuilder
  139 + .setField(rpcResponseMsgDescriptor.findFieldByName("payload"), DEVICE_RESPONSE)
  140 + .build();
  141 + client.post(new CoapHandler() {
  142 + @Override
  143 + public void onLoad(CoapResponse response) {
  144 + log.warn("Command Response Ack: {}, {}", response.getCode(), response.getResponseText());
  145 + latch.countDown();
  146 + }
  147 +
  148 + @Override
  149 + public void onError() {
  150 + log.warn("Command Response Ack Error, No connect");
  151 + }
  152 + }, rpcResponseMsg.toByteArray(), MediaTypeRegistry.APPLICATION_JSON);
  153 + } catch (InvalidProtocolBufferException e) {
  154 + log.warn("Command Response Ack Error, Invalid response received: ", e);
  155 + }
  156 + }
  157 +
  158 + private ProtoTransportPayloadConfiguration getProtoTransportPayloadConfiguration() {
  159 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  160 + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
  161 + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
  162 + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration();
  163 + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration);
  164 + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration;
  165 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration();
  166 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  167 + return (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
74 } 168 }
75 } 169 }
@@ -114,7 +114,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac @@ -114,7 +114,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
114 " }\n" + 114 " }\n" +
115 " }\n" + 115 " }\n" +
116 "}"; 116 "}";
117 - super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, DeviceProfileProvisionType.DISABLED, null, null); 117 + super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
118 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 118 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
119 assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); 119 assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
120 CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; 120 CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@@ -62,7 +62,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg @@ -62,7 +62,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
62 protected DeviceProfile deviceProfile; 62 protected DeviceProfile deviceProfile;
63 63
64 protected void processBeforeTest (String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { 64 protected void processBeforeTest (String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
65 - this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, null, null, DeviceProfileProvisionType.DISABLED, null, null); 65 + this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
66 } 66 }
67 67
68 protected void processBeforeTest(String deviceName, 68 protected void processBeforeTest(String deviceName,
@@ -72,9 +72,12 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg @@ -72,9 +72,12 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
72 String attributesTopic, 72 String attributesTopic,
73 String telemetryProtoSchema, 73 String telemetryProtoSchema,
74 String attributesProtoSchema, 74 String attributesProtoSchema,
75 - DeviceProfileProvisionType provisionType,  
76 - String provisionKey, String provisionSecret  
77 - ) throws Exception { 75 + String rpcResponseProtoSchema,
  76 + String rpcRequestProtoSchema,
  77 + String provisionKey,
  78 + String provisionSecret,
  79 + DeviceProfileProvisionType provisionType
  80 + ) throws Exception {
78 loginSysAdmin(); 81 loginSysAdmin();
79 82
80 Tenant tenant = new Tenant(); 83 Tenant tenant = new Tenant();
@@ -103,7 +106,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg @@ -103,7 +106,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
103 gateway.setAdditionalInfo(additionalInfo); 106 gateway.setAdditionalInfo(additionalInfo);
104 107
105 if (payloadType != null) { 108 if (payloadType != null) {
106 - DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, telemetryProtoSchema, attributesProtoSchema, provisionType, provisionKey, provisionSecret); 109 + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, telemetryProtoSchema, attributesProtoSchema, rpcResponseProtoSchema, rpcRequestProtoSchema, provisionKey, provisionSecret, provisionType);
107 deviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); 110 deviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class);
108 device.setType(deviceProfile.getName()); 111 device.setType(deviceProfile.getName());
109 device.setDeviceProfileId(deviceProfile.getId()); 112 device.setDeviceProfileId(deviceProfile.getId());
@@ -157,8 +160,9 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg @@ -157,8 +160,9 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
157 protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, 160 protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType,
158 String telemetryTopic, String attributesTopic, 161 String telemetryTopic, String attributesTopic,
159 String telemetryProtoSchema, String attributesProtoSchema, 162 String telemetryProtoSchema, String attributesProtoSchema,
160 - DeviceProfileProvisionType provisionType,  
161 - String provisionKey, String provisionSecret) { 163 + String rpcResponseProtoSchema, String rpcRequestProtoSchema,
  164 + String provisionKey, String provisionSecret,
  165 + DeviceProfileProvisionType provisionType) {
162 DeviceProfile deviceProfile = new DeviceProfile(); 166 DeviceProfile deviceProfile = new DeviceProfile();
163 deviceProfile.setName(transportPayloadType.name()); 167 deviceProfile.setName(transportPayloadType.name());
164 deviceProfile.setType(DeviceProfileType.DEFAULT); 168 deviceProfile.setType(DeviceProfileType.DEFAULT);
@@ -186,8 +190,16 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg @@ -186,8 +190,16 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
186 if (StringUtils.isEmpty(attributesProtoSchema)) { 190 if (StringUtils.isEmpty(attributesProtoSchema)) {
187 attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA; 191 attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA;
188 } 192 }
  193 + if (StringUtils.isEmpty(rpcResponseProtoSchema)) {
  194 + rpcResponseProtoSchema = DEVICE_RPC_RESPONSE_PROTO_SCHEMA;
  195 + }
  196 + if (StringUtils.isEmpty(rpcRequestProtoSchema)) {
  197 + rpcRequestProtoSchema = DEVICE_RPC_REQUEST_PROTO_SCHEMA;
  198 + }
189 protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema); 199 protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema);
190 protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema); 200 protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema);
  201 + protoTransportPayloadConfiguration.setDeviceRpcResponseProtoSchema(rpcResponseProtoSchema);
  202 + protoTransportPayloadConfiguration.setDeviceRpcRequestProtoSchema(rpcRequestProtoSchema);
191 transportPayloadTypeConfiguration = protoTransportPayloadConfiguration; 203 transportPayloadTypeConfiguration = protoTransportPayloadConfiguration;
192 } 204 }
193 mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); 205 mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
@@ -83,7 +83,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends @@ -83,7 +83,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends
83 @Test 83 @Test
84 public void testRequestAttributesValuesFromTheServer() throws Exception { 84 public void testRequestAttributesValuesFromTheServer() throws Exception {
85 super.processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", 85 super.processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto",
86 - TransportPayloadType.PROTOBUF, null, null, null, ATTRIBUTES_SCHEMA_STR, DeviceProfileProvisionType.DISABLED, null, null); 86 + TransportPayloadType.PROTOBUF, null, null, null, ATTRIBUTES_SCHEMA_STR, null, null, null, null, DeviceProfileProvisionType.DISABLED);
87 processTestRequestAttributesValuesFromTheServer(); 87 processTestRequestAttributesValuesFromTheServer();
88 } 88 }
89 89
@@ -94,7 +94,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -94,7 +94,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
94 94
95 95
96 protected void processTestProvisioningDisabledDevice() throws Exception { 96 protected void processTestProvisioningDisabledDevice() throws Exception {
97 - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); 97 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
98 byte[] result = createMqttClientAndPublish().getPayloadBytes(); 98 byte[] result = createMqttClientAndPublish().getPayloadBytes();
99 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 99 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
100 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); 100 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
@@ -103,7 +103,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -103,7 +103,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
103 103
104 104
105 protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { 105 protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
106 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 106 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
107 byte[] result = createMqttClientAndPublish().getPayloadBytes(); 107 byte[] result = createMqttClientAndPublish().getPayloadBytes();
108 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 108 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
109 109
@@ -119,7 +119,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -119,7 +119,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
119 119
120 120
121 protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { 121 protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
122 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 122 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
123 String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\""; 123 String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\"";
124 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); 124 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
125 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 125 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
@@ -138,7 +138,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -138,7 +138,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
138 138
139 139
140 protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { 140 protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception {
141 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 141 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
142 String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\""; 142 String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\"";
143 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); 143 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
144 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 144 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
@@ -163,7 +163,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -163,7 +163,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
163 163
164 164
165 protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { 165 protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception {
166 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 166 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
167 String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\""; 167 String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\"";
168 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); 168 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
169 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 169 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
@@ -188,7 +188,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -188,7 +188,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
188 } 188 }
189 189
190 protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { 190 protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
191 - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); 191 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
192 byte[] result = createMqttClientAndPublish().getPayloadBytes(); 192 byte[] result = createMqttClientAndPublish().getPayloadBytes();
193 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 193 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
194 194
@@ -199,7 +199,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn @@ -199,7 +199,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
199 } 199 }
200 200
201 protected void processTestProvisioningWithBadKeyDevice() throws Exception { 201 protected void processTestProvisioningWithBadKeyDevice() throws Exception {
202 - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); 202 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, null, null, "testProvisionKeyOrig", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
203 byte[] result = createMqttClientAndPublish().getPayloadBytes(); 203 byte[] result = createMqttClientAndPublish().getPayloadBytes();
204 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); 204 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
205 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); 205 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
@@ -101,14 +101,14 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI @@ -101,14 +101,14 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
101 101
102 102
103 protected void processTestProvisioningDisabledDevice() throws Exception { 103 protected void processTestProvisioningDisabledDevice() throws Exception {
104 - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); 104 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
105 ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); 105 ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
106 Assert.assertNotNull(result); 106 Assert.assertNotNull(result);
107 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString()); 107 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString());
108 } 108 }
109 109
110 protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { 110 protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
111 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 111 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
112 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); 112 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
113 113
114 Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); 114 Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device");
@@ -122,7 +122,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI @@ -122,7 +122,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
122 } 122 }
123 123
124 protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { 124 protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
125 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null,null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 125 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null,null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
126 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build(); 126 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build();
127 127
128 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes()); 128 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes());
@@ -140,7 +140,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI @@ -140,7 +140,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
140 } 140 }
141 141
142 protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { 142 protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception {
143 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 143 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
144 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build(); 144 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build();
145 145
146 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes()); 146 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes());
@@ -164,7 +164,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI @@ -164,7 +164,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
164 } 164 }
165 165
166 protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { 166 protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception {
167 - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); 167 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES);
168 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateBasicMqttCredRequestMsg( 168 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateBasicMqttCredRequestMsg(
169 ValidateBasicMqttCredRequestMsg.newBuilder() 169 ValidateBasicMqttCredRequestMsg.newBuilder()
170 .setClientId("test_clientId") 170 .setClientId("test_clientId")
@@ -195,7 +195,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI @@ -195,7 +195,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
195 } 195 }
196 196
197 protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { 197 protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
198 - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); 198 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, "testProvisionKey", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
199 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); 199 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
200 200
201 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); 201 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId());
@@ -205,7 +205,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI @@ -205,7 +205,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
205 } 205 }
206 206
207 protected void processTestProvisioningWithBadKeyDevice() throws Exception { 207 protected void processTestProvisioningWithBadKeyDevice() throws Exception {
208 - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); 208 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, "testProvisionKeyOrig", "testProvisionSecret", DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES);
209 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); 209 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
210 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString()); 210 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString());
211 } 211 }
@@ -180,7 +180,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM @@ -180,7 +180,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
180 return message; 180 return message;
181 } 181 }
182 182
183 - private class TestMqttCallback implements MqttCallback { 183 + protected class TestMqttCallback implements MqttCallback {
184 184
185 private final MqttAsyncClient client; 185 private final MqttAsyncClient client;
186 private final CountDownLatch latch; 186 private final CountDownLatch latch;
@@ -15,25 +15,60 @@ @@ -15,25 +15,60 @@
15 */ 15 */
16 package org.thingsboard.server.transport.mqtt.rpc; 16 package org.thingsboard.server.transport.mqtt.rpc;
17 17
  18 +import com.github.os72.protobuf.dynamic.DynamicSchema;
  19 +import com.google.protobuf.Descriptors;
  20 +import com.google.protobuf.DynamicMessage;
18 import com.google.protobuf.InvalidProtocolBufferException; 21 import com.google.protobuf.InvalidProtocolBufferException;
  22 +import com.squareup.wire.schema.internal.parser.ProtoFileElement;
19 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
  24 +import org.eclipse.californium.core.CoapHandler;
  25 +import org.eclipse.californium.core.CoapResponse;
  26 +import org.eclipse.californium.core.coap.MediaTypeRegistry;
20 import org.eclipse.paho.client.mqttv3.MqttAsyncClient; 27 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
21 import org.eclipse.paho.client.mqttv3.MqttException; 28 import org.eclipse.paho.client.mqttv3.MqttException;
22 import org.eclipse.paho.client.mqttv3.MqttMessage; 29 import org.eclipse.paho.client.mqttv3.MqttMessage;
  30 +import org.jetbrains.annotations.NotNull;
23 import org.junit.After; 31 import org.junit.After;
  32 +import org.junit.Assert;
24 import org.junit.Before; 33 import org.junit.Before;
25 import org.junit.Test; 34 import org.junit.Test;
  35 +import org.thingsboard.server.common.data.DeviceProfileProvisionType;
26 import org.thingsboard.server.common.data.TransportPayloadType; 36 import org.thingsboard.server.common.data.TransportPayloadType;
  37 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  38 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
27 import org.thingsboard.server.common.data.device.profile.MqttTopics; 39 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  40 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  41 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
28 import org.thingsboard.server.gen.transport.TransportApiProtos; 42 import org.thingsboard.server.gen.transport.TransportApiProtos;
29 -import org.thingsboard.server.gen.transport.TransportProtos; 43 +
  44 +import java.util.List;
  45 +import java.util.concurrent.CountDownLatch;
  46 +import java.util.concurrent.TimeUnit;
  47 +
  48 +import static org.junit.Assert.assertNotNull;
  49 +import static org.junit.Assert.assertTrue;
  50 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
30 51
31 @Slf4j 52 @Slf4j
32 public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { 53 public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
33 54
  55 + private static final String RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
  56 + "package rpc;\n" +
  57 + "\n" +
  58 + "message RpcRequestMsg {\n" +
  59 + " string method = 1;\n" +
  60 + " int32 requestId = 2;\n" +
  61 + " Params params = 3;\n" +
  62 + "\n" +
  63 + " message Params {\n" +
  64 + " string pin = 1;\n" +
  65 + " int32 value = 2;\n" +
  66 + " }\n" +
  67 + "}";
  68 +
34 @Before 69 @Before
35 public void beforeTest() throws Exception { 70 public void beforeTest() throws Exception {
36 - processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null); 71 + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null, null, null, null, RPC_REQUEST_PROTO_SCHEMA, null, null, DeviceProfileProvisionType.DISABLED);
37 } 72 }
38 73
39 @After 74 @After
@@ -83,14 +118,55 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst @@ -83,14 +118,55 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst
83 return builder.build(); 118 return builder.build();
84 } 119 }
85 120
  121 + protected void processTwoWayRpcTest() throws Exception {
  122 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  123 + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, 1);
  124 +
  125 + CountDownLatch latch = new CountDownLatch(1);
  126 + TestMqttCallback callback = new TestMqttCallback(client, latch);
  127 + client.setCallback(callback);
  128 +
  129 + Thread.sleep(1000);
  130 +
  131 + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
  132 + String deviceId = savedDevice.getId().getId().toString();
  133 +
  134 + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
  135 + String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
  136 + latch.await(3, TimeUnit.SECONDS);
  137 + Assert.assertEquals(expected, result);
  138 + }
  139 +
86 protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { 140 protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
87 MqttMessage message = new MqttMessage(); 141 MqttMessage message = new MqttMessage();
88 if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { 142 if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) {
89 - TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder()  
90 - .setPayload(DEVICE_RESPONSE)  
91 - .setRequestId(0) 143 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = getProtoTransportPayloadConfiguration();
  144 + ProtoFileElement rpcRequestProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(RPC_REQUEST_PROTO_SCHEMA);
  145 + DynamicSchema rpcRequestProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(rpcRequestProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_REQUEST_PROTO_SCHEMA);
  146 +
  147 + byte[] requestPayload = mqttMessage.getPayload();
  148 + DynamicMessage.Builder rpcRequestMsg = rpcRequestProtoSchema.newMessageBuilder("RpcRequestMsg");
  149 + Descriptors.Descriptor rpcRequestMsgDescriptor = rpcRequestMsg.getDescriptorForType();
  150 + assertNotNull(rpcRequestMsgDescriptor);
  151 + try {
  152 + DynamicMessage dynamicMessage = DynamicMessage.parseFrom(rpcRequestMsgDescriptor, requestPayload);
  153 + List<Descriptors.FieldDescriptor> fields = rpcRequestMsgDescriptor.getFields();
  154 + for (Descriptors.FieldDescriptor fieldDescriptor: fields) {
  155 + assertTrue(dynamicMessage.hasField(fieldDescriptor));
  156 + }
  157 + ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_RPC_RESPONSE_PROTO_SCHEMA);
  158 + DynamicSchema rpcResponseProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_RESPONSE_PROTO_SCHEMA);
  159 +
  160 + DynamicMessage.Builder rpcResponseBuilder = rpcResponseProtoSchema.newMessageBuilder("RpcResponseMsg");
  161 + Descriptors.Descriptor rpcResponseMsgDescriptor = rpcResponseBuilder.getDescriptorForType();
  162 + assertNotNull(rpcResponseMsgDescriptor);
  163 + DynamicMessage rpcResponseMsg = rpcResponseBuilder
  164 + .setField(rpcResponseMsgDescriptor.findFieldByName("payload"), DEVICE_RESPONSE)
92 .build(); 165 .build();
93 - message.setPayload(toDeviceRpcResponseMsg.toByteArray()); 166 + message.setPayload(rpcResponseMsg.toByteArray());
  167 + } catch (InvalidProtocolBufferException e) {
  168 + log.warn("Command Response Ack Error, Invalid response received: ", e);
  169 + }
94 } else { 170 } else {
95 TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload()); 171 TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload());
96 String deviceName = msg.getDeviceName(); 172 String deviceName = msg.getDeviceName();
@@ -105,6 +181,14 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst @@ -105,6 +181,14 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst
105 return message; 181 return message;
106 } 182 }
107 183
  184 + private ProtoTransportPayloadConfiguration getProtoTransportPayloadConfiguration() {
  185 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  186 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  187 + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  188 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
  189 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  190 + return (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  191 + }
108 192
109 193
110 } 194 }
@@ -119,7 +119,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac @@ -119,7 +119,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
119 " }\n" + 119 " }\n" +
120 " }\n" + 120 " }\n" +
121 "}"; 121 "}";
122 - super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, DeviceProfileProvisionType.DISABLED, null, null); 122 + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
123 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 123 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
124 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 124 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
125 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); 125 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
@@ -172,7 +172,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac @@ -172,7 +172,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
172 172
173 @Test 173 @Test
174 public void testPushMqttTelemetryGateway() throws Exception { 174 public void testPushMqttTelemetryGateway() throws Exception {
175 - super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); 175 + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
176 TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); 176 TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder();
177 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 177 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
178 String deviceName1 = "Device A"; 178 String deviceName1 = "Device A";
@@ -186,7 +186,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac @@ -186,7 +186,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
186 186
187 @Test 187 @Test
188 public void testGatewayConnect() throws Exception { 188 public void testGatewayConnect() throws Exception {
189 - super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); 189 + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
190 String deviceName = "Device A"; 190 String deviceName = "Device A";
191 TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); 191 TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
192 MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); 192 MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 -import com.fasterxml.jackson.annotation.JsonIgnore;  
19 import com.github.os72.protobuf.dynamic.DynamicSchema; 18 import com.github.os72.protobuf.dynamic.DynamicSchema;
20 import com.github.os72.protobuf.dynamic.EnumDefinition; 19 import com.github.os72.protobuf.dynamic.EnumDefinition;
21 import com.github.os72.protobuf.dynamic.MessageDefinition; 20 import com.github.os72.protobuf.dynamic.MessageDefinition;
@@ -46,9 +45,13 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC @@ -46,9 +45,13 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC
46 public static final Location LOCATION = new Location("", "", -1, -1); 45 public static final Location LOCATION = new Location("", "", -1, -1);
47 public static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; 46 public static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
48 public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; 47 public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema";
  48 + public static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema";
  49 + public static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema";
49 50
50 private String deviceTelemetryProtoSchema; 51 private String deviceTelemetryProtoSchema;
51 private String deviceAttributesProtoSchema; 52 private String deviceAttributesProtoSchema;
  53 + private String deviceRpcRequestProtoSchema;
  54 + private String deviceRpcResponseProtoSchema;
52 55
53 @Override 56 @Override
54 public TransportPayloadType getTransportPayloadType() { 57 public TransportPayloadType getTransportPayloadType() {
@@ -63,13 +66,45 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC @@ -63,13 +66,45 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC
63 return getDescriptor(deviceAttributesProtoSchema, ATTRIBUTES_PROTO_SCHEMA); 66 return getDescriptor(deviceAttributesProtoSchema, ATTRIBUTES_PROTO_SCHEMA);
64 } 67 }
65 68
  69 + public Descriptors.Descriptor getRpcResponseDynamicMessageDescriptor(String deviceRpcResponseProtoSchema) {
  70 + return getDescriptor(deviceRpcResponseProtoSchema, RPC_RESPONSE_PROTO_SCHEMA);
  71 + }
  72 +
  73 + public DynamicMessage.Builder getRpcRequestDynamicMessageBuilder(String deviceRpcRequestProtoSchema) {
  74 + return getDynamicMessageBuilder(deviceRpcRequestProtoSchema, RPC_REQUEST_PROTO_SCHEMA);
  75 + }
  76 +
  77 + public String getDeviceRpcResponseProtoSchema() {
  78 + if (!isEmptyStr(deviceRpcResponseProtoSchema)) {
  79 + return deviceRpcResponseProtoSchema;
  80 + } else {
  81 + return "syntax =\"proto3\";\n" +
  82 + "package rpc;\n" +
  83 + "\n" +
  84 + "message RpcResponseMsg {\n" +
  85 + " string payload = 1;\n" +
  86 + "}";
  87 + }
  88 + }
  89 +
  90 + public String getDeviceRpcRequestProtoSchema() {
  91 + if (!isEmptyStr(deviceRpcRequestProtoSchema)) {
  92 + return deviceRpcRequestProtoSchema;
  93 + } else {
  94 + return "syntax =\"proto3\";\n" +
  95 + "package rpc;\n" +
  96 + "\n" +
  97 + "message RpcRequestMsg {\n" +
  98 + " string method = 1;\n" +
  99 + " int32 requestId = 2;\n" +
  100 + " string params = 3;\n" +
  101 + "}";
  102 + }
  103 + }
  104 +
66 private Descriptors.Descriptor getDescriptor(String protoSchema, String schemaName) { 105 private Descriptors.Descriptor getDescriptor(String protoSchema, String schemaName) {
67 try { 106 try {
68 - ProtoFileElement protoFileElement = getTransportProtoSchema(protoSchema);  
69 - DynamicSchema dynamicSchema = getDynamicSchema(protoFileElement, schemaName);  
70 - String lastMsgName = getMessageTypes(protoFileElement.getTypes()).stream()  
71 - .map(MessageElement::getName).reduce((previous, last) -> last).get();  
72 - DynamicMessage.Builder builder = dynamicSchema.newMessageBuilder(lastMsgName); 107 + DynamicMessage.Builder builder = getDynamicMessageBuilder(protoSchema, schemaName);
73 return builder.getDescriptorForType(); 108 return builder.getDescriptorForType();
74 } catch (Exception e) { 109 } catch (Exception e) {
75 log.warn("Failed to get Message Descriptor due to {}", e.getMessage()); 110 log.warn("Failed to get Message Descriptor due to {}", e.getMessage());
@@ -77,6 +112,14 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC @@ -77,6 +112,14 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC
77 } 112 }
78 } 113 }
79 114
  115 + public DynamicMessage.Builder getDynamicMessageBuilder(String protoSchema, String schemaName) {
  116 + ProtoFileElement protoFileElement = getTransportProtoSchema(protoSchema);
  117 + DynamicSchema dynamicSchema = getDynamicSchema(protoFileElement, schemaName);
  118 + String lastMsgName = getMessageTypes(protoFileElement.getTypes()).stream()
  119 + .map(MessageElement::getName).reduce((previous, last) -> last).get();
  120 + return dynamicSchema.newMessageBuilder(lastMsgName);
  121 + }
  122 +
80 public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) { 123 public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) {
81 DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); 124 DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder();
82 schemaBuilder.setName(schemaName); 125 schemaBuilder.setName(schemaName);
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.coap; @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.coap;
17 17
18 import com.google.gson.JsonParseException; 18 import com.google.gson.JsonParseException;
19 import com.google.protobuf.Descriptors; 19 import com.google.protobuf.Descriptors;
  20 +import com.google.protobuf.DynamicMessage;
20 import lombok.Data; 21 import lombok.Data;
21 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
22 import org.eclipse.californium.core.coap.CoAP; 23 import org.eclipse.californium.core.coap.CoAP;
@@ -261,7 +262,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -261,7 +262,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
261 TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionIdMap.get(getTokenFromRequest(request)); 262 TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
262 if (currentAttrSession == null) { 263 if (currentAttrSession == null) {
263 attributeSubscriptions.add(sessionId); 264 attributeSubscriptions.add(sessionId);
264 - registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request)); 265 + registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor,
  266 + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), getTokenFromRequest(request));
265 transportService.process(sessionInfo, 267 transportService.process(sessionInfo,
266 TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange)); 268 TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange));
267 } 269 }
@@ -281,7 +283,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -281,7 +283,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
281 TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionIdMap.get(getTokenFromRequest(request)); 283 TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
282 if (currentRpcSession == null) { 284 if (currentRpcSession == null) {
283 rpcSubscriptions.add(sessionId); 285 rpcSubscriptions.add(sessionId);
284 - registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor, getTokenFromRequest(request)); 286 + registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor,
  287 + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), getTokenFromRequest(request));
285 transportService.process(sessionInfo, 288 transportService.process(sessionInfo,
286 TransportProtos.SubscribeToRPCMsg.getDefaultInstance(), 289 TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
287 new CoapNoOpCallback(exchange)); 290 new CoapNoOpCallback(exchange));
@@ -303,17 +306,19 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -303,17 +306,19 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
303 break; 306 break;
304 case TO_DEVICE_RPC_RESPONSE: 307 case TO_DEVICE_RPC_RESPONSE:
305 transportService.process(sessionInfo, 308 transportService.process(sessionInfo,
306 - coapTransportAdaptor.convertToDeviceRpcResponse(sessionId, request), 309 + coapTransportAdaptor.convertToDeviceRpcResponse(sessionId, request, transportConfigurationContainer.getRpcResponseMsgDescriptor()),
307 new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); 310 new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
308 break; 311 break;
309 case TO_SERVER_RPC_REQUEST: 312 case TO_SERVER_RPC_REQUEST:
310 - transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), timeout); 313 + transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor,
  314 + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()), timeout);
311 transportService.process(sessionInfo, 315 transportService.process(sessionInfo,
312 coapTransportAdaptor.convertToServerRpcRequest(sessionId, request), 316 coapTransportAdaptor.convertToServerRpcRequest(sessionId, request),
313 new CoapNoOpCallback(exchange)); 317 new CoapNoOpCallback(exchange));
314 break; 318 break;
315 case GET_ATTRIBUTES_REQUEST: 319 case GET_ATTRIBUTES_REQUEST:
316 - transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor), timeout); 320 + transportService.registerSyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor,
  321 + transportConfigurationContainer.getRpcRequestDynamicMessageBuilder()), timeout);
317 transportService.process(sessionInfo, 322 transportService.process(sessionInfo,
318 coapTransportAdaptor.convertToGetAttributes(sessionId, request), 323 coapTransportAdaptor.convertToGetAttributes(sessionId, request),
319 new CoapNoOpCallback(exchange)); 324 new CoapNoOpCallback(exchange));
@@ -330,14 +335,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -330,14 +335,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
330 return tokenToSessionIdMap.remove(token); 335 return tokenToSessionIdMap.remove(token);
331 } 336 }
332 337
333 - private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, String token) { 338 + private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, String token) {
334 tokenToSessionIdMap.putIfAbsent(token, sessionInfo); 339 tokenToSessionIdMap.putIfAbsent(token, sessionInfo);
335 - transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor)); 340 + transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder));
336 transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); 341 transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
337 } 342 }
338 343
339 - private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor) {  
340 - return new CoapSessionListener(exchange, coapTransportAdaptor); 344 + private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
  345 + return new CoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder);
341 } 346 }
342 347
343 private String getTokenFromRequest(Request request) { 348 private String getTokenFromRequest(Request request) {
@@ -423,10 +428,12 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -423,10 +428,12 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
423 428
424 private final CoapExchange exchange; 429 private final CoapExchange exchange;
425 private final CoapTransportAdaptor coapTransportAdaptor; 430 private final CoapTransportAdaptor coapTransportAdaptor;
  431 + private final DynamicMessage.Builder rpcRequestDynamicMessageBuilder;
426 432
427 - CoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor) { 433 + CoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
428 this.exchange = exchange; 434 this.exchange = exchange;
429 this.coapTransportAdaptor = coapTransportAdaptor; 435 this.coapTransportAdaptor = coapTransportAdaptor;
  436 + this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder;
430 } 437 }
431 438
432 @Override 439 @Override
@@ -457,7 +464,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -457,7 +464,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
457 @Override 464 @Override
458 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) { 465 public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg msg) {
459 try { 466 try {
460 - exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg)); 467 + exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg, rpcRequestDynamicMessageBuilder));
461 } catch (AdaptorException e) { 468 } catch (AdaptorException e) {
462 log.trace("Failed to reply due to error", e); 469 log.trace("Failed to reply due to error", e);
463 exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); 470 exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
@@ -545,9 +552,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -545,9 +552,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
545 (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; 552 (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
546 String deviceTelemetryProtoSchema = protoTransportPayloadConfiguration.getDeviceTelemetryProtoSchema(); 553 String deviceTelemetryProtoSchema = protoTransportPayloadConfiguration.getDeviceTelemetryProtoSchema();
547 String deviceAttributesProtoSchema = protoTransportPayloadConfiguration.getDeviceAttributesProtoSchema(); 554 String deviceAttributesProtoSchema = protoTransportPayloadConfiguration.getDeviceAttributesProtoSchema();
  555 + String deviceRpcRequestProtoSchema = protoTransportPayloadConfiguration.getDeviceRpcRequestProtoSchema();
  556 + String deviceRpcResponseProtoSchema = protoTransportPayloadConfiguration.getDeviceRpcResponseProtoSchema();
548 return new TransportConfigurationContainer(false, 557 return new TransportConfigurationContainer(false,
549 protoTransportPayloadConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema), 558 protoTransportPayloadConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema),
550 - protoTransportPayloadConfiguration.getAttributesDynamicMessageDescriptor(deviceAttributesProtoSchema)); 559 + protoTransportPayloadConfiguration.getAttributesDynamicMessageDescriptor(deviceAttributesProtoSchema),
  560 + protoTransportPayloadConfiguration.getRpcResponseDynamicMessageDescriptor(deviceRpcResponseProtoSchema),
  561 + protoTransportPayloadConfiguration.getRpcRequestDynamicMessageBuilder(deviceRpcRequestProtoSchema)
  562 + );
551 } 563 }
552 } else { 564 } else {
553 throw new AdaptorException("Invalid CoapDeviceTypeConfiguration type: " + coapDeviceTypeConfiguration.getClass().getSimpleName() + "!"); 565 throw new AdaptorException("Invalid CoapDeviceTypeConfiguration type: " + coapDeviceTypeConfiguration.getClass().getSimpleName() + "!");
@@ -567,11 +579,15 @@ public class CoapTransportResource extends AbstractCoapTransportResource { @@ -567,11 +579,15 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
567 private boolean jsonPayload; 579 private boolean jsonPayload;
568 private Descriptors.Descriptor telemetryMsgDescriptor; 580 private Descriptors.Descriptor telemetryMsgDescriptor;
569 private Descriptors.Descriptor attributesMsgDescriptor; 581 private Descriptors.Descriptor attributesMsgDescriptor;
  582 + private Descriptors.Descriptor rpcResponseMsgDescriptor;
  583 + private DynamicMessage.Builder rpcRequestDynamicMessageBuilder;
570 584
571 - public TransportConfigurationContainer(boolean jsonPayload, Descriptors.Descriptor telemetryMsgDescriptor, Descriptors.Descriptor attributesMsgDescriptor) { 585 + public TransportConfigurationContainer(boolean jsonPayload, Descriptors.Descriptor telemetryMsgDescriptor, Descriptors.Descriptor attributesMsgDescriptor, Descriptors.Descriptor rpcResponseMsgDescriptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
572 this.jsonPayload = jsonPayload; 586 this.jsonPayload = jsonPayload;
573 this.telemetryMsgDescriptor = telemetryMsgDescriptor; 587 this.telemetryMsgDescriptor = telemetryMsgDescriptor;
574 this.attributesMsgDescriptor = attributesMsgDescriptor; 588 this.attributesMsgDescriptor = attributesMsgDescriptor;
  589 + this.rpcResponseMsgDescriptor = rpcResponseMsgDescriptor;
  590 + this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder;
575 } 591 }
576 592
577 public TransportConfigurationContainer(boolean jsonPayload) { 593 public TransportConfigurationContainer(boolean jsonPayload) {
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.transport.coap.adaptors; 16 package org.thingsboard.server.transport.coap.adaptors;
17 17
18 import com.google.protobuf.Descriptors; 18 import com.google.protobuf.Descriptors;
  19 +import com.google.protobuf.DynamicMessage;
19 import org.eclipse.californium.core.coap.Request; 20 import org.eclipse.californium.core.coap.Request;
20 import org.eclipse.californium.core.coap.Response; 21 import org.eclipse.californium.core.coap.Response;
21 import org.thingsboard.server.common.transport.adaptor.AdaptorException; 22 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
@@ -32,7 +33,7 @@ public interface CoapTransportAdaptor { @@ -32,7 +33,7 @@ public interface CoapTransportAdaptor {
32 33
33 TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException; 34 TransportProtos.GetAttributeRequestMsg convertToGetAttributes(UUID sessionId, Request inbound) throws AdaptorException;
34 35
35 - TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException; 36 + TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound, Descriptors.Descriptor rpcResponseMsgDescriptor) throws AdaptorException;
36 37
37 TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException; 38 TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException;
38 39
@@ -42,7 +43,7 @@ public interface CoapTransportAdaptor { @@ -42,7 +43,7 @@ public interface CoapTransportAdaptor {
42 43
43 Response convertToPublish(boolean isConfirmable, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; 44 Response convertToPublish(boolean isConfirmable, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
44 45
45 - Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; 46 + Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg rpcRequest, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException;
46 47
47 Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException; 48 Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException;
48 49
@@ -20,6 +20,7 @@ import com.google.gson.JsonObject; @@ -20,6 +20,7 @@ import com.google.gson.JsonObject;
20 import com.google.gson.JsonParser; 20 import com.google.gson.JsonParser;
21 import com.google.gson.JsonSyntaxException; 21 import com.google.gson.JsonSyntaxException;
22 import com.google.protobuf.Descriptors; 22 import com.google.protobuf.Descriptors;
  23 +import com.google.protobuf.DynamicMessage;
23 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
24 import org.eclipse.californium.core.coap.CoAP; 25 import org.eclipse.californium.core.coap.CoAP;
25 import org.eclipse.californium.core.coap.Request; 26 import org.eclipse.californium.core.coap.Request;
@@ -64,7 +65,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -64,7 +65,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
64 } 65 }
65 66
66 @Override 67 @Override
67 - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException { 68 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound, Descriptors.Descriptor rpcResponseMsgDescriptor) throws AdaptorException {
68 Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound); 69 Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);
69 String payload = validatePayload(sessionId, inbound, false); 70 String payload = validatePayload(sessionId, inbound, false);
70 JsonObject response = new JsonParser().parse(payload).getAsJsonObject(); 71 JsonObject response = new JsonParser().parse(payload).getAsJsonObject();
@@ -95,7 +96,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -95,7 +96,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
95 } 96 }
96 97
97 @Override 98 @Override
98 - public Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException { 99 + public Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg msg, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException {
99 return getObserveNotification(isConfirmable, JsonConverter.toJson(msg, true)); 100 return getObserveNotification(isConfirmable, JsonConverter.toJson(msg, true));
100 } 101 }
101 102
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.transport.coap.adaptors; 16 package org.thingsboard.server.transport.coap.adaptors;
17 17
  18 +import com.google.gson.JsonElement;
18 import com.google.gson.JsonParser; 19 import com.google.gson.JsonParser;
19 import com.google.protobuf.Descriptors; 20 import com.google.protobuf.Descriptors;
20 import com.google.protobuf.DynamicMessage; 21 import com.google.protobuf.DynamicMessage;
@@ -63,16 +64,16 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { @@ -63,16 +64,16 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
63 } 64 }
64 65
65 @Override 66 @Override
66 - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException { 67 + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound, Descriptors.Descriptor rpcResponseMsgDescriptor) throws AdaptorException {
67 Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound); 68 Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound);
68 if (requestId.isEmpty()) { 69 if (requestId.isEmpty()) {
69 throw new AdaptorException("Request id is missing!"); 70 throw new AdaptorException("Request id is missing!");
70 } else { 71 } else {
71 try { 72 try {
72 - String payload = TransportProtos.ToDeviceRpcResponseMsg.parseFrom(inbound.getPayload()).getPayload();  
73 - return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.get())  
74 - .setPayload(payload).build();  
75 - } catch (InvalidProtocolBufferException e) { 73 + JsonElement response = new JsonParser().parse(dynamicMsgToJson(inbound.getPayload(), rpcResponseMsgDescriptor));
  74 + return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!")))
  75 + .setPayload(response.toString()).build();
  76 + } catch (Exception e) {
76 throw new AdaptorException(e); 77 throw new AdaptorException(e);
77 } 78 }
78 } 79 }
@@ -112,8 +113,8 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { @@ -112,8 +113,8 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
112 } 113 }
113 114
114 @Override 115 @Override
115 - public Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg msg) throws AdaptorException {  
116 - return getObserveNotification(isConfirmable, msg.toByteArray()); 116 + public Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg rpcRequest, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException {
  117 + return getObserveNotification(isConfirmable, ProtoConverter.convertToRpcRequest(rpcRequest, rpcRequestDynamicMessageBuilder));
117 } 118 }
118 119
119 @Override 120 @Override
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.transport.mqtt.adaptors; 16 package org.thingsboard.server.transport.mqtt.adaptors;
17 17
  18 +import com.google.gson.JsonElement;
18 import com.google.gson.JsonParser; 19 import com.google.gson.JsonParser;
19 import com.google.protobuf.Descriptors; 20 import com.google.protobuf.Descriptors;
20 import com.google.protobuf.DynamicMessage; 21 import com.google.protobuf.DynamicMessage;
@@ -64,9 +65,9 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -64,9 +65,9 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
64 public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 65 public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
65 DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx; 66 DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
66 byte[] bytes = toBytes(inbound.payload()); 67 byte[] bytes = toBytes(inbound.payload());
67 - Descriptors.Descriptor attributesDynamicMessage = getDescriptor(deviceSessionCtx.getAttributesDynamicMessageDescriptor()); 68 + Descriptors.Descriptor attributesDynamicMessageDescriptor = getDescriptor(deviceSessionCtx.getAttributesDynamicMessageDescriptor());
68 try { 69 try {
69 - return JsonConverter.convertToAttributesProto(new JsonParser().parse(dynamicMsgToJson(bytes, attributesDynamicMessage))); 70 + return JsonConverter.convertToAttributesProto(new JsonParser().parse(dynamicMsgToJson(bytes, attributesDynamicMessageDescriptor)));
70 } catch (Exception e) { 71 } catch (Exception e) {
71 throw new AdaptorException(e); 72 throw new AdaptorException(e);
72 } 73 }
@@ -86,8 +87,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -86,8 +87,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
86 public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { 87 public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
87 byte[] bytes = toBytes(inbound.payload()); 88 byte[] bytes = toBytes(inbound.payload());
88 String topicName = inbound.variableHeader().topicName(); 89 String topicName = inbound.variableHeader().topicName();
89 - int requestId = getRequestId(topicName, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX);  
90 try { 90 try {
  91 + int requestId = getRequestId(topicName, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX);
91 return ProtoConverter.convertToGetAttributeRequestMessage(bytes, requestId); 92 return ProtoConverter.convertToGetAttributeRequestMessage(bytes, requestId);
92 } catch (InvalidProtocolBufferException e) { 93 } catch (InvalidProtocolBufferException e) {
93 log.warn("Failed to decode get attributes request", e); 94 log.warn("Failed to decode get attributes request", e);
@@ -97,10 +98,15 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -97,10 +98,15 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
97 98
98 @Override 99 @Override
99 public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException { 100 public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
  101 + DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
  102 + String topicName = mqttMsg.variableHeader().topicName();
100 byte[] bytes = toBytes(mqttMsg.payload()); 103 byte[] bytes = toBytes(mqttMsg.payload());
  104 + Descriptors.Descriptor rpcResponseDynamicMessageDescriptor = getDescriptor(deviceSessionCtx.getRpcResponseDynamicMessageDescriptor());
101 try { 105 try {
102 - return TransportProtos.ToDeviceRpcResponseMsg.parseFrom(bytes);  
103 - } catch (RuntimeException | InvalidProtocolBufferException e) { 106 + int requestId = getRequestId(topicName, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC);
  107 + JsonElement response = new JsonParser().parse(dynamicMsgToJson(bytes, rpcResponseDynamicMessageDescriptor));
  108 + return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(response.toString()).build();
  109 + } catch (Exception e) {
104 log.warn("Failed to decode Rpc response", e); 110 log.warn("Failed to decode Rpc response", e);
105 throw new AdaptorException(e); 111 throw new AdaptorException(e);
106 } 112 }
@@ -145,8 +151,10 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -145,8 +151,10 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
145 151
146 152
147 @Override 153 @Override
148 - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) {  
149 - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), ProtoConverter.convertToRpcRequest(rpcRequest))); 154 + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException {
  155 + DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
  156 + DynamicMessage.Builder rpcRequestDynamicMessageBuilder = deviceSessionCtx.getRpcRequestDynamicMessageBuilder();
  157 + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), ProtoConverter.convertToRpcRequest(rpcRequest, rpcRequestDynamicMessageBuilder)));
150 } 158 }
151 159
152 @Override 160 @Override
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.transport.mqtt.session; 16 package org.thingsboard.server.transport.mqtt.session;
17 17
18 import com.google.protobuf.Descriptors; 18 import com.google.protobuf.Descriptors;
  19 +import com.google.protobuf.DynamicMessage;
19 import io.netty.channel.ChannelHandlerContext; 20 import io.netty.channel.ChannelHandlerContext;
20 import lombok.Getter; 21 import lombok.Getter;
21 import lombok.Setter; 22 import lombok.Setter;
@@ -60,6 +61,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -60,6 +61,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
60 private volatile TransportPayloadType payloadType = TransportPayloadType.JSON; 61 private volatile TransportPayloadType payloadType = TransportPayloadType.JSON;
61 private volatile Descriptors.Descriptor attributesDynamicMessageDescriptor; 62 private volatile Descriptors.Descriptor attributesDynamicMessageDescriptor;
62 private volatile Descriptors.Descriptor telemetryDynamicMessageDescriptor; 63 private volatile Descriptors.Descriptor telemetryDynamicMessageDescriptor;
  64 + private volatile Descriptors.Descriptor rpcResponseDynamicMessageDescriptor;
  65 + private volatile DynamicMessage.Builder rpcRequestDynamicMessageBuilder;
63 66
64 @Getter 67 @Getter
65 @Setter 68 @Setter
@@ -102,6 +105,14 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -102,6 +105,14 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
102 return attributesDynamicMessageDescriptor; 105 return attributesDynamicMessageDescriptor;
103 } 106 }
104 107
  108 + public Descriptors.Descriptor getRpcResponseDynamicMessageDescriptor() {
  109 + return rpcResponseDynamicMessageDescriptor;
  110 + }
  111 +
  112 + public DynamicMessage.Builder getRpcRequestDynamicMessageBuilder() {
  113 + return rpcRequestDynamicMessageBuilder;
  114 + }
  115 +
105 @Override 116 @Override
106 public void setDeviceProfile(DeviceProfile deviceProfile) { 117 public void setDeviceProfile(DeviceProfile deviceProfile) {
107 super.setDeviceProfile(deviceProfile); 118 super.setDeviceProfile(deviceProfile);
@@ -136,5 +147,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { @@ -136,5 +147,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
136 ProtoTransportPayloadConfiguration protoTransportPayloadConfig = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; 147 ProtoTransportPayloadConfiguration protoTransportPayloadConfig = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
137 telemetryDynamicMessageDescriptor = protoTransportPayloadConfig.getTelemetryDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceTelemetryProtoSchema()); 148 telemetryDynamicMessageDescriptor = protoTransportPayloadConfig.getTelemetryDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceTelemetryProtoSchema());
138 attributesDynamicMessageDescriptor = protoTransportPayloadConfig.getAttributesDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceAttributesProtoSchema()); 149 attributesDynamicMessageDescriptor = protoTransportPayloadConfig.getAttributesDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceAttributesProtoSchema());
  150 + rpcResponseDynamicMessageDescriptor = protoTransportPayloadConfig.getRpcResponseDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceRpcResponseProtoSchema());
  151 + rpcRequestDynamicMessageBuilder = protoTransportPayloadConfig.getRpcRequestDynamicMessageBuilder(protoTransportPayloadConfig.getDeviceRpcRequestProtoSchema());
139 } 152 }
140 } 153 }
@@ -31,4 +31,8 @@ public class AdaptorException extends Exception { @@ -31,4 +31,8 @@ public class AdaptorException extends Exception {
31 super(cause); 31 super(cause);
32 } 32 }
33 33
  34 + public AdaptorException(String message, Exception cause) {
  35 + super(message, cause);
  36 + }
  37 +
34 } 38 }
@@ -15,10 +15,13 @@ @@ -15,10 +15,13 @@
15 */ 15 */
16 package org.thingsboard.server.common.transport.adaptor; 16 package org.thingsboard.server.common.transport.adaptor;
17 17
  18 +import com.google.gson.Gson;
18 import com.google.gson.JsonElement; 19 import com.google.gson.JsonElement;
  20 +import com.google.gson.JsonObject;
19 import com.google.gson.JsonParser; 21 import com.google.gson.JsonParser;
20 -import com.google.gson.JsonPrimitive; 22 +import com.google.protobuf.DynamicMessage;
21 import com.google.protobuf.InvalidProtocolBufferException; 23 import com.google.protobuf.InvalidProtocolBufferException;
  24 +import com.google.protobuf.util.JsonFormat;
22 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.util.CollectionUtils; 26 import org.springframework.util.CollectionUtils;
24 import org.springframework.util.StringUtils; 27 import org.springframework.util.StringUtils;
@@ -34,6 +37,7 @@ import java.util.List; @@ -34,6 +37,7 @@ import java.util.List;
34 @Slf4j 37 @Slf4j
35 public class ProtoConverter { 38 public class ProtoConverter {
36 39
  40 + public static final Gson GSON = new Gson();
37 public static final JsonParser JSON_PARSER = new JsonParser(); 41 public static final JsonParser JSON_PARSER = new JsonParser();
38 42
39 public static TransportProtos.PostTelemetryMsg convertToTelemetryProto(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { 43 public static TransportProtos.PostTelemetryMsg convertToTelemetryProto(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException {
@@ -170,26 +174,20 @@ public class ProtoConverter { @@ -170,26 +174,20 @@ public class ProtoConverter {
170 return kvList; 174 return kvList;
171 } 175 }
172 176
173 - public static byte[] convertToRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) {  
174 - TransportProtos.ToDeviceRpcRequestMsg.Builder toDeviceRpcRequestMsgBuilder = toDeviceRpcRequestMsg.newBuilderForType();  
175 - toDeviceRpcRequestMsgBuilder.mergeFrom(toDeviceRpcRequestMsg);  
176 - toDeviceRpcRequestMsgBuilder.setParams(parseParams(toDeviceRpcRequestMsg));  
177 - TransportProtos.ToDeviceRpcRequestMsg result = toDeviceRpcRequestMsgBuilder.build();  
178 - return result.toByteArray();  
179 - }  
180 -  
181 - private static String parseParams(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) { 177 + public static byte[] convertToRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException {
  178 + rpcRequestDynamicMessageBuilder.clear();
  179 + JsonObject rpcRequestJson = new JsonObject();
  180 + rpcRequestJson.addProperty("method", toDeviceRpcRequestMsg.getMethodName());
  181 + rpcRequestJson.addProperty("requestId", toDeviceRpcRequestMsg.getRequestId());
182 String params = toDeviceRpcRequestMsg.getParams(); 182 String params = toDeviceRpcRequestMsg.getParams();
183 - JsonElement jsonElementParams = JSON_PARSER.parse(params);  
184 - if (!jsonElementParams.isJsonPrimitive()) {  
185 - return params;  
186 - } else {  
187 - JsonPrimitive primitiveParams = jsonElementParams.getAsJsonPrimitive();  
188 - if (jsonElementParams.getAsJsonPrimitive().isString()) {  
189 - return primitiveParams.getAsString();  
190 - } else {  
191 - return params;  
192 - } 183 + try {
  184 + JsonElement paramsElement = JSON_PARSER.parse(params);
  185 + rpcRequestJson.add("params", paramsElement);
  186 + JsonFormat.parser().ignoringUnknownFields().merge(GSON.toJson(rpcRequestJson), rpcRequestDynamicMessageBuilder);
  187 + DynamicMessage dynamicRpcRequest = rpcRequestDynamicMessageBuilder.build();
  188 + return dynamicRpcRequest.toByteArray();
  189 + } catch (Exception e) {
  190 + throw new AdaptorException("Failed to convert ToDeviceRpcRequestMsg to Dynamic Rpc request message due to: ", e);
193 } 191 }
194 } 192 }
195 } 193 }
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.dao.device; 16 package org.thingsboard.server.dao.device;
17 17
  18 +import com.google.protobuf.Descriptors;
  19 +import com.google.protobuf.DynamicMessage;
18 import com.squareup.wire.Syntax; 20 import com.squareup.wire.Syntax;
19 import com.squareup.wire.schema.Field; 21 import com.squareup.wire.schema.Field;
20 import com.squareup.wire.schema.Location; 22 import com.squareup.wire.schema.Location;
@@ -87,6 +89,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -87,6 +89,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
87 private static final Location LOCATION = new Location("", "", -1, -1); 89 private static final Location LOCATION = new Location("", "", -1, -1);
88 private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; 90 private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
89 private static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; 91 private static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema";
  92 + private static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema";
  93 + private static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema";
90 94
91 private static String invalidSchemaProvidedMessage(String schemaName) { 95 private static String invalidSchemaProvidedMessage(String schemaName) {
92 return "[Transport Configuration] invalid " + schemaName + " provided!"; 96 return "[Transport Configuration] invalid " + schemaName + " provided!";
@@ -357,9 +361,10 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -357,9 +361,10 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
357 if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { 361 if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
358 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; 362 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
359 if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) { 363 if (mqttTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) {
360 - ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration = 364 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration =
361 (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); 365 (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
362 - validateProtoSchemas(protoTransportPayloadTypeConfiguration); 366 + validateProtoSchemas(protoTransportPayloadConfiguration);
  367 + validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration);
363 } 368 }
364 } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { 369 } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) {
365 CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; 370 CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@@ -370,6 +375,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -370,6 +375,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
370 if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { 375 if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) {
371 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; 376 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
372 validateProtoSchemas(protoTransportPayloadConfiguration); 377 validateProtoSchemas(protoTransportPayloadConfiguration);
  378 + validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration);
373 } 379 }
374 } 380 }
375 } 381 }
@@ -417,6 +423,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -417,6 +423,8 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
417 try { 423 try {
418 validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA); 424 validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA);
419 validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA); 425 validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA);
  426 + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema(), RPC_REQUEST_PROTO_SCHEMA);
  427 + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceRpcResponseProtoSchema(), RPC_RESPONSE_PROTO_SCHEMA);
420 } catch (Exception exception) { 428 } catch (Exception exception) {
421 throw new DataValidationException(exception.getMessage()); 429 throw new DataValidationException(exception.getMessage());
422 } 430 }
@@ -539,6 +547,48 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -539,6 +547,48 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
539 547
540 }; 548 };
541 549
  550 + private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) {
  551 + DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema());
  552 + Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType();
  553 + if (rpcRequestDynamicMessageDescriptor == null) {
  554 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get rpcRequestDynamicMessageDescriptor!");
  555 + } else {
  556 + if (CollectionUtils.isEmpty(rpcRequestDynamicMessageDescriptor.getFields()) || rpcRequestDynamicMessageDescriptor.getFields().size() != 3) {
  557 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " " + rpcRequestDynamicMessageDescriptor.getName() + " message should always contains 3 fields: method, requestId and params!");
  558 + }
  559 + Descriptors.FieldDescriptor methodFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("method");
  560 + if (methodFieldDescriptor == null) {
  561 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: method!");
  562 + } else {
  563 + if (!Descriptors.FieldDescriptor.Type.STRING.equals(methodFieldDescriptor.getType())) {
  564 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid data type. Only string type is supported!");
  565 + }
  566 + if (methodFieldDescriptor.isRepeated()) {
  567 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'method' has invalid label!");
  568 + }
  569 + }
  570 + Descriptors.FieldDescriptor requestIdFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("requestId");
  571 + if (requestIdFieldDescriptor == null) {
  572 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: requestId!");
  573 + } else {
  574 + if (!Descriptors.FieldDescriptor.Type.INT32.equals(requestIdFieldDescriptor.getType())) {
  575 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid data type. Only int32 type is supported!");
  576 + }
  577 + if (requestIdFieldDescriptor.isRepeated()) {
  578 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Field 'requestId' has invalid label!");
  579 + }
  580 + }
  581 + Descriptors.FieldDescriptor paramsFieldDescriptor = rpcRequestDynamicMessageDescriptor.findFieldByName("params");
  582 + if (paramsFieldDescriptor == null) {
  583 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + " Failed to get field descriptor for field: params!");
  584 + } else {
  585 + if (paramsFieldDescriptor.isRepeated()) {
  586 + throw new DataValidationException(invalidSchemaProvidedMessage(RPC_REQUEST_PROTO_SCHEMA) + "Field 'params' has invalid label!");
  587 + }
  588 + }
  589 + }
  590 + }
  591 +
542 private PaginatedRemover<TenantId, DeviceProfile> tenantDeviceProfilesRemover = 592 private PaginatedRemover<TenantId, DeviceProfile> tenantDeviceProfilesRemover =
543 new PaginatedRemover<TenantId, DeviceProfile>() { 593 new PaginatedRemover<TenantId, DeviceProfile>() {
544 594
@@ -61,6 +61,21 @@ @@ -61,6 +61,21 @@
61 {{ 'device-profile.attributes-proto-schema-required' | translate}} 61 {{ 'device-profile.attributes-proto-schema-required' | translate}}
62 </mat-error> 62 </mat-error>
63 </mat-form-field> 63 </mat-form-field>
  64 + <mat-form-field style="padding-bottom: 20px" fxFlex>
  65 + <mat-label translate>device-profile.rpc-request-proto-schema</mat-label>
  66 + <textarea matInput required formControlName="deviceRpcRequestProtoSchema" rows="5"></textarea>
  67 + <mat-error *ngIf="coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema').hasError('required')">
  68 + {{ 'device-profile.rpc-request-proto-schema-required' | translate}}
  69 + </mat-error>
  70 + <mat-hint class="tb-hint" translate>device-profile.rpc-request-proto-schema-hint</mat-hint>
  71 + </mat-form-field>
  72 + <mat-form-field fxFlex>
  73 + <mat-label translate>device-profile.rpc-response-proto-schema</mat-label>
  74 + <textarea matInput required formControlName="deviceRpcResponseProtoSchema" rows="5"></textarea>
  75 + <mat-error *ngIf="coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceRpcResponseProtoSchema').hasError('required')">
  76 + {{ 'device-profile.rpc-response-proto-schema-required' | translate}}
  77 + </mat-error>
  78 + </mat-form-field>
64 </div> 79 </div>
65 </div> 80 </div>
66 </fieldset> 81 </fieldset>
@@ -24,6 +24,8 @@ import { @@ -24,6 +24,8 @@ import {
24 coapDeviceTypeTranslationMap, 24 coapDeviceTypeTranslationMap,
25 CoapTransportDeviceType, 25 CoapTransportDeviceType,
26 defaultAttributesSchema, 26 defaultAttributesSchema,
  27 + defaultRpcRequestSchema,
  28 + defaultRpcResponseSchema,
27 defaultTelemetrySchema, 29 defaultTelemetrySchema,
28 DeviceProfileTransportConfiguration, 30 DeviceProfileTransportConfiguration,
29 DeviceTransportType, 31 DeviceTransportType,
@@ -59,7 +61,9 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control @@ -59,7 +61,9 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control
59 private transportPayloadTypeConfiguration = this.fb.group({ 61 private transportPayloadTypeConfiguration = this.fb.group({
60 transportPayloadType: [TransportPayloadType.JSON, Validators.required], 62 transportPayloadType: [TransportPayloadType.JSON, Validators.required],
61 deviceTelemetryProtoSchema: [defaultTelemetrySchema, Validators.required], 63 deviceTelemetryProtoSchema: [defaultTelemetrySchema, Validators.required],
62 - deviceAttributesProtoSchema: [defaultAttributesSchema, Validators.required] 64 + deviceAttributesProtoSchema: [defaultAttributesSchema, Validators.required],
  65 + deviceRpcRequestProtoSchema: [defaultRpcRequestSchema, Validators.required],
  66 + deviceRpcResponseProtoSchema: [defaultRpcResponseSchema, Validators.required]
63 }); 67 });
64 68
65 get required(): boolean { 69 get required(): boolean {
@@ -88,6 +88,21 @@ @@ -88,6 +88,21 @@
88 {{ 'device-profile.attributes-proto-schema-required' | translate}} 88 {{ 'device-profile.attributes-proto-schema-required' | translate}}
89 </mat-error> 89 </mat-error>
90 </mat-form-field> 90 </mat-form-field>
  91 + <mat-form-field style="padding-bottom: 20px" fxFlex>
  92 + <mat-label translate>device-profile.rpc-request-proto-schema</mat-label>
  93 + <textarea matInput required formControlName="deviceRpcRequestProtoSchema" rows="5"></textarea>
  94 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema').hasError('required')">
  95 + {{ 'device-profile.rpc-request-proto-schema-required' | translate}}
  96 + </mat-error>
  97 + <mat-hint class="tb-hint" translate>device-profile.rpc-request-proto-schema-hint</mat-hint>
  98 + </mat-form-field>
  99 + <mat-form-field fxFlex>
  100 + <mat-label translate>device-profile.rpc-response-proto-schema</mat-label>
  101 + <textarea matInput required formControlName="deviceRpcResponseProtoSchema" rows="5"></textarea>
  102 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceRpcResponseProtoSchema').hasError('required')">
  103 + {{ 'device-profile.rpc-response-proto-schema-required' | translate}}
  104 + </mat-error>
  105 + </mat-form-field>
91 </div> 106 </div>
92 </div> 107 </div>
93 </fieldset> 108 </fieldset>
@@ -29,6 +29,8 @@ import { AppState } from '@app/core/core.state'; @@ -29,6 +29,8 @@ import { AppState } from '@app/core/core.state';
29 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 29 import { coerceBooleanProperty } from '@angular/cdk/coercion';
30 import { 30 import {
31 defaultAttributesSchema, 31 defaultAttributesSchema,
  32 + defaultRpcRequestSchema,
  33 + defaultRpcResponseSchema,
32 defaultTelemetrySchema, 34 defaultTelemetrySchema,
33 DeviceProfileTransportConfiguration, 35 DeviceProfileTransportConfiguration,
34 DeviceTransportType, 36 DeviceTransportType,
@@ -90,7 +92,9 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -90,7 +92,9 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
90 transportPayloadTypeConfiguration: this.fb.group({ 92 transportPayloadTypeConfiguration: this.fb.group({
91 transportPayloadType: [TransportPayloadType.JSON, Validators.required], 93 transportPayloadType: [TransportPayloadType.JSON, Validators.required],
92 deviceTelemetryProtoSchema: [defaultTelemetrySchema, Validators.required], 94 deviceTelemetryProtoSchema: [defaultTelemetrySchema, Validators.required],
93 - deviceAttributesProtoSchema: [defaultAttributesSchema, Validators.required] 95 + deviceAttributesProtoSchema: [defaultAttributesSchema, Validators.required],
  96 + deviceRpcRequestProtoSchema: [defaultRpcRequestSchema, Validators.required],
  97 + deviceRpcResponseProtoSchema: [defaultRpcResponseSchema, Validators.required]
94 }) 98 })
95 }, {validator: this.uniqueDeviceTopicValidator} 99 }, {validator: this.uniqueDeviceTopicValidator}
96 ); 100 );
@@ -139,15 +143,21 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control @@ -139,15 +143,21 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
139 if (forceUpdated) { 143 if (forceUpdated) {
140 transportPayloadTypeForm.patchValue({ 144 transportPayloadTypeForm.patchValue({
141 deviceTelemetryProtoSchema: defaultTelemetrySchema, 145 deviceTelemetryProtoSchema: defaultTelemetrySchema,
142 - deviceAttributesProtoSchema: defaultAttributesSchema 146 + deviceAttributesProtoSchema: defaultAttributesSchema,
  147 + deviceRpcRequestProtoSchema: defaultRpcRequestSchema,
  148 + deviceRpcResponseProtoSchema: defaultRpcResponseSchema
143 }, {emitEvent: false}); 149 }, {emitEvent: false});
144 } 150 }
145 if (type === TransportPayloadType.PROTOBUF && !this.disabled) { 151 if (type === TransportPayloadType.PROTOBUF && !this.disabled) {
146 transportPayloadTypeForm.get('deviceTelemetryProtoSchema').enable({emitEvent: false}); 152 transportPayloadTypeForm.get('deviceTelemetryProtoSchema').enable({emitEvent: false});
147 transportPayloadTypeForm.get('deviceAttributesProtoSchema').enable({emitEvent: false}); 153 transportPayloadTypeForm.get('deviceAttributesProtoSchema').enable({emitEvent: false});
  154 + transportPayloadTypeForm.get('deviceRpcRequestProtoSchema').enable({emitEvent: false});
  155 + transportPayloadTypeForm.get('deviceRpcResponseProtoSchema').enable({emitEvent: false});
148 } else { 156 } else {
149 transportPayloadTypeForm.get('deviceTelemetryProtoSchema').disable({emitEvent: false}); 157 transportPayloadTypeForm.get('deviceTelemetryProtoSchema').disable({emitEvent: false});
150 transportPayloadTypeForm.get('deviceAttributesProtoSchema').disable({emitEvent: false}); 158 transportPayloadTypeForm.get('deviceAttributesProtoSchema').disable({emitEvent: false});
  159 + transportPayloadTypeForm.get('deviceRpcRequestProtoSchema').enable({emitEvent: false});
  160 + transportPayloadTypeForm.get('deviceRpcResponseProtoSchema').disable({emitEvent: false});
151 } 161 }
152 } 162 }
153 163
@@ -140,6 +140,24 @@ export const defaultAttributesSchema = @@ -140,6 +140,24 @@ export const defaultAttributesSchema =
140 ' string serialNumber = 2;\n' + 140 ' string serialNumber = 2;\n' +
141 '}'; 141 '}';
142 142
  143 +export const defaultRpcRequestSchema =
  144 + 'syntax ="proto3";\n' +
  145 + 'package rpc;\n' +
  146 + '\n' +
  147 + 'message RpcRequestMsg {\n' +
  148 + ' string method = 1;\n' +
  149 + ' int32 requestId = 2;\n' +
  150 + ' string params = 3;\n' +
  151 + '}';
  152 +
  153 +export const defaultRpcResponseSchema =
  154 + 'syntax ="proto3";\n' +
  155 + 'package rpc;\n' +
  156 + '\n' +
  157 + 'message RpcResponseMsg {\n' +
  158 + ' string payload = 1;\n' +
  159 + '}';
  160 +
143 export const coapDeviceTypeTranslationMap = new Map<CoapTransportDeviceType, string>( 161 export const coapDeviceTypeTranslationMap = new Map<CoapTransportDeviceType, string>(
144 [ 162 [
145 [CoapTransportDeviceType.DEFAULT, 'device-profile.coap-device-type-default'], 163 [CoapTransportDeviceType.DEFAULT, 'device-profile.coap-device-type-default'],
@@ -170,6 +188,13 @@ export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportTy @@ -170,6 +188,13 @@ export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportTy
170 hasProfileConfiguration: true, 188 hasProfileConfiguration: true,
171 hasDeviceConfiguration: false, 189 hasDeviceConfiguration: false,
172 } 190 }
  191 + ],
  192 + [
  193 + DeviceTransportType.COAP,
  194 + {
  195 + hasProfileConfiguration: true,
  196 + hasDeviceConfiguration: false,
  197 + }
173 ] 198 ]
174 ] 199 ]
175 ); 200 );
@@ -270,8 +295,10 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT @@ -270,8 +295,10 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
270 break; 295 break;
271 case DeviceTransportType.COAP: 296 case DeviceTransportType.COAP:
272 const coapTransportConfiguration: CoapDeviceProfileTransportConfiguration = { 297 const coapTransportConfiguration: CoapDeviceProfileTransportConfiguration = {
273 - coapDeviceTypeConfiguration: {coapDeviceType: CoapTransportDeviceType.DEFAULT,  
274 - transportPayloadTypeConfiguration: {transportPayloadType: TransportPayloadType.JSON}} 298 + coapDeviceTypeConfiguration: {
  299 + coapDeviceType: CoapTransportDeviceType.DEFAULT,
  300 + transportPayloadTypeConfiguration: {transportPayloadType: TransportPayloadType.JSON}
  301 + }
275 }; 302 };
276 transportConfiguration = {...coapTransportConfiguration, type: DeviceTransportType.COAP}; 303 transportConfiguration = {...coapTransportConfiguration, type: DeviceTransportType.COAP};
277 break; 304 break;
@@ -1063,8 +1063,13 @@ @@ -1063,8 +1063,13 @@
1063 "telemetry-proto-schema-required": "Telemetry proto schema is required.", 1063 "telemetry-proto-schema-required": "Telemetry proto schema is required.",
1064 "attributes-proto-schema": "Attributes proto schema", 1064 "attributes-proto-schema": "Attributes proto schema",
1065 "attributes-proto-schema-required": "Attributes proto schema is required.", 1065 "attributes-proto-schema-required": "Attributes proto schema is required.",
  1066 + "rpc-response-proto-schema": "RPC response proto schema",
  1067 + "rpc-response-proto-schema-required": "RPC response proto schema is required.",
1066 "rpc-response-topic-filter": "RPC response topic filter", 1068 "rpc-response-topic-filter": "RPC response topic filter",
1067 "rpc-response-topic-filter-required": "RPC response topic filter is required.", 1069 "rpc-response-topic-filter-required": "RPC response topic filter is required.",
  1070 + "rpc-request-proto-schema": "RPC request proto schema",
  1071 + "rpc-request-proto-schema-required": "RPC request proto schema is required.",
  1072 + "rpc-request-proto-schema-hint": "RPC request message should have always fields: string method = 1; int32 requestId = 2; and params = 3 of any data type.",
1068 "not-valid-pattern-topic-filter": "Not valid pattern topic filter", 1073 "not-valid-pattern-topic-filter": "Not valid pattern topic filter",
1069 "not-valid-single-character": "Invalid use of a single-level wildcard character", 1074 "not-valid-single-character": "Invalid use of a single-level wildcard character",
1070 "not-valid-multi-character": "Invalid use of a multi-level wildcard character", 1075 "not-valid-multi-character": "Invalid use of a multi-level wildcard character",