Commit bb833ee0986d48eea7a166f74613dea09a40ca4b

Authored by Andrii Shvaika
2 parents 076f5483 4fc7668a

Merge branch 'feature/device-profile-telemetry-proto-schema' of https://github.c…

…om/ShvaykaD/thingsboard
Showing 49 changed files with 1255 additions and 237 deletions
... ... @@ -15,7 +15,6 @@
15 15 */
16 16 package org.thingsboard.server.controller;
17 17
18   -import com.datastax.oss.driver.api.core.uuid.Uuids;
19 18 import com.fasterxml.jackson.core.type.TypeReference;
20 19 import com.fasterxml.jackson.databind.JsonNode;
21 20 import com.fasterxml.jackson.databind.ObjectMapper;
... ... @@ -33,12 +32,7 @@ import org.junit.Rule;
33 32 import org.junit.rules.TestRule;
34 33 import org.junit.rules.TestWatcher;
35 34 import org.junit.runner.Description;
36   -import org.junit.runner.RunWith;
37 35 import org.springframework.beans.factory.annotation.Autowired;
38   -import org.springframework.boot.test.context.SpringBootContextLoader;
39   -import org.springframework.boot.test.context.SpringBootTest;
40   -import org.springframework.context.annotation.ComponentScan;
41   -import org.springframework.context.annotation.Configuration;
42 36 import org.springframework.http.HttpHeaders;
43 37 import org.springframework.http.MediaType;
44 38 import org.springframework.http.converter.HttpMessageConverter;
... ... @@ -46,10 +40,6 @@ import org.springframework.http.converter.StringHttpMessageConverter;
46 40 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
47 41 import org.springframework.mock.http.MockHttpInputMessage;
48 42 import org.springframework.mock.http.MockHttpOutputMessage;
49   -import org.springframework.test.annotation.DirtiesContext;
50   -import org.springframework.test.context.ActiveProfiles;
51   -import org.springframework.test.context.ContextConfiguration;
52   -import org.springframework.test.context.junit4.SpringRunner;
53 43 import org.springframework.test.web.servlet.MockMvc;
54 44 import org.springframework.test.web.servlet.MvcResult;
55 45 import org.springframework.test.web.servlet.ResultActions;
... ... @@ -58,7 +48,6 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilde
58 48 import org.springframework.util.LinkedMultiValueMap;
59 49 import org.springframework.util.MultiValueMap;
60 50 import org.springframework.web.context.WebApplicationContext;
61   -import org.thingsboard.server.common.data.BaseData;
62 51 import org.thingsboard.server.common.data.Customer;
63 52 import org.thingsboard.server.common.data.DeviceProfile;
64 53 import org.thingsboard.server.common.data.DeviceProfileType;
... ... @@ -68,11 +57,13 @@ import org.thingsboard.server.common.data.User;
68 57 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
69 58 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
70 59 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
71   -import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
  60 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  61 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  62 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  63 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  64 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
72 65 import org.thingsboard.server.common.data.id.HasId;
73   -import org.thingsboard.server.common.data.id.RuleChainId;
74 66 import org.thingsboard.server.common.data.id.TenantId;
75   -import org.thingsboard.server.common.data.id.UUIDBased;
76 67 import org.thingsboard.server.common.data.page.PageLink;
77 68 import org.thingsboard.server.common.data.page.TimePageLink;
78 69 import org.thingsboard.server.common.data.security.Authority;
... ... @@ -330,7 +321,7 @@ public abstract class AbstractWebTest {
330 321 }
331 322 }
332 323
333   - protected DeviceProfile createDeviceProfile(String name) {
  324 + protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) {
334 325 DeviceProfile deviceProfile = new DeviceProfile();
335 326 deviceProfile.setName(name);
336 327 deviceProfile.setType(DeviceProfileType.DEFAULT);
... ... @@ -338,15 +329,34 @@ public abstract class AbstractWebTest {
338 329 deviceProfile.setDescription(name + " Test");
339 330 DeviceProfileData deviceProfileData = new DeviceProfileData();
340 331 DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
341   - DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration();
342 332 deviceProfileData.setConfiguration(configuration);
343   - deviceProfileData.setTransportConfiguration(transportConfiguration);
  333 + if (deviceProfileTransportConfiguration != null) {
  334 + deviceProfileData.setTransportConfiguration(deviceProfileTransportConfiguration);
  335 + } else {
  336 + deviceProfileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration());
  337 + }
344 338 deviceProfile.setProfileData(deviceProfileData);
345 339 deviceProfile.setDefault(false);
346 340 deviceProfile.setDefaultRuleChainId(null);
347 341 return deviceProfile;
348 342 }
349 343
  344 + protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration) {
  345 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
  346 + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC);
  347 + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
  348 + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
  349 + return mqttDeviceProfileTransportConfiguration;
  350 + }
  351 +
  352 + protected ProtoTransportPayloadConfiguration createProtoTransportPayloadConfiguration(String deviceAttributesProtoSchema, String deviceTelemetryProtoSchema) {
  353 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = new ProtoTransportPayloadConfiguration();
  354 + protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(deviceAttributesProtoSchema);
  355 + protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(deviceTelemetryProtoSchema);
  356 + return protoTransportPayloadConfiguration;
  357 + }
  358 +
  359 +
350 360 protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception {
351 361 MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables);
352 362 setJwtToken(getRequest);
... ...
... ... @@ -16,6 +16,12 @@
16 16 package org.thingsboard.server.controller;
17 17
18 18 import com.fasterxml.jackson.core.type.TypeReference;
  19 +import com.github.os72.protobuf.dynamic.DynamicSchema;
  20 +import com.google.protobuf.Descriptors;
  21 +import com.google.protobuf.DynamicMessage;
  22 +import com.google.protobuf.InvalidProtocolBufferException;
  23 +import com.google.protobuf.util.JsonFormat;
  24 +import com.squareup.wire.schema.internal.parser.ProtoFileElement;
19 25 import org.junit.After;
20 26 import org.junit.Assert;
21 27 import org.junit.Before;
... ... @@ -28,7 +34,10 @@ import org.thingsboard.server.common.data.DeviceProfileType;
28 34 import org.thingsboard.server.common.data.DeviceTransportType;
29 35 import org.thingsboard.server.common.data.Tenant;
30 36 import org.thingsboard.server.common.data.User;
31   -import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
  37 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  38 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  39 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  40 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
32 41 import org.thingsboard.server.common.data.page.PageData;
33 42 import org.thingsboard.server.common.data.page.PageLink;
34 43 import org.thingsboard.server.common.data.security.Authority;
... ... @@ -36,9 +45,13 @@ import org.thingsboard.server.common.data.security.Authority;
36 45 import java.util.ArrayList;
37 46 import java.util.Collections;
38 47 import java.util.List;
  48 +import java.util.Set;
39 49 import java.util.stream.Collectors;
40 50
41 51 import static org.hamcrest.Matchers.containsString;
  52 +import static org.junit.Assert.assertEquals;
  53 +import static org.junit.Assert.assertNotNull;
  54 +import static org.junit.Assert.assertTrue;
42 55 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
43 56
44 57 public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest {
... ... @@ -78,7 +91,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
78 91
79 92 @Test
80 93 public void testSaveDeviceProfile() throws Exception {
81   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  94 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
82 95 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
83 96 Assert.assertNotNull(savedDeviceProfile);
84 97 Assert.assertNotNull(savedDeviceProfile.getId());
... ... @@ -96,7 +109,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
96 109
97 110 @Test
98 111 public void testFindDeviceProfileById() throws Exception {
99   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  112 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
100 113 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
101 114 DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
102 115 Assert.assertNotNull(foundDeviceProfile);
... ... @@ -105,7 +118,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
105 118
106 119 @Test
107 120 public void testFindDeviceProfileInfoById() throws Exception {
108   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  121 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
109 122 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
110 123 DeviceProfileInfo foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/"+savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class);
111 124 Assert.assertNotNull(foundDeviceProfileInfo);
... ... @@ -127,7 +140,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
127 140
128 141 @Test
129 142 public void testSetDefaultDeviceProfile() throws Exception {
130   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1");
  143 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1", null);
131 144 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
132 145 DeviceProfile defaultDeviceProfile = doPost("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString()+"/default", null, DeviceProfile.class);
133 146 Assert.assertNotNull(defaultDeviceProfile);
... ... @@ -147,19 +160,19 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
147 160
148 161 @Test
149 162 public void testSaveDeviceProfileWithSameName() throws Exception {
150   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  163 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
151 164 doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
152   - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile");
  165 + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile", null);
153 166 doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest())
154 167 .andExpect(statusReason(containsString("Device profile with such name already exists")));
155 168 }
156 169
157 170 @Test
158 171 public void testSaveDeviceProfileWithSameProvisionDeviceKey() throws Exception {
159   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  172 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
160 173 deviceProfile.setProvisionDeviceKey("testProvisionDeviceKey");
161 174 doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
162   - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2");
  175 + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2", null);
163 176 deviceProfile2.setProvisionDeviceKey("testProvisionDeviceKey");
164 177 doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest())
165 178 .andExpect(statusReason(containsString("Device profile with such provision device key already exists")));
... ... @@ -168,7 +181,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
168 181 @Ignore
169 182 @Test
170 183 public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception {
171   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  184 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
172 185 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
173 186 Device device = new Device();
174 187 device.setName("Test device");
... ... @@ -183,7 +196,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
183 196
184 197 @Test
185 198 public void testChangeDeviceProfileTransportTypeWithExistingDevices() throws Exception {
186   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  199 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
187 200 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
188 201 Device device = new Device();
189 202 device.setName("Test device");
... ... @@ -197,7 +210,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
197 210
198 211 @Test
199 212 public void testDeleteDeviceProfileWithExistingDevice() throws Exception {
200   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  213 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
201 214 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
202 215
203 216 Device device = new Device();
... ... @@ -214,7 +227,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
214 227
215 228 @Test
216 229 public void testDeleteDeviceProfile() throws Exception {
217   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  230 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null);
218 231 DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
219 232
220 233 doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString())
... ... @@ -235,7 +248,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
235 248 deviceProfiles.addAll(pageData.getData());
236 249
237 250 for (int i=0;i<28;i++) {
238   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
  251 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i, null);
239 252 deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class));
240 253 }
241 254
... ... @@ -280,7 +293,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
280 293 deviceProfiles.addAll(deviceProfilePageData.getData());
281 294
282 295 for (int i=0;i<28;i++) {
283   - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
  296 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i, null);
284 297 deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class));
285 298 }
286 299
... ... @@ -318,4 +331,341 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
318 331 Assert.assertEquals(1, pageData.getTotalElements());
319 332 }
320 333
  334 + @Test
  335 + public void testSaveProtoDeviceProfileWithInvalidProtoFile() throws Exception {
  336 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  337 + "\n" +
  338 + "package schemavalidation;\n" +
  339 + "\n" +
  340 + "message SchemaValidationTest {\n" +
  341 + " required int32 parameter = 1;\n" +
  342 + "}", "[Transport Configuration] failed to parse attributes proto schema due to: Syntax error in :6:4: 'required' label forbidden in proto3 field declarations");
  343 + }
  344 +
  345 + @Test
  346 + public void testSaveProtoDeviceProfileWithInvalidProtoSyntax() throws Exception {
  347 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto2\";\n" +
  348 + "\n" +
  349 + "package schemavalidation;\n" +
  350 + "\n" +
  351 + "message SchemaValidationTest {\n" +
  352 + " required int32 parameter = 1;\n" +
  353 + "}", "[Transport Configuration] invalid schema syntax: proto2 for attributes proto schema provided! Only proto3 allowed!");
  354 + }
  355 +
  356 + @Test
  357 + public void testSaveProtoDeviceProfileOptionsNotSupported() throws Exception {
  358 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  359 + "\n" +
  360 + "option java_package = \"com.test.schemavalidation\";\n" +
  361 + "option java_multiple_files = true;\n" +
  362 + "\n" +
  363 + "package schemavalidation;\n" +
  364 + "\n" +
  365 + "message SchemaValidationTest {\n" +
  366 + " int32 parameter = 1;\n" +
  367 + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema options don't support!");
  368 + }
  369 +
  370 + @Test
  371 + public void testSaveProtoDeviceProfilePublicImportsNotSupported() throws Exception {
  372 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  373 + "\n" +
  374 + "import public \"oldschema.proto\";\n" +
  375 + "\n" +
  376 + "package schemavalidation;\n" +
  377 + "\n" +
  378 + "message SchemaValidationTest {\n" +
  379 + " int32 parameter = 1;\n" +
  380 + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema public imports don't support!");
  381 + }
  382 +
  383 + @Test
  384 + public void testSaveProtoDeviceProfileImportsNotSupported() throws Exception {
  385 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  386 + "\n" +
  387 + "import \"oldschema.proto\";\n" +
  388 + "\n" +
  389 + "package schemavalidation;\n" +
  390 + "\n" +
  391 + "message SchemaValidationTest {\n" +
  392 + " int32 parameter = 1;\n" +
  393 + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema imports don't support!");
  394 + }
  395 +
  396 + @Test
  397 + public void testSaveProtoDeviceProfileExtendDeclarationsNotSupported() throws Exception {
  398 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  399 + "\n" +
  400 + "package schemavalidation;\n" +
  401 + "\n" +
  402 + "extend google.protobuf.MethodOptions {\n" +
  403 + " MyMessage my_method_option = 50007;\n" +
  404 + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema extend declarations don't support!");
  405 + }
  406 +
  407 + @Test
  408 + public void testSaveProtoDeviceProfileEnumOptionsNotSupported() throws Exception {
  409 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  410 + "\n" +
  411 + "package schemavalidation;\n" +
  412 + "\n" +
  413 + "enum testEnum {\n" +
  414 + " option allow_alias = true;\n" +
  415 + " DEFAULT = 0;\n" +
  416 + " STARTED = 1;\n" +
  417 + " RUNNING = 2;\n" +
  418 + "}\n" +
  419 + "\n" +
  420 + "message testMessage {\n" +
  421 + " int32 parameter = 1;\n" +
  422 + "}", "[Transport Configuration] invalid attributes proto schema provided! Enum definitions options are not supported!");
  423 + }
  424 +
  425 + @Test
  426 + public void testSaveProtoDeviceProfileNoOneMessageTypeExists() throws Exception {
  427 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  428 + "\n" +
  429 + "package schemavalidation;\n" +
  430 + "\n" +
  431 + "enum testEnum {\n" +
  432 + " DEFAULT = 0;\n" +
  433 + " STARTED = 1;\n" +
  434 + " RUNNING = 2;\n" +
  435 + "}", "[Transport Configuration] invalid attributes proto schema provided! At least one Message definition should exists!");
  436 + }
  437 +
  438 + @Test
  439 + public void testSaveProtoDeviceProfileMessageTypeOptionsNotSupported() throws Exception {
  440 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  441 + "\n" +
  442 + "package schemavalidation;\n" +
  443 + "\n" +
  444 + "message testMessage {\n" +
  445 + " option allow_alias = true;\n" +
  446 + " int32 parameter = 1;\n" +
  447 + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition options don't support!");
  448 + }
  449 +
  450 + @Test
  451 + public void testSaveProtoDeviceProfileMessageTypeExtensionsNotSupported() throws Exception {
  452 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  453 + "\n" +
  454 + "package schemavalidation;\n" +
  455 + "\n" +
  456 + "message TestMessage {\n" +
  457 + " extensions 100 to 199;\n" +
  458 + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition extensions don't support!");
  459 + }
  460 +
  461 + @Test
  462 + public void testSaveProtoDeviceProfileMessageTypeReservedElementsNotSupported() throws Exception {
  463 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  464 + "\n" +
  465 + "package schemavalidation;\n" +
  466 + "\n" +
  467 + "message Foo {\n" +
  468 + " reserved 2, 15, 9 to 11;\n" +
  469 + " reserved \"foo\", \"bar\";\n" +
  470 + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition reserved elements don't support!");
  471 + }
  472 +
  473 + @Test
  474 + public void testSaveProtoDeviceProfileMessageTypeGroupsElementsNotSupported() throws Exception {
  475 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  476 + "\n" +
  477 + "package schemavalidation;\n" +
  478 + "\n" +
  479 + "message TestMessage {\n" +
  480 + " repeated group Result = 1 {\n" +
  481 + " string url = 2;\n" +
  482 + " string title = 3;\n" +
  483 + " repeated string snippets = 4;\n" +
  484 + " }\n" +
  485 + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition groups don't support!");
  486 + }
  487 +
  488 + @Test
  489 + public void testSaveProtoDeviceProfileOneOfsGroupsElementsNotSupported() throws Exception {
  490 + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
  491 + "\n" +
  492 + "package schemavalidation;\n" +
  493 + "\n" +
  494 + "message SampleMessage {\n" +
  495 + " oneof test_oneof {\n" +
  496 + " string name = 1;\n" +
  497 + " group Result = 2 {\n" +
  498 + " \tstring url = 3;\n" +
  499 + " \tstring title = 4;\n" +
  500 + " \trepeated string snippets = 5;\n" +
  501 + " }\n" +
  502 + " }" +
  503 + "}", "[Transport Configuration] invalid attributes proto schema provided! OneOf definition groups don't support!");
  504 + }
  505 +
  506 + @Test
  507 + public void testSaveProtoDeviceProfileWithMessageNestedTypes() throws Exception {
  508 + String schema = "syntax = \"proto3\";\n" +
  509 + "\n" +
  510 + "package testnested;\n" +
  511 + "\n" +
  512 + "message Outer {\n" +
  513 + " message MiddleAA {\n" +
  514 + " message Inner {\n" +
  515 + " int64 ival = 1;\n" +
  516 + " bool booly = 2;\n" +
  517 + " }\n" +
  518 + " Inner inner = 1;\n" +
  519 + " }\n" +
  520 + " message MiddleBB {\n" +
  521 + " message Inner {\n" +
  522 + " int32 ival = 1;\n" +
  523 + " bool booly = 2;\n" +
  524 + " }\n" +
  525 + " Inner inner = 1;\n" +
  526 + " }\n" +
  527 + " MiddleAA middleAA = 1;\n" +
  528 + " MiddleBB middleBB = 2;\n" +
  529 + "}";
  530 + DynamicSchema dynamicSchema = getDynamicSchema(schema);
  531 + assertNotNull(dynamicSchema);
  532 + Set<String> messageTypes = dynamicSchema.getMessageTypes();
  533 + assertEquals(5, messageTypes.size());
  534 + assertTrue(messageTypes.contains("testnested.Outer"));
  535 + assertTrue(messageTypes.contains("testnested.Outer.MiddleAA"));
  536 + assertTrue(messageTypes.contains("testnested.Outer.MiddleAA.Inner"));
  537 + assertTrue(messageTypes.contains("testnested.Outer.MiddleBB"));
  538 + assertTrue(messageTypes.contains("testnested.Outer.MiddleBB.Inner"));
  539 +
  540 + DynamicMessage.Builder middleAAInnerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA.Inner");
  541 + Descriptors.Descriptor middleAAInnerMsgDescriptor = middleAAInnerMsgBuilder.getDescriptorForType();
  542 + DynamicMessage middleAAInnerMsg = middleAAInnerMsgBuilder
  543 + .setField(middleAAInnerMsgDescriptor.findFieldByName("ival"), 1L)
  544 + .setField(middleAAInnerMsgDescriptor.findFieldByName("booly"), true)
  545 + .build();
  546 +
  547 + DynamicMessage.Builder middleAAMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA");
  548 + Descriptors.Descriptor middleAAMsgDescriptor = middleAAMsgBuilder.getDescriptorForType();
  549 + DynamicMessage middleAAMsg = middleAAMsgBuilder
  550 + .setField(middleAAMsgDescriptor.findFieldByName("inner"), middleAAInnerMsg)
  551 + .build();
  552 +
  553 + DynamicMessage.Builder middleBBInnerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA.Inner");
  554 + Descriptors.Descriptor middleBBInnerMsgDescriptor = middleBBInnerMsgBuilder.getDescriptorForType();
  555 + DynamicMessage middleBBInnerMsg = middleBBInnerMsgBuilder
  556 + .setField(middleBBInnerMsgDescriptor.findFieldByName("ival"), 0L)
  557 + .setField(middleBBInnerMsgDescriptor.findFieldByName("booly"), false)
  558 + .build();
  559 +
  560 + DynamicMessage.Builder middleBBMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleBB");
  561 + Descriptors.Descriptor middleBBMsgDescriptor = middleBBMsgBuilder.getDescriptorForType();
  562 + DynamicMessage middleBBMsg = middleBBMsgBuilder
  563 + .setField(middleBBMsgDescriptor.findFieldByName("inner"), middleBBInnerMsg)
  564 + .build();
  565 +
  566 +
  567 + DynamicMessage.Builder outerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer");
  568 + Descriptors.Descriptor outerMsgBuilderDescriptor = outerMsgBuilder.getDescriptorForType();
  569 + DynamicMessage outerMsg = outerMsgBuilder
  570 + .setField(outerMsgBuilderDescriptor.findFieldByName("middleAA"), middleAAMsg)
  571 + .setField(outerMsgBuilderDescriptor.findFieldByName("middleBB"), middleBBMsg)
  572 + .build();
  573 +
  574 + assertEquals("{\n" +
  575 + " \"middleAA\": {\n" +
  576 + " \"inner\": {\n" +
  577 + " \"ival\": \"1\",\n" +
  578 + " \"booly\": true\n" +
  579 + " }\n" +
  580 + " },\n" +
  581 + " \"middleBB\": {\n" +
  582 + " \"inner\": {\n" +
  583 + " \"ival\": 0,\n" +
  584 + " \"booly\": false\n" +
  585 + " }\n" +
  586 + " }\n" +
  587 + "}", dynamicMsgToJson(outerMsgBuilderDescriptor, outerMsg.toByteArray()));
  588 + }
  589 +
  590 + @Test
  591 + public void testSaveProtoDeviceProfileWithMessageOneOfs() throws Exception {
  592 + String schema = "syntax = \"proto3\";\n" +
  593 + "\n" +
  594 + "package testoneofs;\n" +
  595 + "\n" +
  596 + "message SubMessage {\n" +
  597 + " repeated string name = 1;\n" +
  598 + "}\n" +
  599 + "\n" +
  600 + "message SampleMessage {\n" +
  601 + " oneof testOneOf {\n" +
  602 + " string name = 4;\n" +
  603 + " SubMessage subMessage = 9;\n" +
  604 + " }\n" +
  605 + "}";
  606 + DynamicSchema dynamicSchema = getDynamicSchema(schema);
  607 + assertNotNull(dynamicSchema);
  608 + Set<String> messageTypes = dynamicSchema.getMessageTypes();
  609 + assertEquals(2, messageTypes.size());
  610 + assertTrue(messageTypes.contains("testoneofs.SubMessage"));
  611 + assertTrue(messageTypes.contains("testoneofs.SampleMessage"));
  612 +
  613 + DynamicMessage.Builder sampleMsgBuilder = dynamicSchema.newMessageBuilder("testoneofs.SampleMessage");
  614 + Descriptors.Descriptor sampleMsgDescriptor = sampleMsgBuilder.getDescriptorForType();
  615 + assertNotNull(sampleMsgDescriptor);
  616 +
  617 + List<Descriptors.FieldDescriptor> fields = sampleMsgDescriptor.getFields();
  618 + assertEquals(2, fields.size());
  619 + DynamicMessage sampleMsg = sampleMsgBuilder
  620 + .setField(sampleMsgDescriptor.findFieldByName("name"), "Bob")
  621 + .build();
  622 + assertEquals("{\n" + " \"name\": \"Bob\"\n" + "}", dynamicMsgToJson(sampleMsgDescriptor, sampleMsg.toByteArray()));
  623 +
  624 + DynamicMessage.Builder subMsgBuilder = dynamicSchema.newMessageBuilder("testoneofs.SubMessage");
  625 + Descriptors.Descriptor subMsgDescriptor = subMsgBuilder.getDescriptorForType();
  626 + DynamicMessage subMsg = subMsgBuilder
  627 + .addRepeatedField(subMsgDescriptor.findFieldByName("name"), "Alice")
  628 + .addRepeatedField(subMsgDescriptor.findFieldByName("name"), "John")
  629 + .build();
  630 +
  631 + DynamicMessage sampleMsgWithOneOfSubMessage = sampleMsgBuilder.setField(sampleMsgDescriptor.findFieldByName("subMessage"), subMsg).build();
  632 + assertEquals("{\n" + " \"subMessage\": {\n" + " \"name\": [\"Alice\", \"John\"]\n" + " }\n" + "}",
  633 + dynamicMsgToJson(sampleMsgDescriptor, sampleMsgWithOneOfSubMessage.toByteArray()));
  634 + }
  635 +
  636 + private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception {
  637 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema);
  638 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration);
  639 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
  640 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  641 + DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
  642 + Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName());
  643 + return savedDeviceProfile;
  644 + }
  645 +
  646 + private void testSaveDeviceProfileWithInvalidProtoSchema(String schema, String errorMsg) throws Exception {
  647 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema);
  648 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration);
  649 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
  650 + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
  651 + .andExpect(statusReason(containsString(errorMsg)));
  652 + }
  653 +
  654 + private DynamicSchema getDynamicSchema(String schema) throws Exception {
  655 + DeviceProfile deviceProfile = testSaveDeviceProfileWithProtoPayloadType(schema);
  656 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  657 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  658 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  659 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttDeviceProfileTransportConfiguration.getTransportPayloadTypeConfiguration();
  660 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  661 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  662 + ProtoFileElement protoFile = protoTransportPayloadConfiguration.getTransportProtoSchema(schema);
  663 + return protoTransportPayloadConfiguration.getDynamicSchema(protoFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA);
  664 + }
  665 +
  666 + private String dynamicMsgToJson(Descriptors.Descriptor descriptor, byte[] payload) throws InvalidProtocolBufferException {
  667 + DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, payload);
  668 + return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage);
  669 + }
  670 +
321 671 }
... ...
... ... @@ -38,7 +38,10 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileCon
38 38 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
39 39 import org.thingsboard.server.common.data.device.profile.DeviceProfileProvisionConfiguration;
40 40 import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
  41 +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
41 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;
42 45 import org.thingsboard.server.common.data.security.Authority;
43 46 import org.thingsboard.server.common.data.security.DeviceCredentials;
44 47 import org.thingsboard.server.controller.AbstractControllerTest;
... ... @@ -47,7 +50,6 @@ import org.thingsboard.server.gen.transport.TransportProtos;
47 50 import java.util.ArrayList;
48 51 import java.util.List;
49 52 import java.util.concurrent.atomic.AtomicInteger;
50   -import java.util.function.Supplier;
51 53
52 54 import static org.junit.Assert.assertEquals;
53 55 import static org.junit.Assert.assertNotNull;
... ... @@ -60,6 +62,30 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
60 62
61 63 private static final AtomicInteger atomicInteger = new AtomicInteger(2);
62 64
  65 + protected static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
  66 + "\n" +
  67 + "package test;\n" +
  68 + " \n" +
  69 + "message PostTelemetry {\n" +
  70 + " string key1 = 1;\n" +
  71 + " bool key2 = 2;\n" +
  72 + " double key3 = 3;\n" +
  73 + " int32 key4 = 4;\n" +
  74 + " string key5 = 5;\n" +
  75 + "}";
  76 +
  77 + protected static final String DEVICE_ATTRIBUTES_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
  78 + "\n" +
  79 + "package test;\n" +
  80 + "\n" +
  81 + "message PostAttributes {\n" +
  82 + " string key1 = 1;\n" +
  83 + " bool key2 = 2;\n" +
  84 + " double key3 = 3;\n" +
  85 + " int32 key4 = 4;\n" +
  86 + " string key5 = 5;\n" +
  87 + "}";
  88 +
63 89 protected Tenant savedTenant;
64 90 protected User tenantAdmin;
65 91
... ... @@ -69,8 +95,10 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
69 95 protected Device savedGateway;
70 96 protected String gatewayAccessToken;
71 97
  98 + protected DeviceProfile deviceProfile;
  99 +
72 100 protected void processBeforeTest (String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
73   - this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, DeviceProfileProvisionType.DISABLED, null, null);
  101 + this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, null, null, DeviceProfileProvisionType.DISABLED, null, null);
74 102 }
75 103
76 104 protected void processBeforeTest(String deviceName,
... ... @@ -78,6 +106,8 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
78 106 TransportPayloadType payloadType,
79 107 String telemetryTopic,
80 108 String attributesTopic,
  109 + String telemetryProtoSchema,
  110 + String attributesProtoSchema,
81 111 DeviceProfileProvisionType provisionType,
82 112 String provisionKey, String provisionSecret
83 113 ) throws Exception {
... ... @@ -109,12 +139,12 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
109 139 gateway.setAdditionalInfo(additionalInfo);
110 140
111 141 if (payloadType != null) {
112   - DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, provisionType, provisionKey, provisionSecret);
113   - DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class);
114   - device.setType(savedDeviceProfile.getName());
115   - device.setDeviceProfileId(savedDeviceProfile.getId());
116   - gateway.setType(savedDeviceProfile.getName());
117   - gateway.setDeviceProfileId(savedDeviceProfile.getId());
  142 + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, telemetryProtoSchema, attributesProtoSchema, provisionType, provisionKey, provisionSecret);
  143 + deviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class);
  144 + device.setType(deviceProfile.getName());
  145 + device.setDeviceProfileId(deviceProfile.getId());
  146 + gateway.setType(deviceProfile.getName());
  147 + gateway.setDeviceProfileId(deviceProfile.getId());
118 148 }
119 149
120 150 savedDevice = doPost("/api/device", device, Device.class);
... ... @@ -201,9 +231,9 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
201 231
202 232 protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType,
203 233 String telemetryTopic, String attributesTopic,
  234 + String telemetryProtoSchema, String attributesProtoSchema,
204 235 DeviceProfileProvisionType provisionType,
205   - String provisionKey, String provisionSecret
206   - ) {
  236 + String provisionKey, String provisionSecret) {
207 237 DeviceProfile deviceProfile = new DeviceProfile();
208 238 deviceProfile.setName(transportPayloadType.name());
209 239 deviceProfile.setType(DeviceProfileType.DEFAULT);
... ... @@ -213,15 +243,30 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
213 243 deviceProfile.setDescription(transportPayloadType.name() + " Test");
214 244 DeviceProfileData deviceProfileData = new DeviceProfileData();
215 245 DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
216   - MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration();
217   - transportConfiguration.setTransportPayloadType(transportPayloadType);
  246 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
218 247 if (!StringUtils.isEmpty(telemetryTopic)) {
219   - transportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
  248 + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
220 249 }
221 250 if (!StringUtils.isEmpty(attributesTopic)) {
222   - transportConfiguration.setDeviceAttributesTopic(attributesTopic);
  251 + mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(attributesTopic);
223 252 }
224   - deviceProfileData.setTransportConfiguration(transportConfiguration);
  253 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration;
  254 + if (TransportPayloadType.JSON.equals(transportPayloadType)) {
  255 + transportPayloadTypeConfiguration = new JsonTransportPayloadConfiguration();
  256 + } else {
  257 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = new ProtoTransportPayloadConfiguration();
  258 + if (StringUtils.isEmpty(telemetryProtoSchema)) {
  259 + telemetryProtoSchema = DEVICE_TELEMETRY_PROTO_SCHEMA;
  260 + }
  261 + if (StringUtils.isEmpty(attributesProtoSchema)) {
  262 + attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA;
  263 + }
  264 + protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema);
  265 + protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema);
  266 + transportPayloadTypeConfiguration = protoTransportPayloadConfiguration;
  267 + }
  268 + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
  269 + deviceProfileData.setTransportConfiguration(mqttDeviceProfileTransportConfiguration);
225 270 DeviceProfileProvisionConfiguration provisionConfiguration;
226 271 switch (provisionType) {
227 272 case ALLOW_CREATE_NEW_DEVICES:
... ... @@ -233,6 +278,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
233 278 case DISABLED:
234 279 default:
235 280 provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(provisionSecret);
  281 + break;
236 282 }
237 283 deviceProfileData.setProvisionConfiguration(provisionConfiguration);
238 284 deviceProfileData.setConfiguration(configuration);
... ...
... ... @@ -18,9 +18,7 @@ package org.thingsboard.server.mqtt.attributes.request;
18 18 import com.google.protobuf.InvalidProtocolBufferException;
19 19 import io.netty.handler.codec.mqtt.MqttQoS;
20 20 import lombok.extern.slf4j.Slf4j;
21   -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
22 21 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
23   -import org.eclipse.paho.client.mqttv3.MqttCallback;
24 22 import org.eclipse.paho.client.mqttv3.MqttException;
25 23 import org.eclipse.paho.client.mqttv3.MqttMessage;
26 24 import org.junit.After;
... ... @@ -36,9 +34,7 @@ import java.util.concurrent.CountDownLatch;
36 34 import java.util.concurrent.TimeUnit;
37 35
38 36 import static org.junit.Assert.assertEquals;
39   -import static org.junit.Assert.assertFalse;
40 37 import static org.junit.Assert.assertNotNull;
41   -import static org.junit.Assert.assertTrue;
42 38 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
43 39
44 40 @Slf4j
... ...
... ... @@ -18,14 +18,9 @@ package org.thingsboard.server.mqtt.attributes.request;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.junit.After;
20 20 import org.junit.Before;
21   -import org.junit.Ignore;
22 21 import org.junit.Test;
23 22 import org.thingsboard.server.common.data.TransportPayloadType;
24 23
25   -import static org.junit.Assert.assertEquals;
26   -import static org.junit.Assert.assertNotNull;
27   -import static org.junit.Assert.assertTrue;
28   -
29 24 @Slf4j
30 25 public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
31 26
... ...
... ... @@ -15,18 +15,26 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.attributes.request;
17 17
  18 +import com.github.os72.protobuf.dynamic.DynamicSchema;
  19 +import com.google.protobuf.Descriptors;
  20 +import com.google.protobuf.DynamicMessage;
18 21 import com.google.protobuf.InvalidProtocolBufferException;
  22 +import com.squareup.wire.schema.internal.parser.ProtoFileElement;
19 23 import io.netty.handler.codec.mqtt.MqttQoS;
20 24 import lombok.extern.slf4j.Slf4j;
21 25 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
22 26 import org.eclipse.paho.client.mqttv3.MqttException;
23 27 import org.eclipse.paho.client.mqttv3.MqttMessage;
24 28 import org.junit.After;
25   -import org.junit.Before;
26 29 import org.junit.Test;
27 30 import org.thingsboard.server.common.data.Device;
  31 +import org.thingsboard.server.common.data.DeviceProfileProvisionType;
28 32 import org.thingsboard.server.common.data.TransportPayloadType;
  33 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  35 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
29 36 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  37 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
30 38 import org.thingsboard.server.gen.transport.TransportApiProtos;
31 39 import org.thingsboard.server.gen.transport.TransportProtos;
32 40
... ... @@ -38,16 +46,24 @@ import java.util.concurrent.TimeUnit;
38 46 import java.util.stream.Collectors;
39 47
40 48 import static org.junit.Assert.assertEquals;
  49 +import static org.junit.Assert.assertNotNull;
41 50 import static org.junit.Assert.assertTrue;
42 51 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
43 52
44 53 @Slf4j
45 54 public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
46 55
47   - @Before
48   - public void beforeTest() throws Exception {
49   - processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null);
50   - }
  56 + public static final String ATTRIBUTES_SCHEMA_STR = "syntax =\"proto3\";\n" +
  57 + "\n" +
  58 + "package test;\n" +
  59 + "\n" +
  60 + "message PostAttributes {\n" +
  61 + " string attribute1 = 1;\n" +
  62 + " bool attribute2 = 2;\n" +
  63 + " double attribute3 = 3;\n" +
  64 + " int32 attribute4 = 4;\n" +
  65 + " string attribute5 = 5;\n" +
  66 + "}";
51 67
52 68 @After
53 69 public void afterTest() throws Exception {
... ... @@ -56,21 +72,38 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends
56 72
57 73 @Test
58 74 public void testRequestAttributesValuesFromTheServer() throws Exception {
  75 + super.processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null, null, ATTRIBUTES_SCHEMA_STR, DeviceProfileProvisionType.DISABLED, null, null);
59 76 processTestRequestAttributesValuesFromTheServer();
60 77 }
61 78
62 79
63 80 @Test
64 81 public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
  82 + super.processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null);
65 83 processTestGatewayRequestAttributesValuesFromTheServer();
66 84 }
67 85
68 86 protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception {
69 87 doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
70   - String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
71   - List<String> expectedKeys = Arrays.asList(keys.split(","));
72   - TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys);
73   - byte[] payload = postAttributeMsg.toByteArray();
  88 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  89 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  90 + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  91 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
  92 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  93 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  94 + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(ATTRIBUTES_SCHEMA_STR);
  95 + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA);
  96 + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes");
  97 + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType();
  98 + assertNotNull(postAttributesMsgDescriptor);
  99 + DynamicMessage postAttributesMsg = postAttributesBuilder
  100 + .setField(postAttributesMsgDescriptor.findFieldByName("attribute1"), "value1")
  101 + .setField(postAttributesMsgDescriptor.findFieldByName("attribute2"), true)
  102 + .setField(postAttributesMsgDescriptor.findFieldByName("attribute3"), 42.0)
  103 + .setField(postAttributesMsgDescriptor.findFieldByName("attribute4"), 73)
  104 + .setField(postAttributesMsgDescriptor.findFieldByName("attribute5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}")
  105 + .build();
  106 + byte[] payload = postAttributesMsg.toByteArray();
74 107 client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload));
75 108 client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
76 109 }
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.attributes.request.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest;
20 19 import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.attributes.request.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest;
20 19 import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -18,11 +18,7 @@ package org.thingsboard.server.mqtt.attributes.updates;
18 18 import com.google.protobuf.InvalidProtocolBufferException;
19 19 import io.netty.handler.codec.mqtt.MqttQoS;
20 20 import lombok.extern.slf4j.Slf4j;
21   -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
22 21 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
23   -import org.eclipse.paho.client.mqttv3.MqttCallback;
24   -import org.eclipse.paho.client.mqttv3.MqttException;
25   -import org.eclipse.paho.client.mqttv3.MqttMessage;
26 22 import org.junit.After;
27 23 import org.junit.Before;
28 24 import org.junit.Test;
... ... @@ -33,12 +29,10 @@ import org.thingsboard.server.dao.util.mapping.JacksonUtil;
33 29 import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
34 30
35 31 import java.nio.charset.StandardCharsets;
36   -import java.util.concurrent.CountDownLatch;
37 32 import java.util.concurrent.TimeUnit;
38 33
39 34 import static org.junit.Assert.assertEquals;
40 35 import static org.junit.Assert.assertNotNull;
41   -import static org.junit.Assert.assertTrue;
42 36 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
43 37
44 38 @Slf4j
... ...
... ... @@ -21,11 +21,6 @@ import org.junit.Before;
21 21 import org.junit.Test;
22 22 import org.thingsboard.server.common.data.TransportPayloadType;
23 23
24   -import static org.junit.Assert.assertEquals;
25   -import static org.junit.Assert.assertFalse;
26   -import static org.junit.Assert.assertNotNull;
27   -import static org.junit.Assert.assertTrue;
28   -
29 24 @Slf4j
30 25 public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest {
31 26
... ...
... ... @@ -21,11 +21,9 @@ import org.junit.After;
21 21 import org.junit.Before;
22 22 import org.junit.Test;
23 23 import org.thingsboard.server.common.data.TransportPayloadType;
24   -import org.thingsboard.server.common.data.device.profile.MqttTopics;
25 24 import org.thingsboard.server.gen.transport.TransportApiProtos;
26 25 import org.thingsboard.server.gen.transport.TransportProtos;
27 26
28   -import java.nio.charset.StandardCharsets;
29 27 import java.util.List;
30 28 import java.util.stream.Collectors;
31 29
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.attributes.updates.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest;
20 19 import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.attributes.updates.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest;
20 19 import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -20,7 +20,6 @@ import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
20 20 import org.eclipse.paho.client.mqttv3.MqttMessage;
21 21 import org.junit.After;
22 22 import org.junit.Before;
23   -import org.junit.Ignore;
24 23 import org.junit.Test;
25 24 import org.thingsboard.server.common.data.ClaimRequest;
26 25 import org.thingsboard.server.common.data.Customer;
... ...
... ... @@ -18,7 +18,6 @@ package org.thingsboard.server.mqtt.claim;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.junit.After;
20 20 import org.junit.Before;
21   -import org.junit.Ignore;
22 21 import org.junit.Test;
23 22 import org.thingsboard.server.common.data.TransportPayloadType;
24 23
... ...
... ... @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
20 20 import org.junit.After;
21 21 import org.junit.Before;
22   -import org.junit.Ignore;
23 22 import org.junit.Test;
24 23 import org.thingsboard.server.common.data.TransportPayloadType;
25 24 import org.thingsboard.server.gen.transport.TransportApiProtos;
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.claim.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest;
20 19 import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.claim.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest;
20 19 import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -94,7 +94,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
94 94
95 95
96 96 protected void processTestProvisioningDisabledDevice() throws Exception {
97   - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.DISABLED, null, null);
  97 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null);
98 98 byte[] result = createMqttClientAndPublish().getPayloadBytes();
99 99 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
100 100 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
... ... @@ -103,7 +103,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
103 103
104 104
105 105 protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
106   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  106 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
107 107 byte[] result = createMqttClientAndPublish().getPayloadBytes();
108 108 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
109 109
... ... @@ -121,7 +121,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
121 121
122 122
123 123 protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
124   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  124 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
125 125 String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\"";
126 126 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
127 127 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
... ... @@ -142,7 +142,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
142 142
143 143
144 144 protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception {
145   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  145 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
146 146 String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\"";
147 147 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
148 148 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
... ... @@ -169,7 +169,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
169 169
170 170
171 171 protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception {
172   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  172 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
173 173 String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\"";
174 174 byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
175 175 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
... ... @@ -197,7 +197,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
197 197 }
198 198
199 199 protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
200   - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret");
  200 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret");
201 201 byte[] result = createMqttClientAndPublish().getPayloadBytes();
202 202 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
203 203 Assert.assertEquals(savedDevice.getId().toString(), response.get("deviceId").getAsString());
... ... @@ -210,7 +210,7 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn
210 210 }
211 211
212 212 protected void processTestProvisioningWithBadKeyDevice() throws Exception {
213   - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret");
  213 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret");
214 214 byte[] result = createMqttClientAndPublish().getPayloadBytes();
215 215 JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
216 216 Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
... ...
... ... @@ -102,14 +102,14 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
102 102
103 103
104 104 protected void processTestProvisioningDisabledDevice() throws Exception {
105   - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.DISABLED, null, null);
  105 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null);
106 106 ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
107 107 Assert.assertNotNull(result);
108 108 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getProvisionResponseStatus().toString());
109 109 }
110 110
111 111 protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
112   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  112 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
113 113 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
114 114
115 115 Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device");
... ... @@ -125,7 +125,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
125 125 }
126 126
127 127 protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
128   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  128 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null,null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
129 129 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build();
130 130
131 131 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes());
... ... @@ -145,7 +145,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
145 145 }
146 146
147 147 protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception {
148   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  148 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
149 149 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build();
150 150
151 151 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes());
... ... @@ -171,7 +171,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
171 171 }
172 172
173 173 protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception {
174   - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
  174 + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret");
175 175 CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateBasicMqttCredRequestMsg(
176 176 ValidateBasicMqttCredRequestMsg.newBuilder()
177 177 .setClientId("test_clientId")
... ... @@ -205,7 +205,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
205 205 }
206 206
207 207 protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
208   - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret");
  208 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret");
209 209 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
210 210 Assert.assertEquals(savedDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB()));
211 211
... ... @@ -217,7 +217,7 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI
217 217 }
218 218
219 219 protected void processTestProvisioningWithBadKeyDevice() throws Exception {
220   - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret");
  220 + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret");
221 221 ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
222 222 Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getProvisionResponseStatus().toString());
223 223 }
... ...
... ... @@ -15,49 +15,14 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.rpc;
17 17
18   -import com.fasterxml.jackson.databind.JsonNode;
19   -import com.fasterxml.jackson.databind.node.ObjectNode;
20   -import com.google.protobuf.InvalidProtocolBufferException;
21   -import com.nimbusds.jose.util.StandardCharset;
22 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
23   -import io.netty.handler.codec.mqtt.MqttQoS;
24 19 import lombok.extern.slf4j.Slf4j;
25   -import org.apache.commons.lang3.StringUtils;
26   -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
27   -import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
28   -import org.eclipse.paho.client.mqttv3.MqttCallback;
29   -import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
30   -import org.eclipse.paho.client.mqttv3.MqttException;
31   -import org.eclipse.paho.client.mqttv3.MqttMessage;
32 20 import org.junit.After;
33 21 import org.junit.Assert;
34 22 import org.junit.Before;
35   -import org.junit.Ignore;
36 23 import org.junit.Test;
37   -import org.thingsboard.server.common.data.Device;
38   -import org.thingsboard.server.common.data.DeviceProfile;
39   -import org.thingsboard.server.common.data.DeviceProfileType;
40   -import org.thingsboard.server.common.data.DeviceTransportType;
41   -import org.thingsboard.server.common.data.Tenant;
42   -import org.thingsboard.server.common.data.TransportPayloadType;
43   -import org.thingsboard.server.common.data.User;
44   -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
45   -import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
46   -import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
47   -import org.thingsboard.server.common.data.device.profile.MqttTopics;
48   -import org.thingsboard.server.common.data.security.Authority;
49   -import org.thingsboard.server.common.data.security.DeviceCredentials;
50   -import org.thingsboard.server.controller.AbstractControllerTest;
51   -import org.thingsboard.server.dao.util.mapping.JacksonUtil;
52 24 import org.thingsboard.server.service.security.AccessValidator;
53 25
54   -import java.util.Arrays;
55   -import java.util.concurrent.CountDownLatch;
56   -import java.util.concurrent.TimeUnit;
57   -import java.util.concurrent.atomic.AtomicInteger;
58   -
59   -import static org.junit.Assert.assertEquals;
60   -import static org.junit.Assert.assertNotNull;
61 26 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
62 27
63 28 /**
... ...
... ... @@ -15,9 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.rpc;
17 17
18   -import com.datastax.oss.driver.api.core.uuid.Uuids;
19 18 import com.fasterxml.jackson.databind.JsonNode;
20   -import com.fasterxml.jackson.databind.node.ObjectNode;
21 19 import com.google.protobuf.InvalidProtocolBufferException;
22 20 import com.nimbusds.jose.util.StandardCharset;
23 21 import io.netty.handler.codec.mqtt.MqttQoS;
... ... @@ -26,35 +24,18 @@ import org.apache.commons.lang3.StringUtils;
26 24 import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
27 25 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
28 26 import org.eclipse.paho.client.mqttv3.MqttCallback;
29   -import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
30 27 import org.eclipse.paho.client.mqttv3.MqttException;
31 28 import org.eclipse.paho.client.mqttv3.MqttMessage;
32   -import org.junit.After;
33 29 import org.junit.Assert;
34   -import org.junit.Before;
35   -import org.junit.Test;
36 30 import org.thingsboard.server.common.data.Device;
37   -import org.thingsboard.server.common.data.DeviceProfile;
38   -import org.thingsboard.server.common.data.DeviceProfileType;
39   -import org.thingsboard.server.common.data.DeviceTransportType;
40   -import org.thingsboard.server.common.data.Tenant;
41 31 import org.thingsboard.server.common.data.TransportPayloadType;
42   -import org.thingsboard.server.common.data.User;
43   -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
44   -import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
45   -import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
46 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
47   -import org.thingsboard.server.common.data.security.Authority;
48   -import org.thingsboard.server.common.data.security.DeviceCredentials;
49   -import org.thingsboard.server.controller.AbstractControllerTest;
50 33 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
51 34 import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
52   -import org.thingsboard.server.service.security.AccessValidator;
53 35
54 36 import java.util.Arrays;
55 37 import java.util.concurrent.CountDownLatch;
56 38 import java.util.concurrent.TimeUnit;
57   -import java.util.concurrent.atomic.AtomicInteger;
58 39
59 40 import static org.junit.Assert.assertEquals;
60 41 import static org.junit.Assert.assertNotNull;
... ...
... ... @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
20 20 import org.junit.After;
21 21 import org.junit.Before;
22   -import org.junit.Ignore;
23 22 import org.junit.Test;
24 23 import org.thingsboard.server.common.data.TransportPayloadType;
25 24
... ...
... ... @@ -22,16 +22,12 @@ import org.eclipse.paho.client.mqttv3.MqttException;
22 22 import org.eclipse.paho.client.mqttv3.MqttMessage;
23 23 import org.junit.After;
24 24 import org.junit.Before;
25   -import org.junit.Ignore;
26 25 import org.junit.Test;
27 26 import org.thingsboard.server.common.data.TransportPayloadType;
28 27 import org.thingsboard.server.common.data.device.profile.MqttTopics;
29 28 import org.thingsboard.server.gen.transport.TransportApiProtos;
30 29 import org.thingsboard.server.gen.transport.TransportProtos;
31 30
32   -import static org.junit.Assert.assertEquals;
33   -import static org.junit.Assert.assertNotNull;
34   -
35 31 @Slf4j
36 32 public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
37 33
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.telemetry.attributes;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
18 19 import lombok.extern.slf4j.Slf4j;
19 20 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
20 21 import org.junit.After;
... ... @@ -142,7 +143,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
142 143
143 144 }
144 145
145   - protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) {
  146 + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException {
146 147 for (Map<String, Object> map : deviceValues) {
147 148 String key = (String) map.get("key");
148 149 Object value = map.get("value");
... ... @@ -162,10 +163,16 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
162 163 break;
163 164 case "key5":
164 165 assertNotNull(value);
165   - assertEquals(3, ((LinkedHashMap) value).size());
166   - assertEquals(42, ((LinkedHashMap) value).get("someNumber"));
167   - assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray"));
168   - LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject");
  166 + LinkedHashMap valueMap;
  167 + if (value instanceof String) {
  168 + valueMap = mapper.readValue((String) value, LinkedHashMap.class);
  169 + } else {
  170 + valueMap = (LinkedHashMap) value;
  171 + }
  172 + assertEquals(3, valueMap.size());
  173 + assertEquals(42, valueMap.get("someNumber"));
  174 + assertEquals(Arrays.asList(1, 2, 3), valueMap.get("someArray"));
  175 + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) valueMap.get("someNestedObject");
169 176 assertEquals("value", someNestedObject.get("key"));
170 177 break;
171 178 }
... ...
... ... @@ -15,18 +15,24 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.telemetry.attributes;
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.squareup.wire.schema.internal.parser.ProtoFileElement;
18 22 import lombok.extern.slf4j.Slf4j;
19 23 import org.junit.After;
20   -import org.junit.Before;
21 24 import org.junit.Test;
22 25 import org.thingsboard.server.common.data.TransportPayloadType;
  26 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  27 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  28 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  29 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
23 30 import org.thingsboard.server.gen.transport.TransportApiProtos;
24 31 import org.thingsboard.server.gen.transport.TransportProtos;
25 32
26 33 import java.util.Arrays;
27 34 import java.util.List;
28 35
29   -import static org.junit.Assert.assertEquals;
30 36 import static org.junit.Assert.assertNotNull;
31 37 import static org.junit.Assert.assertTrue;
32 38
... ... @@ -35,11 +41,6 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
35 41
36 42 private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes";
37 43
38   - @Before
39   - public void beforeTest() throws Exception {
40   - processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
41   - }
42   -
43 44 @After
44 45 public void afterTest() throws Exception {
45 46 processAfterTest();
... ... @@ -47,13 +48,32 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
47 48
48 49 @Test
49 50 public void testPushMqttAttributes() throws Exception {
  51 + super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
50 52 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
51   - TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys);
52   - processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, msg.toByteArray());
  53 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  54 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  55 + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  56 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
  57 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  58 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  59 + ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA);
  60 + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA);
  61 + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes");
  62 + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType();
  63 + assertNotNull(postAttributesMsgDescriptor);
  64 + DynamicMessage postAttributesMsg = postAttributesBuilder
  65 + .setField(postAttributesMsgDescriptor.findFieldByName("key1"), "value1")
  66 + .setField(postAttributesMsgDescriptor.findFieldByName("key2"), true)
  67 + .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 3.0)
  68 + .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4)
  69 + .setField(postAttributesMsgDescriptor.findFieldByName("key5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}")
  70 + .build();
  71 + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, postAttributesMsg.toByteArray());
53 72 }
54 73
55 74 @Test
56 75 public void testPushMqttAttributesGateway() throws Exception {
  76 + super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null);
57 77 TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder();
58 78 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
59 79 String deviceName1 = "Device A";
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.telemetry.attributes.nosql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoNoSqlTest;
19   -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest;
20 19 import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest;
21 20
22 21 @DaoNoSqlTest
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.telemetry.attributes.nosql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoNoSqlTest;
19   -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest;
20 19 import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest;
21 20
22 21 @DaoNoSqlTest
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.telemetry.attributes.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest;
20 19 import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest;
21 20
22 21 @DaoSqlTest
... ...
... ... @@ -27,7 +27,6 @@ import org.junit.After;
27 27 import org.junit.Before;
28 28 import org.junit.Test;
29 29 import org.thingsboard.server.common.data.Device;
30   -import org.thingsboard.server.common.data.TransportPayloadType;
31 30 import org.thingsboard.server.common.data.device.profile.MqttTopics;
32 31 import org.thingsboard.server.common.data.id.DeviceId;
33 32 import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
... ...
... ... @@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics;
27 27 import java.util.Arrays;
28 28 import java.util.List;
29 29
30   -import static org.junit.Assert.assertEquals;
31 30 import static org.junit.Assert.assertNotNull;
32 31
33 32 @Slf4j
... ...
... ... @@ -15,33 +15,36 @@
15 15 */
16 16 package org.thingsboard.server.mqtt.telemetry.timeseries;
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.squareup.wire.schema.internal.parser.ProtoFileElement;
18 22 import lombok.extern.slf4j.Slf4j;
19 23 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
20 24 import org.junit.After;
21   -import org.junit.Before;
22 25 import org.junit.Test;
23 26 import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.DeviceProfileProvisionType;
24 28 import org.thingsboard.server.common.data.TransportPayloadType;
  29 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
  30 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  31 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
25 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  33 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
26 34 import org.thingsboard.server.gen.transport.TransportApiProtos;
27 35 import org.thingsboard.server.gen.transport.TransportProtos;
28 36
29 37 import java.util.Arrays;
30 38 import java.util.List;
31 39
32   -import static org.junit.Assert.assertEquals;
33 40 import static org.junit.Assert.assertNotNull;
  41 +import static org.junit.Assert.assertTrue;
34 42
35 43 @Slf4j
36 44 public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest {
37 45
38 46 private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry";
39 47
40   - @Before
41   - public void beforeTest() throws Exception {
42   - processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
43   - }
44   -
45 48 @After
46 49 public void afterTest() throws Exception {
47 50 processAfterTest();
... ... @@ -49,20 +52,85 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
49 52
50 53 @Test
51 54 public void testPushMqttTelemetry() throws Exception {
  55 + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
52 56 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
53   - TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 0);
54   - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), false);
  57 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  58 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  59 + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  60 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
  61 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  62 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  63 + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA);
  64 + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema");
  65 + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry");
  66 + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType();
  67 + assertNotNull(postTelemetryMsgDescriptor);
  68 + DynamicMessage postTelemetryMsg = postTelemetryBuilder
  69 + .setField(postTelemetryMsgDescriptor.findFieldByName("key1"), "value1")
  70 + .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), true)
  71 + .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 3.0)
  72 + .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4)
  73 + .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}")
  74 + .build();
  75 + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), false);
55 76 }
56 77
57 78 @Test
58 79 public void testPushMqttTelemetryWithTs() throws Exception {
  80 + String schemaStr = "syntax =\"proto3\";\n" +
  81 + "\n" +
  82 + "package test;\n" +
  83 + " \n" +
  84 + "message PostTelemetry {\n" +
  85 + "\n" +
  86 + " message Values {\n" +
  87 + " string key1 = 1;\n" +
  88 + " bool key2 = 2;\n" +
  89 + " double key3 = 3;\n" +
  90 + " int32 key4 = 4;\n" +
  91 + " string key5 = 5;\n" +
  92 + " }\n" +
  93 + "\n" +
  94 + " int64 ts = 1;\n" +
  95 + " Values values = 2;\n" +
  96 + "}";
  97 + 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);
59 98 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
60   - TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 10000);
61   - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), true);
  99 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  100 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  101 + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  102 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
  103 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  104 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  105 + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr);
  106 + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema");
  107 +
  108 + DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values");
  109 + Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType();
  110 + assertNotNull(valuesDescriptor);
  111 +
  112 + DynamicMessage valuesMsg = valuesBuilder
  113 + .setField(valuesDescriptor.findFieldByName("key1"), "value1")
  114 + .setField(valuesDescriptor.findFieldByName("key2"), true)
  115 + .setField(valuesDescriptor.findFieldByName("key3"), 3.0)
  116 + .setField(valuesDescriptor.findFieldByName("key4"), 4)
  117 + .setField(valuesDescriptor.findFieldByName("key5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}")
  118 + .build();
  119 +
  120 + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry");
  121 + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType();
  122 + assertNotNull(postTelemetryMsgDescriptor);
  123 + DynamicMessage postTelemetryMsg = postTelemetryBuilder
  124 + .setField(postTelemetryMsgDescriptor.findFieldByName("ts"), 10000L)
  125 + .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg)
  126 + .build();
  127 +
  128 + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), true);
62 129 }
63 130
64 131 @Test
65 132 public void testPushMqttTelemetryGateway() throws Exception {
  133 + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null);
66 134 TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder();
67 135 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
68 136 String deviceName1 = "Device A";
... ... @@ -76,6 +144,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
76 144
77 145 @Test
78 146 public void testGatewayConnect() throws Exception {
  147 + 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);
79 148 String deviceName = "Device A";
80 149 TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
81 150 MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.telemetry.timeseries.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest;
20 19 import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest;
21 20
22 21 /**
... ...
... ... @@ -16,7 +16,6 @@
16 16 package org.thingsboard.server.mqtt.telemetry.timeseries.sql;
17 17
18 18 import org.thingsboard.server.dao.service.DaoSqlTest;
19   -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest;
20 19 import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest;
21 20
22 21 /**
... ...
... ... @@ -71,6 +71,14 @@
71 71 <artifactId>java-driver-core</artifactId>
72 72 <scope>test</scope>
73 73 </dependency>
  74 + <dependency>
  75 + <groupId>com.squareup.wire</groupId>
  76 + <artifactId>wire-schema</artifactId>
  77 + </dependency>
  78 + <dependency>
  79 + <groupId>org.thingsboard</groupId>
  80 + <artifactId>protobuf-dynamic</artifactId>
  81 + </dependency>
74 82 </dependencies>
75 83
76 84 <build>
... ...
... ... @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
29 29 property = "type")
30 30 @JsonSubTypes({
31 31 @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
32   - @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
  32 + @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
33 33 @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M")})
34 34 public interface DeviceProfileTransportConfiguration {
35 35
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.TransportPayloadType;
  20 +
  21 +@Data
  22 +public class JsonTransportPayloadConfiguration implements TransportPayloadTypeConfiguration {
  23 +
  24 + @Override
  25 + public TransportPayloadType getTransportPayloadType() {
  26 + return TransportPayloadType.JSON;
  27 + }
  28 +}
... ...
... ... @@ -16,16 +16,14 @@
16 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 18 import lombok.Data;
19   -import org.thingsboard.server.common.data.TransportPayloadType;
20 19 import org.thingsboard.server.common.data.DeviceTransportType;
21 20
22 21 @Data
23 22 public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
24 23
25   - private TransportPayloadType transportPayloadType = TransportPayloadType.JSON;
26   -
27 24 private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
28 25 private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
  26 + private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration;
29 27
30 28 @Override
31 29 public DeviceTransportType getType() {
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import com.github.os72.protobuf.dynamic.DynamicSchema;
  19 +import com.github.os72.protobuf.dynamic.EnumDefinition;
  20 +import com.github.os72.protobuf.dynamic.MessageDefinition;
  21 +import com.google.protobuf.Descriptors;
  22 +import com.google.protobuf.DynamicMessage;
  23 +import com.squareup.wire.schema.Location;
  24 +import com.squareup.wire.schema.internal.parser.EnumConstantElement;
  25 +import com.squareup.wire.schema.internal.parser.EnumElement;
  26 +import com.squareup.wire.schema.internal.parser.FieldElement;
  27 +import com.squareup.wire.schema.internal.parser.MessageElement;
  28 +import com.squareup.wire.schema.internal.parser.OneOfElement;
  29 +import com.squareup.wire.schema.internal.parser.ProtoFileElement;
  30 +import com.squareup.wire.schema.internal.parser.ProtoParser;
  31 +import com.squareup.wire.schema.internal.parser.TypeElement;
  32 +import lombok.Data;
  33 +import lombok.extern.slf4j.Slf4j;
  34 +import org.thingsboard.server.common.data.TransportPayloadType;
  35 +
  36 +import java.util.ArrayList;
  37 +import java.util.Collections;
  38 +import java.util.List;
  39 +import java.util.stream.Collectors;
  40 +
  41 +@Slf4j
  42 +@Data
  43 +public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeConfiguration {
  44 +
  45 + public static final Location LOCATION = new Location("", "", -1, -1);
  46 + public static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
  47 + public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema";
  48 +
  49 + private String deviceTelemetryProtoSchema;
  50 + private String deviceAttributesProtoSchema;
  51 +
  52 + @Override
  53 + public TransportPayloadType getTransportPayloadType() {
  54 + return TransportPayloadType.PROTOBUF;
  55 + }
  56 +
  57 + public Descriptors.Descriptor getTelemetryDynamicMessageDescriptor(String deviceTelemetryProtoSchema) {
  58 + return getDescriptor(deviceTelemetryProtoSchema, TELEMETRY_PROTO_SCHEMA);
  59 + }
  60 +
  61 + public Descriptors.Descriptor getAttributesDynamicMessageDescriptor(String deviceAttributesProtoSchema) {
  62 + return getDescriptor(deviceAttributesProtoSchema, ATTRIBUTES_PROTO_SCHEMA);
  63 + }
  64 +
  65 + private Descriptors.Descriptor getDescriptor(String protoSchema, String schemaName) {
  66 + try {
  67 + ProtoFileElement protoFileElement = getTransportProtoSchema(protoSchema);
  68 + DynamicSchema dynamicSchema = getDynamicSchema(protoFileElement, schemaName);
  69 + String lastMsgName = getMessageTypes(protoFileElement.getTypes()).stream()
  70 + .map(MessageElement::getName).reduce((previous, last) -> last).get();
  71 + DynamicMessage.Builder builder = dynamicSchema.newMessageBuilder(lastMsgName);
  72 + return builder.getDescriptorForType();
  73 + } catch (Exception e) {
  74 + log.warn("Failed to get Message Descriptor due to {}", e.getMessage());
  75 + return null;
  76 + }
  77 + }
  78 +
  79 + public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) {
  80 + DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder();
  81 + schemaBuilder.setName(schemaName);
  82 + schemaBuilder.setPackage(!isEmptyStr(protoFileElement.getPackageName()) ?
  83 + protoFileElement.getPackageName() : schemaName.toLowerCase());
  84 + List<TypeElement> types = protoFileElement.getTypes();
  85 + List<MessageElement> messageTypes = getMessageTypes(types);
  86 +
  87 + if (!messageTypes.isEmpty()) {
  88 + List<EnumElement> enumTypes = getEnumElements(types);
  89 + if (!enumTypes.isEmpty()) {
  90 + enumTypes.forEach(enumElement -> {
  91 + EnumDefinition enumDefinition = getEnumDefinition(enumElement);
  92 + schemaBuilder.addEnumDefinition(enumDefinition);
  93 + });
  94 + }
  95 + List<MessageDefinition> messageDefinitions = getMessageDefinitions(messageTypes);
  96 + messageDefinitions.forEach(schemaBuilder::addMessageDefinition);
  97 + try {
  98 + return schemaBuilder.build();
  99 + } catch (Descriptors.DescriptorValidationException e) {
  100 + throw new RuntimeException("Failed to create dynamic schema due to: " + e.getMessage());
  101 + }
  102 + } else {
  103 + throw new RuntimeException("Failed to get Dynamic Schema! Message types is empty for schema:" + schemaName);
  104 + }
  105 + }
  106 +
  107 + public ProtoFileElement getTransportProtoSchema(String protoSchema) {
  108 + return new ProtoParser(LOCATION, protoSchema.toCharArray()).readProtoFile();
  109 + }
  110 +
  111 + private List<MessageElement> getMessageTypes(List<TypeElement> types) {
  112 + return types.stream()
  113 + .filter(typeElement -> typeElement instanceof MessageElement)
  114 + .map(typeElement -> (MessageElement) typeElement)
  115 + .collect(Collectors.toList());
  116 + }
  117 +
  118 + private List<EnumElement> getEnumElements(List<TypeElement> types) {
  119 + return types.stream()
  120 + .filter(typeElement -> typeElement instanceof EnumElement)
  121 + .map(typeElement -> (EnumElement) typeElement)
  122 + .collect(Collectors.toList());
  123 + }
  124 +
  125 + private List<MessageDefinition> getMessageDefinitions(List<MessageElement> messageElementsList) {
  126 + if (!messageElementsList.isEmpty()) {
  127 + List<MessageDefinition> messageDefinitions = new ArrayList<>();
  128 + messageElementsList.forEach(messageElement -> {
  129 + MessageDefinition.Builder messageDefinitionBuilder = MessageDefinition.newBuilder(messageElement.getName());
  130 + List<FieldElement> messageElementFields = messageElement.getFields();
  131 + List<OneOfElement> oneOfs = messageElement.getOneOfs();
  132 +
  133 + List<TypeElement> nestedTypes = messageElement.getNestedTypes();
  134 + if (!messageElementFields.isEmpty()) {
  135 + addMessageFieldsToTheMessageDefinition(messageElementFields, messageDefinitionBuilder);
  136 + }
  137 + if (!oneOfs.isEmpty()) {
  138 + for (OneOfElement oneOfelement: oneOfs) {
  139 + MessageDefinition.OneofBuilder oneofBuilder = messageDefinitionBuilder.addOneof(oneOfelement.getName());
  140 + addMessageFieldsToTheOneOfDefinition(oneOfelement.getFields(), oneofBuilder);
  141 + }
  142 + }
  143 + if (!nestedTypes.isEmpty()) {
  144 + List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes);
  145 + if (!nestedEnumTypes.isEmpty()) {
  146 + nestedEnumTypes.forEach(enumElement -> {
  147 + EnumDefinition nestedEnumDefinition = getEnumDefinition(enumElement);
  148 + messageDefinitionBuilder.addEnumDefinition(nestedEnumDefinition);
  149 + });
  150 + }
  151 + List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes);
  152 + List<MessageDefinition> nestedMessageDefinitions = getMessageDefinitions(nestedMessageTypes);
  153 + nestedMessageDefinitions.forEach(messageDefinitionBuilder::addMessageDefinition);
  154 + }
  155 + messageDefinitions.add(messageDefinitionBuilder.build());
  156 + });
  157 + return messageDefinitions;
  158 + } else {
  159 + return Collections.emptyList();
  160 + }
  161 + }
  162 +
  163 + private EnumDefinition getEnumDefinition(EnumElement enumElement) {
  164 + List<EnumConstantElement> enumElementTypeConstants = enumElement.getConstants();
  165 + EnumDefinition.Builder enumDefinitionBuilder = EnumDefinition.newBuilder(enumElement.getName());
  166 + if (!enumElementTypeConstants.isEmpty()) {
  167 + enumElementTypeConstants.forEach(constantElement -> enumDefinitionBuilder.addValue(constantElement.getName(), constantElement.getTag()));
  168 + }
  169 + return enumDefinitionBuilder.build();
  170 + }
  171 +
  172 +
  173 + private void addMessageFieldsToTheMessageDefinition(List<FieldElement> messageElementFields, MessageDefinition.Builder messageDefinitionBuilder) {
  174 + messageElementFields.forEach(fieldElement -> {
  175 + String labelStr = null;
  176 + if (fieldElement.getLabel() != null) {
  177 + labelStr = fieldElement.getLabel().name().toLowerCase();
  178 + }
  179 + messageDefinitionBuilder.addField(
  180 + labelStr,
  181 + fieldElement.getType(),
  182 + fieldElement.getName(),
  183 + fieldElement.getTag());
  184 + });
  185 + }
  186 +
  187 + private void addMessageFieldsToTheOneOfDefinition(List<FieldElement> oneOfsElementFields, MessageDefinition.OneofBuilder oneofBuilder) {
  188 + oneOfsElementFields.forEach(fieldElement -> oneofBuilder.addField(
  189 + fieldElement.getType(),
  190 + fieldElement.getName(),
  191 + fieldElement.getTag()));
  192 + oneofBuilder.msgDefBuilder();
  193 + }
  194 +
  195 + private boolean isEmptyStr(String str) {
  196 + return str == null || "".equals(str);
  197 + }
  198 +
  199 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.profile;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  20 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  21 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
  25 +@JsonTypeInfo(
  26 + use = JsonTypeInfo.Id.NAME,
  27 + include = JsonTypeInfo.As.PROPERTY,
  28 + property = "transportPayloadType")
  29 +@JsonSubTypes({
  30 + @JsonSubTypes.Type(value = JsonTransportPayloadConfiguration.class, name = "JSON"),
  31 + @JsonSubTypes.Type(value = ProtoTransportPayloadConfiguration.class, name = "PROTOBUF")})
  32 +public interface TransportPayloadTypeConfiguration {
  33 +
  34 + @JsonIgnore
  35 + TransportPayloadType getTransportPayloadType();
  36 +
  37 +}
... ...
... ... @@ -15,7 +15,11 @@
15 15 */
16 16 package org.thingsboard.server.transport.mqtt.adaptors;
17 17
  18 +import com.google.gson.JsonParser;
  19 +import com.google.protobuf.Descriptors;
  20 +import com.google.protobuf.DynamicMessage;
18 21 import com.google.protobuf.InvalidProtocolBufferException;
  22 +import com.google.protobuf.util.JsonFormat;
19 23 import io.netty.buffer.ByteBuf;
20 24 import io.netty.buffer.ByteBufAllocator;
21 25 import io.netty.buffer.UnpooledByteBufAllocator;
... ... @@ -29,10 +33,11 @@ import org.springframework.stereotype.Component;
29 33 import org.springframework.util.StringUtils;
30 34 import org.thingsboard.server.common.data.device.profile.MqttTopics;
31 35 import org.thingsboard.server.common.transport.adaptor.AdaptorException;
  36 +import org.thingsboard.server.common.transport.adaptor.JsonConverter;
32 37 import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
33 38 import org.thingsboard.server.gen.transport.TransportApiProtos;
34 39 import org.thingsboard.server.gen.transport.TransportProtos;
35   -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg;
  40 +import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx;
36 41 import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext;
37 42
38 43 import java.util.Optional;
... ... @@ -45,20 +50,24 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
45 50
46 51 @Override
47 52 public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  53 + DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
48 54 byte[] bytes = toBytes(inbound.payload());
  55 + Descriptors.Descriptor telemetryDynamicMsgDescriptor = getDescriptor(deviceSessionCtx.getTelemetryDynamicMsgDescriptor());
49 56 try {
50   - return ProtoConverter.convertToTelemetryProto(bytes);
51   - } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
  57 + return JsonConverter.convertToTelemetryProto(new JsonParser().parse(dynamicMsgToJson(bytes, telemetryDynamicMsgDescriptor)));
  58 + } catch (Exception e) {
52 59 throw new AdaptorException(e);
53 60 }
54 61 }
55 62
56 63 @Override
57 64 public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException {
  65 + DeviceSessionCtx deviceSessionCtx = (DeviceSessionCtx) ctx;
58 66 byte[] bytes = toBytes(inbound.payload());
  67 + Descriptors.Descriptor attributesDynamicMessage = getDescriptor(deviceSessionCtx.getAttributesDynamicMessageDescriptor());
59 68 try {
60   - return ProtoConverter.validatePostAttributeMsg(bytes);
61   - } catch (InvalidProtocolBufferException | IllegalArgumentException e) {
  69 + return JsonConverter.convertToAttributesProto(new JsonParser().parse(dynamicMsgToJson(bytes, attributesDynamicMessage)));
  70 + } catch (Exception e) {
62 71 throw new AdaptorException(e);
63 72 }
64 73 }
... ... @@ -112,7 +121,6 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
112 121 @Override
113 122 public TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException {
114 123 byte[] bytes = toBytes(mqttMsg.payload());
115   - String topicName = mqttMsg.variableHeader().topicName();
116 124 try {
117 125 return ProtoConverter.convertToProvisionRequestMsg(bytes);
118 126 } catch (InvalidProtocolBufferException ex) {
... ... @@ -207,4 +215,16 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor {
207 215 return Integer.parseInt(topicName.substring(topic.length()));
208 216 }
209 217
  218 + private Descriptors.Descriptor getDescriptor(Descriptors.Descriptor descriptor) throws AdaptorException {
  219 + if (descriptor == null) {
  220 + throw new AdaptorException("Failed to get dynamic message descriptor!");
  221 + }
  222 + return descriptor;
  223 + }
  224 +
  225 + private String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException {
  226 + DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, bytes);
  227 + return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage);
  228 + }
  229 +
210 230 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.transport.mqtt.session;
17 17
  18 +import com.google.protobuf.Descriptors;
18 19 import io.netty.channel.ChannelHandlerContext;
19 20 import lombok.Getter;
20 21 import lombok.Setter;
... ... @@ -24,6 +25,8 @@ import org.thingsboard.server.common.data.DeviceTransportType;
24 25 import org.thingsboard.server.common.data.TransportPayloadType;
25 26 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
26 27 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  28 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  29 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
27 30 import org.thingsboard.server.transport.mqtt.MqttTransportContext;
28 31 import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
29 32 import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter;
... ... @@ -54,6 +57,8 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
54 57 private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
55 58 private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
56 59 private volatile TransportPayloadType payloadType = TransportPayloadType.JSON;
  60 + private volatile Descriptors.Descriptor attributesDynamicMessageDescriptor;
  61 + private volatile Descriptors.Descriptor telemetryDynamicMessageDescriptor;
57 62
58 63 @Getter
59 64 @Setter
... ... @@ -72,7 +77,9 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
72 77 return msgIdSeq.incrementAndGet();
73 78 }
74 79
75   - public boolean isDeviceTelemetryTopic(String topicName) { return telemetryTopicFilter.filter(topicName); }
  80 + public boolean isDeviceTelemetryTopic(String topicName) {
  81 + return telemetryTopicFilter.filter(topicName);
  82 + }
76 83
77 84 public boolean isDeviceAttributesTopic(String topicName) {
78 85 return attributesTopicFilter.filter(topicName);
... ... @@ -86,6 +93,14 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
86 93 return payloadType.equals(TransportPayloadType.JSON);
87 94 }
88 95
  96 + public Descriptors.Descriptor getTelemetryDynamicMsgDescriptor() {
  97 + return telemetryDynamicMessageDescriptor;
  98 + }
  99 +
  100 + public Descriptors.Descriptor getAttributesDynamicMessageDescriptor() {
  101 + return attributesDynamicMessageDescriptor;
  102 + }
  103 +
89 104 @Override
90 105 public void setDeviceProfile(DeviceProfile deviceProfile) {
91 106 super.setDeviceProfile(deviceProfile);
... ... @@ -104,13 +119,22 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
104 119 if (transportConfiguration.getType().equals(DeviceTransportType.MQTT) &&
105 120 transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
106 121 MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
107   - payloadType = mqttConfig.getTransportPayloadType();
  122 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttConfig.getTransportPayloadTypeConfiguration();
  123 + payloadType = transportPayloadTypeConfiguration.getTransportPayloadType();
108 124 telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic());
109 125 attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic());
  126 + if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) {
  127 + updateDynamicMessageDescriptors(transportPayloadTypeConfiguration);
  128 + }
110 129 } else {
111 130 telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
112 131 attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter();
113 132 }
114 133 }
115 134
  135 + private void updateDynamicMessageDescriptors(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration) {
  136 + ProtoTransportPayloadConfiguration protoTransportPayloadConfig = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  137 + telemetryDynamicMessageDescriptor = protoTransportPayloadConfig.getTelemetryDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceTelemetryProtoSchema());
  138 + attributesDynamicMessageDescriptor = protoTransportPayloadConfig.getAttributesDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceAttributesProtoSchema());
  139 + }
116 140 }
... ...
... ... @@ -19,6 +19,7 @@ import com.google.gson.Gson;
19 19 import com.google.gson.JsonArray;
20 20 import com.google.gson.JsonElement;
21 21 import com.google.gson.JsonObject;
  22 +import com.google.gson.JsonParseException;
22 23 import com.google.gson.JsonParser;
23 24 import com.google.gson.JsonPrimitive;
24 25 import com.google.gson.JsonSyntaxException;
... ... @@ -193,40 +194,62 @@ public class JsonConverter {
193 194 String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength);
194 195 throw new JsonSyntaxException(message);
195 196 }
196   - if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) {
197   - try {
198   - result.add(buildNumericKeyValueProto(value, valueEntry.getKey()));
199   - } catch (RuntimeException th) {
200   - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)
201   - .setStringV(value.getAsString()).build());
  197 + if (isTypeCastEnabled) {
  198 + if (NumberUtils.isParsable(value.getAsString())) {
  199 + try {
  200 + result.add(buildNumericKeyValueProto(value, valueEntry.getKey()));
  201 + } catch (RuntimeException th) {
  202 + result.add(buildStringKVProto(valueEntry, value));
  203 + }
  204 + } else {
  205 + try {
  206 + JsonElement jsonElement = JSON_PARSER.parse(value.getAsString());
  207 + if (jsonElement.isJsonObject() || jsonElement.isJsonArray()) {
  208 + result.add(buildJsonKVProto(valueEntry, jsonElement));
  209 + } else {
  210 + result.add(buildStringKVProto(valueEntry, value));
  211 + }
  212 + } catch (JsonParseException e) {
  213 + result.add(buildStringKVProto(valueEntry, value));
  214 + }
202 215 }
203 216 } else {
204   - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V)
205   - .setStringV(value.getAsString()).build());
  217 + result.add(buildStringKVProto(valueEntry, value));
206 218 }
207 219 } else if (value.isBoolean()) {
208 220 result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V)
209 221 .setBoolV(value.getAsBoolean()).build());
210 222 } else if (value.isNumber()) {
211 223 result.add(buildNumericKeyValueProto(value, valueEntry.getKey()));
212   - } else if (!value.isJsonNull()) {
  224 + } else {
213 225 throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value);
214 226 }
215 227 } else if (element.isJsonObject() || element.isJsonArray()) {
216   - result.add(KeyValueProto
217   - .newBuilder()
218   - .setKey(valueEntry
219   - .getKey())
220   - .setType(KeyValueType.JSON_V)
221   - .setJsonV(element.toString())
222   - .build());
223   - } else if (!element.isJsonNull()) {
  228 + result.add(buildJsonKVProto(valueEntry, element));
  229 + } else {
224 230 throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element);
225 231 }
226 232 }
227 233 return result;
228 234 }
229 235
  236 + private static KeyValueProto buildStringKVProto(Entry<String, JsonElement> valueEntry, JsonPrimitive value) {
  237 + return KeyValueProto.newBuilder()
  238 + .setKey(valueEntry.getKey())
  239 + .setType(KeyValueType.STRING_V)
  240 + .setStringV(value.getAsString())
  241 + .build();
  242 + }
  243 +
  244 + private static KeyValueProto buildJsonKVProto(Entry<String, JsonElement> valueEntry, JsonElement jsonElement) {
  245 + return KeyValueProto
  246 + .newBuilder()
  247 + .setKey(valueEntry.getKey())
  248 + .setType(KeyValueType.JSON_V)
  249 + .setJsonV(jsonElement.toString())
  250 + .build();
  251 + }
  252 +
230 253 private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) {
231 254 if (value.getAsString().contains(".")) {
232 255 return KeyValueProto.newBuilder()
... ...
... ... @@ -15,6 +15,16 @@
15 15 */
16 16 package org.thingsboard.server.dao.device;
17 17
  18 +import com.squareup.wire.Syntax;
  19 +import com.squareup.wire.schema.Field;
  20 +import com.squareup.wire.schema.Location;
  21 +import com.squareup.wire.schema.internal.parser.EnumElement;
  22 +import com.squareup.wire.schema.internal.parser.FieldElement;
  23 +import com.squareup.wire.schema.internal.parser.MessageElement;
  24 +import com.squareup.wire.schema.internal.parser.OneOfElement;
  25 +import com.squareup.wire.schema.internal.parser.ProtoFileElement;
  26 +import com.squareup.wire.schema.internal.parser.ProtoParser;
  27 +import com.squareup.wire.schema.internal.parser.TypeElement;
18 28 import lombok.extern.slf4j.Slf4j;
19 29 import org.apache.commons.lang3.StringUtils;
20 30 import org.hibernate.exception.ConstraintViolationException;
... ... @@ -29,12 +39,14 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
29 39 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
30 40 import org.thingsboard.server.common.data.DeviceProfileType;
31 41 import org.thingsboard.server.common.data.DeviceTransportType;
32   -import org.thingsboard.server.common.data.EntitySubtype;
33 42 import org.thingsboard.server.common.data.Tenant;
34 43 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
35 44 import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
36 45 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  46 +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
37 47 import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
  48 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  49 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
38 50 import org.thingsboard.server.common.data.id.DeviceProfileId;
39 51 import org.thingsboard.server.common.data.id.TenantId;
40 52 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -49,6 +61,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
49 61 import java.util.Arrays;
50 62 import java.util.Collections;
51 63 import java.util.List;
  64 +import java.util.stream.Collectors;
52 65
53 66 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE;
54 67 import static org.thingsboard.server.dao.service.Validator.validateId;
... ... @@ -61,6 +74,14 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
61 74 private static final String INCORRECT_DEVICE_PROFILE_ID = "Incorrect deviceProfileId ";
62 75 private static final String INCORRECT_DEVICE_PROFILE_NAME = "Incorrect deviceProfileName ";
63 76
  77 + private static final Location LOCATION = new Location("", "", -1, -1);
  78 + private static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
  79 + private static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema";
  80 +
  81 + private static String invalidSchemaProvidedMessage(String schemaName) {
  82 + return "[Transport Configuration] invalid " + schemaName + " provided!";
  83 + }
  84 +
64 85 @Autowired
65 86 private DeviceProfileDao deviceProfileDao;
66 87
... ... @@ -310,6 +331,20 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
310 331 if (defaultDeviceProfile != null && !defaultDeviceProfile.getId().equals(deviceProfile.getId())) {
311 332 throw new DataValidationException("Another default device profile is present in scope of current tenant!");
312 333 }
  334 + } else {
  335 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  336 + if (transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
  337 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  338 + if (mqttDeviceProfileTransportConfiguration.getTransportPayloadTypeConfiguration() instanceof ProtoTransportPayloadConfiguration) {
  339 + ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration = (ProtoTransportPayloadConfiguration) mqttDeviceProfileTransportConfiguration.getTransportPayloadTypeConfiguration();
  340 + try {
  341 + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceAttributesProtoSchema(), ATTRIBUTES_PROTO_SCHEMA);
  342 + validateTransportProtoSchema(protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(), TELEMETRY_PROTO_SCHEMA);
  343 + } catch (Exception exception) {
  344 + throw new DataValidationException(exception.getMessage());
  345 + }
  346 + }
  347 + }
313 348 }
314 349 }
315 350
... ... @@ -334,6 +369,121 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
334 369 }
335 370 }
336 371 }
  372 +
  373 + private void validateTransportProtoSchema(String schema, String schemaName) throws IllegalArgumentException {
  374 + ProtoParser schemaParser = new ProtoParser(LOCATION, schema.toCharArray());
  375 + ProtoFileElement protoFileElement;
  376 + try {
  377 + protoFileElement = schemaParser.readProtoFile();
  378 + } catch (Exception e) {
  379 + throw new IllegalArgumentException("[Transport Configuration] failed to parse " + schemaName + " due to: " + e.getMessage());
  380 + }
  381 + checkProtoFileSyntax(schemaName, protoFileElement);
  382 + checkProtoFileCommonSettings(schemaName, protoFileElement.getOptions().isEmpty(), " Schema options don't support!");
  383 + checkProtoFileCommonSettings(schemaName, protoFileElement.getPublicImports().isEmpty(), " Schema public imports don't support!");
  384 + checkProtoFileCommonSettings(schemaName, protoFileElement.getImports().isEmpty(), " Schema imports don't support!");
  385 + checkProtoFileCommonSettings(schemaName, protoFileElement.getExtendDeclarations().isEmpty(), " Schema extend declarations don't support!");
  386 + checkTypeElements(schemaName, protoFileElement);
  387 + }
  388 +
  389 + private void checkProtoFileSyntax(String schemaName, ProtoFileElement protoFileElement) {
  390 + if (protoFileElement.getSyntax() == null || !protoFileElement.getSyntax().equals(Syntax.PROTO_3)) {
  391 + throw new IllegalArgumentException("[Transport Configuration] invalid schema syntax: " + protoFileElement.getSyntax() +
  392 + " for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!");
  393 + }
  394 + }
  395 + private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) {
  396 + if (!isEmptySettings) {
  397 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage);
  398 + }
  399 + }
  400 +
  401 + private void checkTypeElements(String schemaName, ProtoFileElement protoFileElement) {
  402 + List<TypeElement> types = protoFileElement.getTypes();
  403 + if (!types.isEmpty()) {
  404 + if (types.stream().noneMatch(typeElement -> typeElement instanceof MessageElement)) {
  405 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " At least one Message definition should exists!");
  406 + } else {
  407 + checkEnumElements(schemaName, getEnumElements(types));
  408 + checkMessageElements(schemaName, getMessageTypes(types));
  409 + }
  410 + } else {
  411 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Type elements is empty!");
  412 + }
  413 + }
  414 +
  415 + private void checkFieldElements(String schemaName, List<FieldElement> fieldElements) {
  416 + if (!fieldElements.isEmpty()) {
  417 + boolean hasRequiredLabel = fieldElements.stream().anyMatch(fieldElement -> {
  418 + Field.Label label = fieldElement.getLabel();
  419 + return label != null && label.equals(Field.Label.REQUIRED);
  420 + });
  421 + if (hasRequiredLabel) {
  422 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Required labels are not supported!");
  423 + }
  424 + boolean hasDefaultValue = fieldElements.stream().anyMatch(fieldElement -> fieldElement.getDefaultValue() != null);
  425 + if (hasDefaultValue) {
  426 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Default values are not supported!");
  427 + }
  428 + }
  429 + }
  430 +
  431 + private void checkEnumElements(String schemaName, List<EnumElement> enumTypes) {
  432 + if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getNestedTypes().isEmpty())) {
  433 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Nested types in Enum definitions are not supported!");
  434 + }
  435 + if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getOptions().isEmpty())) {
  436 + throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Enum definitions options are not supported!");
  437 + }
  438 + }
  439 +
  440 + private void checkMessageElements(String schemaName, List<MessageElement> messageElementsList) {
  441 + if (!messageElementsList.isEmpty()) {
  442 + messageElementsList.forEach(messageElement -> {
  443 + checkProtoFileCommonSettings(schemaName, messageElement.getGroups().isEmpty(),
  444 + " Message definition groups don't support!");
  445 + checkProtoFileCommonSettings(schemaName, messageElement.getOptions().isEmpty(),
  446 + " Message definition options don't support!");
  447 + checkProtoFileCommonSettings(schemaName, messageElement.getExtensions().isEmpty(),
  448 + " Message definition extensions don't support!");
  449 + checkProtoFileCommonSettings(schemaName, messageElement.getReserveds().isEmpty(),
  450 + " Message definition reserved elements don't support!");
  451 + checkFieldElements(schemaName, messageElement.getFields());
  452 + List<OneOfElement> oneOfs = messageElement.getOneOfs();
  453 + if (!oneOfs.isEmpty()) {
  454 + oneOfs.forEach(oneOfElement -> {
  455 + checkProtoFileCommonSettings(schemaName, oneOfElement.getGroups().isEmpty(),
  456 + " OneOf definition groups don't support!");
  457 + checkFieldElements(schemaName, oneOfElement.getFields());
  458 + });
  459 + }
  460 + List<TypeElement> nestedTypes = messageElement.getNestedTypes();
  461 + if (!nestedTypes.isEmpty()) {
  462 + List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes);
  463 + if (!nestedEnumTypes.isEmpty()) {
  464 + checkEnumElements(schemaName, nestedEnumTypes);
  465 + }
  466 + List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes);
  467 + checkMessageElements(schemaName, nestedMessageTypes);
  468 + }
  469 + });
  470 + }
  471 + }
  472 +
  473 + private List<MessageElement> getMessageTypes(List<TypeElement> types) {
  474 + return types.stream()
  475 + .filter(typeElement -> typeElement instanceof MessageElement)
  476 + .map(typeElement -> (MessageElement) typeElement)
  477 + .collect(Collectors.toList());
  478 + }
  479 +
  480 + private List<EnumElement> getEnumElements(List<TypeElement> types) {
  481 + return types.stream()
  482 + .filter(typeElement -> typeElement instanceof EnumElement)
  483 + .map(typeElement -> (EnumElement) typeElement)
  484 + .collect(Collectors.toList());
  485 + }
  486 +
337 487 };
338 488
339 489 private PaginatedRemover<TenantId, DeviceProfile> tenantDeviceProfilesRemover =
... ...
... ... @@ -106,6 +106,8 @@
106 106 <commons-collections.version>3.2.2</commons-collections.version>
107 107 <java-websocket.version>1.5.0</java-websocket.version>
108 108 <micrometer.version>1.5.2</micrometer.version>
  109 + <protobuf-dynamic.version>1.0.2TB</protobuf-dynamic.version>
  110 + <wire-schema.version>3.4.0</wire-schema.version>
109 111 </properties>
110 112
111 113 <modules>
... ... @@ -1369,6 +1371,16 @@
1369 1371 <artifactId>micrometer-registry-prometheus</artifactId>
1370 1372 <version>${micrometer.version}</version>
1371 1373 </dependency>
  1374 + <dependency>
  1375 + <groupId>org.thingsboard</groupId>
  1376 + <artifactId>protobuf-dynamic</artifactId>
  1377 + <version>${protobuf-dynamic.version}</version>
  1378 + </dependency>
  1379 + <dependency>
  1380 + <groupId>com.squareup.wire</groupId>
  1381 + <artifactId>wire-schema</artifactId>
  1382 + <version>${wire-schema.version}</version>
  1383 + </dependency>
1372 1384 </dependencies>
1373 1385 </dependencyManagement>
1374 1386
... ...
... ... @@ -20,17 +20,6 @@
20 20 <fieldset class="fields-group">
21 21 <legend class="group-title" translate>device-profile.mqtt-device-topic-filters</legend>
22 22 <div fxLayoutGap="8px" fxLayout="column">
23   - <mat-form-field class="mat-block">
24   - <mat-label translate>device-profile.mqtt-device-payload-type</mat-label>
25   - <mat-select formControlName="transportPayloadType" required>
26   - <mat-option *ngFor="let type of mqttTransportPayloadTypes" [value]="type">
27   - {{mqttTransportPayloadTypeTranslations.get(type) | translate}}
28   - </mat-option>
29   - </mat-select>
30   - <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadType').hasError('required')">
31   - {{ 'device-profile.mqtt-payload-type-required' | translate }}
32   - </mat-error>
33   - </mat-form-field>
34 23 <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column">
35 24 <mat-form-field fxFlex>
36 25 <mat-label translate>device-profile.telemetry-topic-filter</mat-label>
... ... @@ -71,5 +60,42 @@
71 60 <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div>
72 61 </div>
73 62 </fieldset>
  63 + <section formGroupName="transportPayloadTypeConfiguration">
  64 + <fieldset class="fields-group">
  65 + <legend class="group-title" translate>device-profile.mqtt-device-payload-type</legend>
  66 + <div fxLayoutGap="8px" fxLayout="column">
  67 + <mat-form-field class="mat-block">
  68 + <mat-select formControlName="transportPayloadType" required>
  69 + <mat-option *ngFor="let type of mqttTransportPayloadTypes" [value]="type">
  70 + {{mqttTransportPayloadTypeTranslations.get(type) | translate}}
  71 + </mat-option>
  72 + </mat-select>
  73 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').hasError('required')">
  74 + {{ 'device-profile.mqtt-payload-type-required' | translate }}
  75 + </mat-error>
  76 + </mat-form-field>
  77 + <div *ngIf="protoPayloadType" fxLayout="column">
  78 + <mat-form-field fxFlex>
  79 + <mat-label translate>device-profile.telemetry-proto-schema</mat-label>
  80 + <textarea matInput required
  81 + formControlName="deviceTelemetryProtoSchema"
  82 + rows="5"></textarea>
  83 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.deviceTelemetryProtoSchema').hasError('required')">
  84 + {{ 'device-profile.telemetry-proto-schema-required' | translate}}
  85 + </mat-error>
  86 + </mat-form-field>
  87 + <mat-form-field fxFlex>
  88 + <mat-label translate>device-profile.attributes-proto-schema</mat-label>
  89 + <textarea matInput required
  90 + formControlName="deviceAttributesProtoSchema"
  91 + rows="5"></textarea>
  92 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.deviceAttributesProtoSchema').hasError('required')">
  93 + {{ 'device-profile.attributes-proto-schema-required' | translate}}
  94 + </mat-error>
  95 + </mat-form-field>
  96 + </div>
  97 + </div>
  98 + </fieldset>
  99 + </section>
74 100 </section>
75 101 </form>
... ...
... ... @@ -28,10 +28,11 @@ import { Store } from '@ngrx/store';
28 28 import { AppState } from '@app/core/core.state';
29 29 import { coerceBooleanProperty } from '@angular/cdk/coercion';
30 30 import {
31   - MqttTransportPayloadType,
32 31 DeviceProfileTransportConfiguration,
33 32 DeviceTransportType,
34   - MqttDeviceProfileTransportConfiguration, mqttTransportPayloadTypeTranslationMap
  33 + MqttDeviceProfileTransportConfiguration,
  34 + MqttTransportPayloadType,
  35 + mqttTransportPayloadTypeTranslationMap
35 36 } from '@shared/models/device.models';
36 37 import { isDefinedAndNotNull } from '@core/utils';
37 38
... ... @@ -85,9 +86,15 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
85 86 configuration: this.fb.group({
86 87 deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]],
87 88 deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]],
88   - transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
  89 + transportPayloadTypeConfiguration: this.fb.group({
  90 + transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
  91 + })
89 92 }, {validator: this.uniqueDeviceTopicValidator})
90 93 });
  94 + this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').valueChanges.subscribe(payloadType => {
  95 + this.updateTransportPayloadBasedControls(payloadType);
  96 + this.mqttDeviceProfileTransportConfigurationFormGroup.updateValueAndValidity();
  97 + });
91 98 this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
92 99 this.updateModel();
93 100 });
... ... @@ -102,8 +109,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
102 109 }
103 110 }
104 111
  112 + get protoPayloadType(): boolean {
  113 + let transportPayloadType = this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').value;
  114 + return transportPayloadType === MqttTransportPayloadType.PROTOBUF;
  115 + }
  116 +
105 117 writeValue(value: MqttDeviceProfileTransportConfiguration | null): void {
106 118 if (isDefinedAndNotNull(value)) {
  119 + this.updateTransportPayloadBasedControls(value.transportPayloadTypeConfiguration.transportPayloadType);
107 120 this.mqttDeviceProfileTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
108 121 }
109 122 }
... ... @@ -117,6 +130,41 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
117 130 this.propagateChange(configuration);
118 131 }
119 132
  133 + private updateTransportPayloadBasedControls(type: MqttTransportPayloadType) {
  134 + const transportPayloadTypeConfigurationFormGroup = this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration') as FormGroup;
  135 + if (type === MqttTransportPayloadType.PROTOBUF) {
  136 + const defaultTelemetrySchema = "syntax =\"proto3\";\n" +
  137 + "package telemetry;\n" +
  138 + "\n" +
  139 + "message SensorDataReading {\n" +
  140 + "\n" +
  141 + " double temperature = 1;\n" +
  142 + " double humidity = 2;\n" +
  143 + " InnerObject innerObject = 3;\n" +
  144 + "\n" +
  145 + " message InnerObject {\n" +
  146 + " string key1 = 1;\n" +
  147 + " bool key2 = 2;\n" +
  148 + " double key3 = 3;\n" +
  149 + " int32 key4 = 4;\n" +
  150 + " string key5 = 5;\n" +
  151 + " }\n" +
  152 + "}\n";
  153 + const defaultAttributesSchema = "syntax =\"proto3\";\n" +
  154 + "package attributes;\n" +
  155 + "\n" +
  156 + "message SensorDataReading {\n" +
  157 + " string firmwareVersion = 1;\n" +
  158 + " string serialNumber = 2;\n" +
  159 + "}";
  160 + transportPayloadTypeConfigurationFormGroup.registerControl('deviceTelemetryProtoSchema', this.fb.control(defaultTelemetrySchema, Validators.required));
  161 + transportPayloadTypeConfigurationFormGroup.registerControl('deviceAttributesProtoSchema', this.fb.control(defaultAttributesSchema, Validators.required));
  162 + } else {
  163 + transportPayloadTypeConfigurationFormGroup.removeControl('deviceTelemetryProtoSchema');
  164 + transportPayloadTypeConfigurationFormGroup.removeControl('deviceAttributesProtoSchema');
  165 + }
  166 + }
  167 +
120 168 private validationMQTTTopic(): ValidatorFn {
121 169 return (c: FormControl) => {
122 170 const newTopic = c.value;
... ...
... ... @@ -148,6 +148,9 @@ export interface DefaultDeviceProfileTransportConfiguration {
148 148 export interface MqttDeviceProfileTransportConfiguration {
149 149 deviceTelemetryTopic?: string;
150 150 deviceAttributesTopic?: string;
  151 + transportPayloadTypeConfiguration?: {
  152 + transportPayloadType?: MqttTransportPayloadType;
  153 + };
151 154 [key: string]: any;
152 155 }
153 156
... ... @@ -207,7 +210,7 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
207 210 const mqttTransportConfiguration: MqttDeviceProfileTransportConfiguration = {
208 211 deviceTelemetryTopic: 'v1/devices/me/telemetry',
209 212 deviceAttributesTopic: 'v1/devices/me/attributes',
210   - transportPayloadType: MqttTransportPayloadType.JSON
  213 + transportPayloadTypeConfiguration: {transportPayloadType: MqttTransportPayloadType.JSON}
211 214 };
212 215 transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT};
213 216 break;
... ...
... ... @@ -895,6 +895,10 @@
895 895 "telemetry-topic-filter-required": "Telemetry topic filter is required.",
896 896 "attributes-topic-filter": "Attributes topic filter",
897 897 "attributes-topic-filter-required": "Attributes topic filter is required.",
  898 + "telemetry-proto-schema": "Telemetry proto schema",
  899 + "telemetry-proto-schema-required": "Telemetry proto schema is required.",
  900 + "attributes-proto-schema": "Attributes proto schema",
  901 + "attributes-proto-schema-required": "Attributes proto schema is required.",
898 902 "rpc-response-topic-filter": "RPC response topic filter",
899 903 "rpc-response-topic-filter-required": "RPC response topic filter is required.",
900 904 "not-valid-pattern-topic-filter": "Not valid pattern topic filter",
... ...