Commit 4a1878130493753063101c8d03ec1b13ca015dbe

Authored by ShvaykaD
1 parent 4725ad14

added TransportPayloadTypeConfiguration, fix tests, ui

Showing 18 changed files with 725 additions and 738 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,9 +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;
42   -import org.thingsboard.server.common.data.device.profile.MqttJsonDeviceProfileTransportConfiguration;
43   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
  43 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  44 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
44 45 import org.thingsboard.server.common.data.security.Authority;
45 46 import org.thingsboard.server.common.data.security.DeviceCredentials;
46 47 import org.thingsboard.server.controller.AbstractControllerTest;
... ... @@ -94,7 +95,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
94 95 protected Device savedGateway;
95 96 protected String gatewayAccessToken;
96 97
97   - protected MqttDeviceProfileTransportConfiguration transportConfiguration;
  98 + protected DeviceProfile deviceProfile;
98 99
99 100 protected void processBeforeTest (String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
100 101 this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, null, null, DeviceProfileProvisionType.DISABLED, null, null);
... ... @@ -139,11 +140,11 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
139 140
140 141 if (payloadType != null) {
141 142 DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, telemetryProtoSchema, attributesProtoSchema, provisionType, provisionKey, provisionSecret);
142   - DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class);
143   - device.setType(savedDeviceProfile.getName());
144   - device.setDeviceProfileId(savedDeviceProfile.getId());
145   - gateway.setType(savedDeviceProfile.getName());
146   - gateway.setDeviceProfileId(savedDeviceProfile.getId());
  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());
147 148 }
148 149
149 150 savedDevice = doPost("/api/device", device, Device.class);
... ... @@ -242,27 +243,30 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
242 243 deviceProfile.setDescription(transportPayloadType.name() + " Test");
243 244 DeviceProfileData deviceProfileData = new DeviceProfileData();
244 245 DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  246 + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration();
  247 + if (!StringUtils.isEmpty(telemetryTopic)) {
  248 + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
  249 + }
  250 + if (!StringUtils.isEmpty(attributesTopic)) {
  251 + mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(attributesTopic);
  252 + }
  253 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration;
245 254 if (TransportPayloadType.JSON.equals(transportPayloadType)) {
246   - transportConfiguration = new MqttJsonDeviceProfileTransportConfiguration();
  255 + transportPayloadTypeConfiguration = new JsonTransportPayloadConfiguration();
247 256 } else {
248   - MqttProtoDeviceProfileTransportConfiguration protoTransportConfiguration = new MqttProtoDeviceProfileTransportConfiguration();
  257 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = new ProtoTransportPayloadConfiguration();
249 258 if (StringUtils.isEmpty(telemetryProtoSchema)) {
250 259 telemetryProtoSchema = DEVICE_TELEMETRY_PROTO_SCHEMA;
251 260 }
252 261 if (StringUtils.isEmpty(attributesProtoSchema)) {
253 262 attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA;
254 263 }
255   - protoTransportConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema);
256   - protoTransportConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema);
257   - transportConfiguration = protoTransportConfiguration;
  264 + protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema);
  265 + protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema);
  266 + transportPayloadTypeConfiguration = protoTransportPayloadConfiguration;
258 267 }
259   - if (!StringUtils.isEmpty(telemetryTopic)) {
260   - transportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
261   - }
262   - if (!StringUtils.isEmpty(attributesTopic)) {
263   - transportConfiguration.setDeviceAttributesTopic(attributesTopic);
264   - }
265   - deviceProfileData.setTransportConfiguration(transportConfiguration);
  268 + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration);
  269 + deviceProfileData.setTransportConfiguration(mqttDeviceProfileTransportConfiguration);
266 270 DeviceProfileProvisionConfiguration provisionConfiguration;
267 271 switch (provisionType) {
268 272 case ALLOW_CREATE_NEW_DEVICES:
... ... @@ -274,6 +278,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
274 278 case DISABLED:
275 279 default:
276 280 provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(provisionSecret);
  281 + break;
277 282 }
278 283 deviceProfileData.setProvisionConfiguration(provisionConfiguration);
279 284 deviceProfileData.setConfiguration(configuration);
... ...
... ... @@ -30,8 +30,11 @@ import org.junit.Test;
30 30 import org.thingsboard.server.common.data.Device;
31 31 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
32 32 import org.thingsboard.server.common.data.TransportPayloadType;
33   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
  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;
34 36 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  37 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
35 38 import org.thingsboard.server.gen.transport.TransportApiProtos;
36 39 import org.thingsboard.server.gen.transport.TransportProtos;
37 40
... ... @@ -82,11 +85,15 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends
82 85
83 86 protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception {
84 87 doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
85   - assertTrue(transportConfiguration instanceof MqttProtoDeviceProfileTransportConfiguration);
86   - MqttProtoDeviceProfileTransportConfiguration configuration = (MqttProtoDeviceProfileTransportConfiguration) transportConfiguration;
87   - ProtoFileElement transportProtoSchema = configuration.getTransportProtoSchema(ATTRIBUTES_SCHEMA_STR);
88   - DynamicSchema telemetrySchema = configuration.getDynamicSchema(transportProtoSchema, "attributesSchema");
89   - DynamicMessage.Builder postAttributesBuilder = telemetrySchema.newMessageBuilder("PostAttributes");
  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");
90 97 Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType();
91 98 assertNotNull(postAttributesMsgDescriptor);
92 99 DynamicMessage postAttributesMsg = postAttributesBuilder
... ...
... ... @@ -23,7 +23,10 @@ import lombok.extern.slf4j.Slf4j;
23 23 import org.junit.After;
24 24 import org.junit.Test;
25 25 import org.thingsboard.server.common.data.TransportPayloadType;
26   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
  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;
27 30 import org.thingsboard.server.gen.transport.TransportApiProtos;
28 31 import org.thingsboard.server.gen.transport.TransportProtos;
29 32
... ... @@ -47,11 +50,15 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
47 50 public void testPushMqttAttributes() throws Exception {
48 51 super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
49 52 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
50   - assertTrue(transportConfiguration instanceof MqttProtoDeviceProfileTransportConfiguration);
51   - MqttProtoDeviceProfileTransportConfiguration configuration = (MqttProtoDeviceProfileTransportConfiguration) transportConfiguration;
52   - ProtoFileElement transportProtoSchema = configuration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA);
53   - DynamicSchema telemetrySchema = configuration.getDynamicSchema(transportProtoSchema, "attributesSchema");
54   - DynamicMessage.Builder postAttributesBuilder = telemetrySchema.newMessageBuilder("PostAttributes");
  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");
55 62 Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType();
56 63 assertNotNull(postAttributesMsgDescriptor);
57 64 DynamicMessage postAttributesMsg = postAttributesBuilder
... ...
... ... @@ -26,8 +26,11 @@ import org.junit.Test;
26 26 import org.thingsboard.server.common.data.Device;
27 27 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
28 28 import org.thingsboard.server.common.data.TransportPayloadType;
29   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
  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;
30 32 import org.thingsboard.server.common.data.device.profile.MqttTopics;
  33 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
31 34 import org.thingsboard.server.gen.transport.TransportApiProtos;
32 35 import org.thingsboard.server.gen.transport.TransportProtos;
33 36
... ... @@ -51,10 +54,14 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
51 54 public void testPushMqttTelemetry() throws Exception {
52 55 super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
53 56 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
54   - assertTrue(transportConfiguration instanceof MqttProtoDeviceProfileTransportConfiguration);
55   - MqttProtoDeviceProfileTransportConfiguration configuration = (MqttProtoDeviceProfileTransportConfiguration) transportConfiguration;
56   - ProtoFileElement transportProtoSchema = configuration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA);
57   - DynamicSchema telemetrySchema = configuration.getDynamicSchema(transportProtoSchema, "telemetrySchema");
  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");
58 65 DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry");
59 66 Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType();
60 67 assertNotNull(postTelemetryMsgDescriptor);
... ... @@ -88,10 +95,14 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
88 95 "}";
89 96 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);
90 97 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
91   - assertTrue(transportConfiguration instanceof MqttProtoDeviceProfileTransportConfiguration);
92   - MqttProtoDeviceProfileTransportConfiguration configuration = (MqttProtoDeviceProfileTransportConfiguration) transportConfiguration;
93   - ProtoFileElement transportProtoSchema = configuration.getTransportProtoSchema(schemaStr);
94   - DynamicSchema telemetrySchema = configuration.getDynamicSchema(transportProtoSchema, "telemetrySchema");
  98 + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
  99 + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
  100 + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
  101 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
  102 + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration);
  103 + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  104 + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr);
  105 + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema");
95 106
96 107 DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("Values");
97 108 Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType();
... ...
... ... @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
33 33 @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M")})
34 34 public interface DeviceProfileTransportConfiguration {
35 35
  36 + @JsonIgnore
36 37 DeviceTransportType getType();
37 38
38 39 }
... ...
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/JsonTransportPayloadConfiguration.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttJsonDeviceProfileTransportConfiguration.java
... ... @@ -15,24 +15,14 @@
15 15 */
16 16 package org.thingsboard.server.common.data.device.profile;
17 17
18   -import com.fasterxml.jackson.annotation.JsonTypeName;
19   -import com.fasterxml.jackson.databind.JsonDeserializer;
20   -import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
21 18 import lombok.Data;
22   -import lombok.EqualsAndHashCode;
23   -import lombok.extern.slf4j.Slf4j;
24   -import org.thingsboard.server.common.data.DeviceTransportType;
25 19 import org.thingsboard.server.common.data.TransportPayloadType;
26 20
27   -@Slf4j
28   -@EqualsAndHashCode(callSuper = true)
29 21 @Data
30   -@JsonDeserialize(as = MqttJsonDeviceProfileTransportConfiguration.class)
31   -public class MqttJsonDeviceProfileTransportConfiguration extends MqttDeviceProfileTransportConfiguration{
  22 +public class JsonTransportPayloadConfiguration implements TransportPayloadTypeConfiguration {
32 23
33 24 @Override
34 25 public TransportPayloadType getTransportPayloadType() {
35 26 return TransportPayloadType.JSON;
36 27 }
37   -
38 28 }
... ...
... ... @@ -15,30 +15,15 @@
15 15 */
16 16 package org.thingsboard.server.common.data.device.profile;
17 17
18   -import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
19   -import com.fasterxml.jackson.annotation.JsonSubTypes;
20   -import com.fasterxml.jackson.annotation.JsonTypeInfo;
21   -import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22 18 import lombok.Data;
23   -import org.thingsboard.server.common.data.TransportPayloadType;
24 19 import org.thingsboard.server.common.data.DeviceTransportType;
25 20
26   -@JsonIgnoreProperties(ignoreUnknown = true)
27   -@JsonTypeInfo(
28   - use = JsonTypeInfo.Id.NAME,
29   - include = JsonTypeInfo.As.PROPERTY,
30   - property = "transportPayloadType")
31   -@JsonSubTypes({
32   - @JsonSubTypes.Type(value = MqttJsonDeviceProfileTransportConfiguration.class, name = "JSON"),
33   - @JsonSubTypes.Type(value = MqttProtoDeviceProfileTransportConfiguration.class, name = "PROTOBUF")})
34   -@JsonDeserialize(using = MqttTransportConfigurationDeserializer.class)
35 21 @Data
36   -public abstract class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
  22 +public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
37 23
38   - public abstract TransportPayloadType getTransportPayloadType();
39   -
40   - protected String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
41   - protected String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
  24 + private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC;
  25 + private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
  26 + private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration;
42 27
43 28 @Override
44 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.fasterxml.jackson.core.JsonParser;
19   -import com.fasterxml.jackson.core.JsonProcessingException;
20   -import com.fasterxml.jackson.databind.DeserializationContext;
21   -import com.fasterxml.jackson.databind.JsonNode;
22   -import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
23   -import lombok.extern.slf4j.Slf4j;
24   -import org.thingsboard.server.common.data.TransportPayloadType;
25   -
26   -import java.io.IOException;
27   -
28   -@Slf4j
29   -public class MqttTransportConfigurationDeserializer extends StdDeserializer<MqttDeviceProfileTransportConfiguration> {
30   -
31   - public MqttTransportConfigurationDeserializer() {
32   - super(MqttDeviceProfileTransportConfiguration.class);
33   - }
34   -
35   - @Override
36   - public MqttDeviceProfileTransportConfiguration deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
37   - try {
38   - JsonNode jsonNode = jsonParser.readValueAsTree();
39   - if (jsonNode.hasNonNull("transportPayloadType") && jsonNode.get("transportPayloadType").asText().equals(TransportPayloadType.PROTOBUF.name())) {
40   - return jsonParser.getCodec().treeToValue(jsonNode, MqttProtoDeviceProfileTransportConfiguration.class);
41   - } else {
42   - return jsonParser.getCodec().treeToValue(jsonNode, MqttJsonDeviceProfileTransportConfiguration.class);
43   - }
44   - } catch (IOException e) {
45   - log.trace("Failed to deserialize JSON content into equivalent tree model during creating {}!", MqttDeviceProfileTransportConfiguration.class.getSimpleName(), e);
46   - throw new RuntimeException("Failed to deserialize JSON content into equivalent tree model during creating " + MqttDeviceProfileTransportConfiguration.class.getSimpleName() + "!", e);
47   - }
48   - }
49   -
50   -}
\ No newline at end of file
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/ProtoTransportPayloadConfiguration.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttProtoDeviceProfileTransportConfiguration.java
... ... @@ -15,16 +15,11 @@
15 15 */
16 16 package org.thingsboard.server.common.data.device.profile;
17 17
18   -import com.fasterxml.jackson.annotation.JsonTypeName;
19   -import com.fasterxml.jackson.databind.JsonDeserializer;
20   -import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
21 18 import com.github.os72.protobuf.dynamic.DynamicSchema;
22 19 import com.github.os72.protobuf.dynamic.EnumDefinition;
23 20 import com.github.os72.protobuf.dynamic.MessageDefinition;
24 21 import com.google.protobuf.Descriptors;
25 22 import com.google.protobuf.DynamicMessage;
26   -import com.squareup.wire.Syntax;
27   -import com.squareup.wire.schema.Field;
28 23 import com.squareup.wire.schema.Location;
29 24 import com.squareup.wire.schema.internal.parser.EnumConstantElement;
30 25 import com.squareup.wire.schema.internal.parser.EnumElement;
... ... @@ -35,30 +30,22 @@ import com.squareup.wire.schema.internal.parser.ProtoFileElement;
35 30 import com.squareup.wire.schema.internal.parser.ProtoParser;
36 31 import com.squareup.wire.schema.internal.parser.TypeElement;
37 32 import lombok.Data;
38   -import lombok.EqualsAndHashCode;
39 33 import lombok.extern.slf4j.Slf4j;
40 34 import org.thingsboard.server.common.data.TransportPayloadType;
41 35
42   -
43 36 import java.util.ArrayList;
44 37 import java.util.Collections;
45 38 import java.util.List;
46 39 import java.util.stream.Collectors;
47 40
48 41 @Slf4j
49   -@EqualsAndHashCode(callSuper = true)
50 42 @Data
51   -@JsonDeserialize(as = MqttProtoDeviceProfileTransportConfiguration.class)
52   -public class MqttProtoDeviceProfileTransportConfiguration extends MqttDeviceProfileTransportConfiguration {
  43 +public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeConfiguration {
53 44
54 45 public static final Location LOCATION = new Location("", "", -1, -1);
55 46 public static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema";
56 47 public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema";
57 48
58   - public static String invalidSchemaProvidedMessage(String schemaName) {
59   - return "[Transport Configuration] invalid " + schemaName + " schema provided!";
60   - }
61   -
62 49 private String deviceTelemetryProtoSchema;
63 50 private String deviceAttributesProtoSchema;
64 51
... ... @@ -67,23 +54,15 @@ public class MqttProtoDeviceProfileTransportConfiguration extends MqttDeviceProf
67 54 return TransportPayloadType.PROTOBUF;
68 55 }
69 56
70   - public void validateTransportProtoSchema(String schema, String schemaName) throws IllegalArgumentException {
71   - ProtoParser schemaParser = new ProtoParser(LOCATION, schema.toCharArray());
72   - ProtoFileElement protoFileElement;
73   - try {
74   - protoFileElement = schemaParser.readProtoFile();
75   - } catch (Exception e) {
76   - throw new IllegalArgumentException("[Transport Configuration] failed to parse " + schemaName + " due to: " + e.getMessage());
77   - }
78   - checkProtoFileSyntax(schemaName, protoFileElement);
79   - checkProtoFileCommonSettings(schemaName, protoFileElement.getOptions().isEmpty(), " Schema options don't support!");
80   - checkProtoFileCommonSettings(schemaName, protoFileElement.getPublicImports().isEmpty(), " Schema public imports don't support!");
81   - checkProtoFileCommonSettings(schemaName, protoFileElement.getImports().isEmpty(), " Schema imports don't support!");
82   - checkProtoFileCommonSettings(schemaName, protoFileElement.getExtendDeclarations().isEmpty(), " Schema extend declarations don't support!");
83   - checkTypeElements(schemaName, protoFileElement);
  57 + public Descriptors.Descriptor getTelemetryDynamicMessageDescriptor(String deviceTelemetryProtoSchema) {
  58 + return getDescriptor(deviceTelemetryProtoSchema, TELEMETRY_PROTO_SCHEMA);
84 59 }
85 60
86   - public Descriptors.Descriptor getDynamicMessageDescriptor(String protoSchema, String schemaName) {
  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) {
87 66 try {
88 67 ProtoFileElement protoFileElement = getTransportProtoSchema(protoSchema);
89 68 DynamicSchema dynamicSchema = getDynamicSchema(protoFileElement, schemaName);
... ... @@ -125,91 +104,6 @@ public class MqttProtoDeviceProfileTransportConfiguration extends MqttDeviceProf
125 104 }
126 105 }
127 106
128   - private void checkProtoFileSyntax(String schemaName, ProtoFileElement protoFileElement) {
129   - if (protoFileElement.getSyntax() == null || !protoFileElement.getSyntax().equals(Syntax.PROTO_3)) {
130   - throw new IllegalArgumentException("[Transport Configuration] invalid schema syntax: " + protoFileElement.getSyntax() +
131   - " for: " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!");
132   - }
133   - }
134   -
135   - private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) {
136   - if (!isEmptySettings) {
137   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage);
138   - }
139   - }
140   -
141   - private void checkTypeElements(String schemaName, ProtoFileElement protoFileElement) {
142   - List<TypeElement> types = protoFileElement.getTypes();
143   - if (!types.isEmpty()) {
144   - if (types.stream().noneMatch(typeElement -> typeElement instanceof MessageElement)) {
145   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " At least one Message definition should exists!");
146   - } else {
147   - checkEnumElements(schemaName, getEnumElements(types));
148   - checkMessageElements(schemaName, getMessageTypes(types));
149   - }
150   - } else {
151   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Type elements is empty!");
152   - }
153   - }
154   -
155   - private void checkFieldElements(String schemaName, List<FieldElement> fieldElements) {
156   - if (!fieldElements.isEmpty()) {
157   - boolean hasRequiredLabel = fieldElements.stream().anyMatch(fieldElement -> {
158   - Field.Label label = fieldElement.getLabel();
159   - return label != null && label.equals(Field.Label.REQUIRED);
160   - });
161   - if (hasRequiredLabel) {
162   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Required labels are not supported!");
163   - }
164   - boolean hasDefaultValue = fieldElements.stream().anyMatch(fieldElement -> fieldElement.getDefaultValue() != null);
165   - if (hasDefaultValue) {
166   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Default values are not supported!");
167   - }
168   - }
169   - }
170   -
171   - private void checkEnumElements(String schemaName, List<EnumElement> enumTypes) {
172   - if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getNestedTypes().isEmpty())) {
173   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Nested types in Enum definitions are not supported!");
174   - }
175   - if (enumTypes.stream().anyMatch(enumElement -> !enumElement.getOptions().isEmpty())) {
176   - throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + " Enum definitions options are not supported!");
177   - }
178   - }
179   -
180   - private void checkMessageElements(String schemaName, List<MessageElement> messageElementsList) {
181   - if (!messageElementsList.isEmpty()) {
182   - messageElementsList.forEach(messageElement -> {
183   - checkProtoFileCommonSettings(schemaName, messageElement.getGroups().isEmpty(),
184   - " Message definition groups don't support!");
185   - checkProtoFileCommonSettings(schemaName, messageElement.getOptions().isEmpty(),
186   - " Message definition options don't support!");
187   - checkProtoFileCommonSettings(schemaName, messageElement.getExtensions().isEmpty(),
188   - " Message definition extensions don't support!");
189   - checkProtoFileCommonSettings(schemaName, messageElement.getReserveds().isEmpty(),
190   - " Message definition reserved elements don't support!");
191   - checkFieldElements(schemaName, messageElement.getFields());
192   - List<OneOfElement> oneOfs = messageElement.getOneOfs();
193   - if (!oneOfs.isEmpty()) {
194   - oneOfs.forEach(oneOfElement -> {
195   - checkProtoFileCommonSettings(schemaName, oneOfElement.getGroups().isEmpty(),
196   - " OneOf definition groups don't support!");
197   - checkFieldElements(schemaName, oneOfElement.getFields());
198   - });
199   - }
200   - List<TypeElement> nestedTypes = messageElement.getNestedTypes();
201   - if (!nestedTypes.isEmpty()) {
202   - List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes);
203   - if (!nestedEnumTypes.isEmpty()) {
204   - checkEnumElements(schemaName, nestedEnumTypes);
205   - }
206   - List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes);
207   - checkMessageElements(schemaName, nestedMessageTypes);
208   - }
209   - });
210   - }
211   - }
212   -
213 107 public ProtoFileElement getTransportProtoSchema(String protoSchema) {
214 108 return new ProtoParser(LOCATION, protoSchema.toCharArray()).readProtoFile();
215 109 }
... ...
  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 +}
... ...
... ... @@ -25,7 +25,8 @@ import org.thingsboard.server.common.data.DeviceTransportType;
25 25 import org.thingsboard.server.common.data.TransportPayloadType;
26 26 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
27 27 import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
28   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
  28 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
  29 +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
29 30 import org.thingsboard.server.transport.mqtt.MqttTransportContext;
30 31 import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor;
31 32 import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter;
... ... @@ -118,11 +119,12 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
118 119 if (transportConfiguration.getType().equals(DeviceTransportType.MQTT) &&
119 120 transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) {
120 121 MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
121   - payloadType = mqttConfig.getTransportPayloadType();
  122 + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttConfig.getTransportPayloadTypeConfiguration();
  123 + payloadType = transportPayloadTypeConfiguration.getTransportPayloadType();
122 124 telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic());
123 125 attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic());
124   - if (mqttConfig instanceof MqttProtoDeviceProfileTransportConfiguration) {
125   - updateDynamicMessageDescriptors(mqttConfig);
  126 + if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) {
  127 + updateDynamicMessageDescriptors(transportPayloadTypeConfiguration);
126 128 }
127 129 } else {
128 130 telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter();
... ... @@ -130,9 +132,9 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext {
130 132 }
131 133 }
132 134
133   - private void updateDynamicMessageDescriptors(MqttDeviceProfileTransportConfiguration mqttConfig) {
134   - MqttProtoDeviceProfileTransportConfiguration protoMqttConfig = (MqttProtoDeviceProfileTransportConfiguration) mqttConfig;
135   - telemetryDynamicMessageDescriptor = protoMqttConfig.getDynamicMessageDescriptor(protoMqttConfig.getDeviceTelemetryProtoSchema(), "telemetrySchema");
136   - attributesDynamicMessageDescriptor = protoMqttConfig.getDynamicMessageDescriptor(protoMqttConfig.getDeviceAttributesProtoSchema(), "attributesSchema");
  135 + private void updateDynamicMessageDescriptors(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration) {
  136 + ProtoTransportPayloadConfiguration protoTransportPayloadConfig = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
  137 + telemetryDynamicMessageDescriptor = protoTransportPayloadConfig.getTelemetryDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceTelemetryProtoSchema());
  138 + attributesDynamicMessageDescriptor = protoTransportPayloadConfig.getAttributesDynamicMessageDescriptor(protoTransportPayloadConfig.getDeviceAttributesProtoSchema());
137 139 }
138 140 }
... ...
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.transport.mqtt.util;
17   -
18   -import com.github.os72.protobuf.dynamic.DynamicSchema;
19   -import com.google.protobuf.Descriptors;
20   -import com.google.protobuf.DynamicMessage;
21   -import com.google.protobuf.InvalidProtocolBufferException;
22   -import com.google.protobuf.util.JsonFormat;
23   -import com.squareup.wire.schema.internal.parser.ProtoFileElement;
24   -import com.squareup.wire.schema.internal.parser.ProtoParser;
25   -import org.junit.Rule;
26   -import org.junit.Test;
27   -import org.junit.rules.ExpectedException;
28   -import org.junit.runner.RunWith;
29   -import org.mockito.runners.MockitoJUnitRunner;
30   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
31   -
32   -import java.util.List;
33   -import java.util.Set;
34   -
35   -import static org.junit.Assert.assertEquals;
36   -import static org.junit.Assert.assertNotNull;
37   -import static org.junit.Assert.assertTrue;
38   -import static org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration.LOCATION;
39   -
40   -@RunWith(MockitoJUnitRunner.class)
41   -public class MqttDynamicProtoSchemaTest {
42   -
43   - private static final String PROTO_SCHEMA_WITH_NESTED_MSG_TYPES = "syntax = \"proto3\";\n" +
44   - "\n" +
45   - "package testnested;\n" +
46   - "\n" +
47   - "message Outer {\n" +
48   - " message MiddleAA {\n" +
49   - " message Inner {\n" +
50   - " int64 ival = 1;\n" +
51   - " bool booly = 2;\n" +
52   - " }\n" +
53   - " Inner inner = 1;\n" +
54   - " }\n" +
55   - " message MiddleBB {\n" +
56   - " message Inner {\n" +
57   - " int32 ival = 1;\n" +
58   - " bool booly = 2;\n" +
59   - " }\n" +
60   - " Inner inner = 1;\n" +
61   - " }\n" +
62   - " MiddleAA middleAA = 1;\n" +
63   - " MiddleBB middleBB = 2;\n" +
64   - "}\n";
65   -
66   - private static final String PROTO_SCHEMA_WITH_ONE_OFS = "syntax = \"proto3\";\n" +
67   - "\n" +
68   - "package testoneofs;\n" +
69   - "\n" +
70   - "message SubMessage {\n" +
71   - " repeated string name = 1;\n" +
72   - "}\n" +
73   - "\n" +
74   - "message SampleMessage {\n" +
75   - " oneof testOneOf {\n" +
76   - " string name = 4;\n" +
77   - " SubMessage subMessage = 9;\n" +
78   - " }\n" +
79   - "}";
80   -
81   - private static final String IVALID_PROTO_SCHEMA_REQUIRED_FIELD_EXISTS = "syntax = \"proto3\";\n" +
82   - "\n" +
83   - "package schemavalidation;\n" +
84   - "\n" +
85   - "message SchemaValidationTest {\n" +
86   - " required int32 parameter = 1;\n" +
87   - "}";
88   -
89   - private static final String INVALID_PROTO_SCHEMA_NOT_VALID_SYNTAX = "syntax = \"proto2\";\n" +
90   - "\n" +
91   - "package schemavalidation;\n" +
92   - "\n" +
93   - "message SchemaValidationTest {\n" +
94   - " required int32 parameter = 1;\n" +
95   - "}";
96   -
97   - private static final String INVALID_PROTO_SCHEMA_OPTIONS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
98   - "\n" +
99   - "option java_package = \"com.test.schemavalidation\";\n" +
100   - "option java_multiple_files = true;\n" +
101   - "\n" +
102   - "package schemavalidation;\n" +
103   - "\n" +
104   - "message SchemaValidationTest {\n" +
105   - " int32 parameter = 1;\n" +
106   - "}";
107   -
108   - private static final String INVALID_PROTO_SCHEMA_PUBLIC_IMPORTS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
109   - "\n" +
110   - "import public \"oldschema.proto\";\n" +
111   - "\n" +
112   - "package schemavalidation;\n" +
113   - "\n" +
114   - "message SchemaValidationTest {\n" +
115   - " int32 parameter = 1;\n" +
116   - "}";
117   -
118   - private static final String INVALID_PROTO_SCHEMA_IMPORTS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
119   - "\n" +
120   - "import \"oldschema.proto\";\n" +
121   - "\n" +
122   - "package schemavalidation;\n" +
123   - "\n" +
124   - "message SchemaValidationTest {\n" +
125   - " int32 parameter = 1;\n" +
126   - "}";
127   -
128   - private static final String INVALID_PROTO_SCHEMA_EXTEND_DECLARATION_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
129   - "\n" +
130   - "package schemavalidation;\n" +
131   - "\n" +
132   - "extend google.protobuf.MethodOptions {\n" +
133   - " MyMessage my_method_option = 50007;\n" +
134   - "}";
135   -
136   - private static final String INVALID_PROTO_SCHEMA_ENUM_OPTIONS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
137   - "\n" +
138   - "package schemavalidation;\n" +
139   - "\n" +
140   - "enum testEnum {\n" +
141   - " option allow_alias = true;\n" +
142   - " DEFAULT = 0;\n" +
143   - " STARTED = 1;\n" +
144   - " RUNNING = 2;\n" +
145   - "}\n" +
146   - "\n" +
147   - "message testMessage {\n" +
148   - " int32 parameter = 1;\n" +
149   - "}\n";
150   -
151   - private static final String INVALID_PROTO_SCHEMA_NO_MESSAGE_TYPES_EXISTS = "syntax = \"proto3\";\n" +
152   - "\n" +
153   - "package schemavalidation;\n" +
154   - "\n" +
155   - "enum testEnum {\n" +
156   - " DEFAULT = 0;\n" +
157   - " STARTED = 1;\n" +
158   - " RUNNING = 2;\n" +
159   - "}";
160   -
161   - private static final String INVALID_PROTO_SCHEMA_MESSAGE_OPTIONS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
162   - "\n" +
163   - "package schemavalidation;\n" +
164   - "\n" +
165   - "message testMessage {\n" +
166   - " option allow_alias = true;\n" +
167   - " int32 parameter = 1;\n" +
168   - "}";
169   -
170   - private static final String INVALID_PROTO_SCHEMA_MESSAGE_EXTENSIONS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
171   - "\n" +
172   - "package schemavalidation;\n" +
173   - "\n" +
174   - "message TestMessage {\n" +
175   - " extensions 100 to 199;\n" +
176   - "}\n";
177   -
178   - private static final String INVALID_PROTO_SCHEMA_MESSAGE_GROUPS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
179   - "\n" +
180   - "package schemavalidation;\n" +
181   - "\n" +
182   - "message TestMessage {\n" +
183   - " repeated group Result = 1 {\n" +
184   - " string url = 2;\n" +
185   - " string title = 3;\n" +
186   - " repeated string snippets = 4;\n" +
187   - " }\n" +
188   - "}\n";
189   -
190   - private static final String INVALID_PROTO_SCHEMA_MESSAGE_RESERVED_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
191   - "\n" +
192   - "package schemavalidation;\n" +
193   - "\n" +
194   - "message Foo {\n" +
195   - " reserved 2, 15, 9 to 11;\n" +
196   - " reserved \"foo\", \"bar\";\n" +
197   - "}";
198   -
199   - private static final String INVALID_PROTO_SCHEMA_ONE_OFS_GROUPS_NOT_SUPPORTED = "syntax = \"proto3\";\n" +
200   - "\n" +
201   - "package schemavalidation;\n" +
202   - "\n" +
203   - "message SampleMessage {\n" +
204   - " oneof test_oneof {\n" +
205   - " string name = 1;\n" +
206   - " group Result = 2 {\n" +
207   - " \tstring url = 3;\n" +
208   - " \tstring title = 4;\n" +
209   - " \trepeated string snippets = 5;\n" +
210   - " }\n" +
211   - " }\n" +
212   - "}";
213   -
214   - private static final MqttProtoDeviceProfileTransportConfiguration mqttProtoDeviceProfileTransportConfiguration = new MqttProtoDeviceProfileTransportConfiguration();
215   -
216   - private static void validateTransportProtoSchema(String schema, String schemaName) {
217   - mqttProtoDeviceProfileTransportConfiguration.validateTransportProtoSchema(schema, schemaName);
218   - }
219   -
220   - private static DynamicSchema getDynamicSchema(String schema, String schemaName) {
221   - ProtoFileElement protoFileElement = getTransportProtoSchema(schema);
222   - return mqttProtoDeviceProfileTransportConfiguration.getDynamicSchema(protoFileElement, schemaName);
223   - }
224   -
225   - @Rule
226   - public ExpectedException exceptionRule = ExpectedException.none();
227   -
228   - @Test
229   - public void testDynamicSchemaProtoFileValidation() {
230   - processValidation("[Transport Configuration] failed to parse testParseToProtoFile due to: Syntax error in :6:4: 'required' label forbidden in proto3 field declarations", IVALID_PROTO_SCHEMA_REQUIRED_FIELD_EXISTS, "testParseToProtoFile");
231   - }
232   -
233   - @Test
234   - public void testDynamicSchemaSyntaxValidation() {
235   - processValidation("[Transport Configuration] invalid schema syntax: proto2 for: testSyntaxValidation provided! Only proto3 allowed!", INVALID_PROTO_SCHEMA_NOT_VALID_SYNTAX, "testSyntaxValidation");
236   - }
237   -
238   - @Test
239   - public void testDynamicSchemaOptionsValidation() {
240   - processValidation("[Transport Configuration] invalid testOptionsValidation schema provided! Schema options don't support!", INVALID_PROTO_SCHEMA_OPTIONS_NOT_SUPPORTED, "testOptionsValidation");
241   - }
242   -
243   - @Test
244   - public void testDynamicSchemaPublicImportsValidation() {
245   - processValidation("[Transport Configuration] invalid testPublicImportsValidation schema provided! Schema public imports don't support!", INVALID_PROTO_SCHEMA_PUBLIC_IMPORTS_NOT_SUPPORTED, "testPublicImportsValidation");
246   - }
247   -
248   - @Test
249   - public void testDynamicSchemaImportsValidation() {
250   - processValidation("[Transport Configuration] invalid testImportsValidation schema provided! Schema imports don't support!", INVALID_PROTO_SCHEMA_IMPORTS_NOT_SUPPORTED, "testImportsValidation");
251   - }
252   -
253   - @Test
254   - public void testDynamicSchemaExtendDeclarationsValidation() {
255   - processValidation("[Transport Configuration] invalid testExtendDeclarationsValidation schema provided! Schema extend declarations don't support!", INVALID_PROTO_SCHEMA_EXTEND_DECLARATION_NOT_SUPPORTED, "testExtendDeclarationsValidation");
256   - }
257   -
258   - @Test
259   - public void testDynamicSchemaEnumOptionsValidation() {
260   - processValidation("[Transport Configuration] invalid testEnumOptionsValidation schema provided! Enum definitions options are not supported!", INVALID_PROTO_SCHEMA_ENUM_OPTIONS_NOT_SUPPORTED, "testEnumOptionsValidation");
261   - }
262   -
263   - @Test
264   - public void testDynamicSchemaNoOneMessageTypeExistsValidation() {
265   - processValidation("[Transport Configuration] invalid noOneMessageTypeExists schema provided! At least one Message definition should exists!", INVALID_PROTO_SCHEMA_NO_MESSAGE_TYPES_EXISTS, "noOneMessageTypeExists");
266   - }
267   -
268   - @Test
269   - public void testDynamicSchemaMessageTypeOptionsValidation() {
270   - processValidation("[Transport Configuration] invalid messageTypeOptions schema provided! Message definition options don't support!", INVALID_PROTO_SCHEMA_MESSAGE_OPTIONS_NOT_SUPPORTED, "messageTypeOptions");
271   - }
272   -
273   - @Test
274   - public void testDynamicSchemaMessageTypeExtensionsValidation() {
275   - processValidation("[Transport Configuration] invalid messageTypeExtensions schema provided! Message definition extensions don't support!", INVALID_PROTO_SCHEMA_MESSAGE_EXTENSIONS_NOT_SUPPORTED, "messageTypeExtensions");
276   - }
277   -
278   - @Test
279   - public void testDynamicSchemaMessageTypeReservedElementsValidation() {
280   - processValidation("[Transport Configuration] invalid messageTypeReservedElements schema provided! Message definition reserved elements don't support!", INVALID_PROTO_SCHEMA_MESSAGE_RESERVED_NOT_SUPPORTED, "messageTypeReservedElements");
281   - }
282   -
283   - @Test
284   - public void testDynamicSchemaMessageTypeGroupsElementsValidation() {
285   - processValidation("[Transport Configuration] invalid messageTypeGroupsElements schema provided! Message definition groups don't support!", INVALID_PROTO_SCHEMA_MESSAGE_GROUPS_NOT_SUPPORTED, "messageTypeGroupsElements");
286   - }
287   -
288   - @Test
289   - public void testDynamicSchemaOneOfsTypeGroupsElementsValidation() {
290   - processValidation("[Transport Configuration] invalid oneOfsTypeGroupsElements schema provided! OneOf definition groups don't support!", INVALID_PROTO_SCHEMA_ONE_OFS_GROUPS_NOT_SUPPORTED, "oneOfsTypeGroupsElements");
291   - }
292   -
293   - private void processValidation(String expectedMessage, String schema, String schemaName) {
294   - exceptionRule.expect(IllegalArgumentException.class);
295   - exceptionRule.expectMessage(expectedMessage);
296   - validateTransportProtoSchema(schema, schemaName);
297   - }
298   -
299   - @Test
300   - public void testDynamicSchemaCreationWithMessageNestedTypes() throws Exception {
301   - String testNestedTypesProtoSchema = "testNestedTypesProtoSchema";
302   - validateTransportProtoSchema(PROTO_SCHEMA_WITH_NESTED_MSG_TYPES, testNestedTypesProtoSchema);
303   - DynamicSchema dynamicSchema = getDynamicSchema(PROTO_SCHEMA_WITH_NESTED_MSG_TYPES, testNestedTypesProtoSchema);
304   - assertNotNull(dynamicSchema);
305   - Set<String> messageTypes = dynamicSchema.getMessageTypes();
306   - assertEquals(5, messageTypes.size());
307   - assertTrue(messageTypes.contains("testnested.Outer"));
308   - assertTrue(messageTypes.contains("testnested.Outer.MiddleAA"));
309   - assertTrue(messageTypes.contains("testnested.Outer.MiddleAA.Inner"));
310   - assertTrue(messageTypes.contains("testnested.Outer.MiddleBB"));
311   - assertTrue(messageTypes.contains("testnested.Outer.MiddleBB.Inner"));
312   -
313   - DynamicMessage.Builder middleAAInnerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA.Inner");
314   - Descriptors.Descriptor middleAAInnerMsgDescriptor = middleAAInnerMsgBuilder.getDescriptorForType();
315   - DynamicMessage middleAAInnerMsg = middleAAInnerMsgBuilder
316   - .setField(middleAAInnerMsgDescriptor.findFieldByName("ival"), 1L)
317   - .setField(middleAAInnerMsgDescriptor.findFieldByName("booly"), true)
318   - .build();
319   -
320   - DynamicMessage.Builder middleAAMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA");
321   - Descriptors.Descriptor middleAAMsgDescriptor = middleAAMsgBuilder.getDescriptorForType();
322   - DynamicMessage middleAAMsg = middleAAMsgBuilder
323   - .setField(middleAAMsgDescriptor.findFieldByName("inner"), middleAAInnerMsg)
324   - .build();
325   -
326   - DynamicMessage.Builder middleBBInnerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA.Inner");
327   - Descriptors.Descriptor middleBBInnerMsgDescriptor = middleBBInnerMsgBuilder.getDescriptorForType();
328   - DynamicMessage middleBBInnerMsg = middleBBInnerMsgBuilder
329   - .setField(middleBBInnerMsgDescriptor.findFieldByName("ival"), 0L)
330   - .setField(middleBBInnerMsgDescriptor.findFieldByName("booly"), false)
331   - .build();
332   -
333   - DynamicMessage.Builder middleBBMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleBB");
334   - Descriptors.Descriptor middleBBMsgDescriptor = middleBBMsgBuilder.getDescriptorForType();
335   - DynamicMessage middleBBMsg = middleBBMsgBuilder
336   - .setField(middleBBMsgDescriptor.findFieldByName("inner"), middleBBInnerMsg)
337   - .build();
338   -
339   -
340   - DynamicMessage.Builder outerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer");
341   - Descriptors.Descriptor outerMsgBuilderDescriptor = outerMsgBuilder.getDescriptorForType();
342   - DynamicMessage outerMsg = outerMsgBuilder
343   - .setField(outerMsgBuilderDescriptor.findFieldByName("middleAA"), middleAAMsg)
344   - .setField(outerMsgBuilderDescriptor.findFieldByName("middleBB"), middleBBMsg)
345   - .build();
346   -
347   - assertEquals("{\n" +
348   - " \"middleAA\": {\n" +
349   - " \"inner\": {\n" +
350   - " \"ival\": \"1\",\n" +
351   - " \"booly\": true\n" +
352   - " }\n" +
353   - " },\n" +
354   - " \"middleBB\": {\n" +
355   - " \"inner\": {\n" +
356   - " \"ival\": 0,\n" +
357   - " \"booly\": false\n" +
358   - " }\n" +
359   - " }\n" +
360   - "}", dynamicMsgToJson(outerMsgBuilderDescriptor, outerMsg.toByteArray()));
361   - }
362   -
363   - @Test
364   - public void testDynamicSchemaCreationWithMessageOneOfs() throws Exception {
365   - String testOneOfsProtoSchema = "testOneOfsProtoSchema";
366   - validateTransportProtoSchema(PROTO_SCHEMA_WITH_ONE_OFS, testOneOfsProtoSchema);
367   - DynamicSchema dynamicSchema = getDynamicSchema(PROTO_SCHEMA_WITH_ONE_OFS, testOneOfsProtoSchema);
368   - assertNotNull(dynamicSchema);
369   - Set<String> messageTypes = dynamicSchema.getMessageTypes();
370   - assertEquals(2, messageTypes.size());
371   - assertTrue(messageTypes.contains("testoneofs.SubMessage"));
372   - assertTrue(messageTypes.contains("testoneofs.SampleMessage"));
373   -
374   - DynamicMessage.Builder sampleMsgBuilder = dynamicSchema.newMessageBuilder("testoneofs.SampleMessage");
375   - Descriptors.Descriptor sampleMsgDescriptor = sampleMsgBuilder.getDescriptorForType();
376   - assertNotNull(sampleMsgDescriptor);
377   -
378   - List<Descriptors.FieldDescriptor> fields = sampleMsgDescriptor.getFields();
379   - assertEquals(2, fields.size());
380   - DynamicMessage sampleMsg = sampleMsgBuilder
381   - .setField(sampleMsgDescriptor.findFieldByName("name"), "Bob")
382   - .build();
383   - assertEquals("{\n" + " \"name\": \"Bob\"\n" + "}", dynamicMsgToJson(sampleMsgDescriptor, sampleMsg.toByteArray()));
384   -
385   - DynamicMessage.Builder subMsgBuilder = dynamicSchema.newMessageBuilder("testoneofs.SubMessage");
386   - Descriptors.Descriptor subMsgDescriptor = subMsgBuilder.getDescriptorForType();
387   - DynamicMessage subMsg = subMsgBuilder
388   - .addRepeatedField(subMsgDescriptor.findFieldByName("name"), "Alice")
389   - .addRepeatedField(subMsgDescriptor.findFieldByName("name"), "John")
390   - .build();
391   -
392   - DynamicMessage sampleMsgWithOneOfSubMessage = sampleMsgBuilder.setField(sampleMsgDescriptor.findFieldByName("subMessage"), subMsg).build();
393   - assertEquals("{\n" + " \"subMessage\": {\n" + " \"name\": [\"Alice\", \"John\"]\n" + " }\n" + "}",
394   - dynamicMsgToJson(sampleMsgDescriptor, sampleMsgWithOneOfSubMessage.toByteArray()));
395   -
396   - }
397   -
398   - private String dynamicMsgToJson(Descriptors.Descriptor descriptor, byte[] payload) throws InvalidProtocolBufferException {
399   - DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, payload);
400   - return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage);
401   - }
402   -
403   - private static ProtoFileElement getTransportProtoSchema(String protoSchema) {
404   - return new ProtoParser(LOCATION, protoSchema.toCharArray()).readProtoFile();
405   - }
406   -}
... ... @@ -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;
... ... @@ -35,7 +45,8 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTra
35 45 import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
36 46 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
37 47 import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
38   -import org.thingsboard.server.common.data.device.profile.MqttProtoDeviceProfileTransportConfiguration;
  48 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  49 +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
39 50 import org.thingsboard.server.common.data.id.DeviceProfileId;
40 51 import org.thingsboard.server.common.data.id.TenantId;
41 52 import org.thingsboard.server.common.data.page.PageData;
... ... @@ -49,6 +60,8 @@ import org.thingsboard.server.dao.tenant.TenantDao;
49 60
50 61 import java.util.Arrays;
51 62 import java.util.Collections;
  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
... ... @@ -312,13 +333,16 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
312 333 }
313 334 } else {
314 335 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
315   - if (transportConfiguration instanceof MqttProtoDeviceProfileTransportConfiguration) {
316   - MqttProtoDeviceProfileTransportConfiguration protoTransportConfiguration = (MqttProtoDeviceProfileTransportConfiguration) transportConfiguration;
317   - try {
318   - protoTransportConfiguration.validateTransportProtoSchema(protoTransportConfiguration.getDeviceAttributesProtoSchema(), MqttProtoDeviceProfileTransportConfiguration.ATTRIBUTES_PROTO_SCHEMA);
319   - protoTransportConfiguration.validateTransportProtoSchema(protoTransportConfiguration.getDeviceTelemetryProtoSchema(), MqttProtoDeviceProfileTransportConfiguration.TELEMETRY_PROTO_SCHEMA);
320   - } catch (Exception exception) {
321   - throw new DataValidationException(exception.getMessage());
  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 + }
322 346 }
323 347 }
324 348 }
... ... @@ -345,6 +369,121 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
345 369 }
346 370 }
347 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 +
348 487 };
349 488
350 489 private PaginatedRemover<TenantId, DeviceProfile> tenantDeviceProfilesRemover =
... ...
... ... @@ -57,40 +57,42 @@
57 57 <div class="tb-hint" innerHTML="{{ 'device-profile.multi-level-wildcards-hint' | translate }}"></div>
58 58 </div>
59 59 </fieldset>
60   - <fieldset class="fields-group">
61   - <legend class="group-title" translate>device-profile.mqtt-device-payload-type</legend>
62   - <div fxLayoutGap="8px" fxLayout="column">
63   - <mat-form-field class="mat-block">
64   - <mat-select formControlName="transportPayloadType" required>
65   - <mat-option *ngFor="let type of mqttTransportPayloadTypes" [value]="type">
66   - {{mqttTransportPayloadTypeTranslations.get(type) | translate}}
67   - </mat-option>
68   - </mat-select>
69   - <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadType').hasError('required')">
70   - {{ 'device-profile.mqtt-payload-type-required' | translate }}
71   - </mat-error>
72   - </mat-form-field>
73   - <div *ngIf="protoPayloadType()" fxLayout="column">
74   - <mat-form-field fxFlex>
75   - <mat-label translate>device-profile.telemetry-proto-schema</mat-label>
76   - <textarea matInput required
77   - formControlName="deviceTelemetryProtoSchema"
78   - rows="5"></textarea>
79   - <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.deviceTelemetryProtoSchema').hasError('required')">
80   - {{ 'device-profile.telemetry-proto-schema-required' | translate}}
81   - </mat-error>
82   - </mat-form-field>
83   - <mat-form-field fxFlex>
84   - <mat-label translate>device-profile.attributes-proto-schema</mat-label>
85   - <textarea matInput required
86   - formControlName="deviceAttributesProtoSchema"
87   - rows="5"></textarea>
88   - <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.deviceAttributesProtoSchema').hasError('required')">
89   - {{ 'device-profile.attributes-proto-schema-required' | translate}}
  60 + <section formGroupName="transportPayloadTypeConfiguration">
  61 + <fieldset class="fields-group">
  62 + <legend class="group-title" translate>device-profile.mqtt-device-payload-type</legend>
  63 + <div fxLayoutGap="8px" fxLayout="column">
  64 + <mat-form-field class="mat-block">
  65 + <mat-select formControlName="transportPayloadType" required>
  66 + <mat-option *ngFor="let type of mqttTransportPayloadTypes" [value]="type">
  67 + {{mqttTransportPayloadTypeTranslations.get(type) | translate}}
  68 + </mat-option>
  69 + </mat-select>
  70 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').hasError('required')">
  71 + {{ 'device-profile.mqtt-payload-type-required' | translate }}
90 72 </mat-error>
91 73 </mat-form-field>
  74 + <div *ngIf="protoPayloadType" fxLayout="column">
  75 + <mat-form-field fxFlex>
  76 + <mat-label translate>device-profile.telemetry-proto-schema</mat-label>
  77 + <textarea matInput required
  78 + formControlName="deviceTelemetryProtoSchema"
  79 + rows="5"></textarea>
  80 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.deviceTelemetryProtoSchema').hasError('required')">
  81 + {{ 'device-profile.telemetry-proto-schema-required' | translate}}
  82 + </mat-error>
  83 + </mat-form-field>
  84 + <mat-form-field fxFlex>
  85 + <mat-label translate>device-profile.attributes-proto-schema</mat-label>
  86 + <textarea matInput required
  87 + formControlName="deviceAttributesProtoSchema"
  88 + rows="5"></textarea>
  89 + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.deviceAttributesProtoSchema').hasError('required')">
  90 + {{ 'device-profile.attributes-proto-schema-required' | translate}}
  91 + </mat-error>
  92 + </mat-form-field>
  93 + </div>
92 94 </div>
93   - </div>
94   - </fieldset>
  95 + </fieldset>
  96 + </section>
95 97 </section>
96 98 </form>
... ...
... ... @@ -87,13 +87,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
87 87 configuration: this.fb.group({
88 88 deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]],
89 89 deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]],
90   - transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
  90 + transportPayloadTypeConfiguration: this.fb.group({
  91 + transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required]
  92 + })
91 93 })
92 94 });
93   - let configurationFormGroup = this.mqttDeviceProfileTransportConfigurationFormGroup.controls.configuration as FormGroup;
94   - configurationFormGroup.get('transportPayloadType').valueChanges.subscribe(payloadType => {
95   - this.updateTransportPayloadBasedControls(payloadType, configurationFormGroup);
96   - this.mqttDeviceProfileTransportConfigurationFormGroup.updateValueAndValidity();
  95 + this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').valueChanges.subscribe(payloadType => {
  96 + this.updateTransportPayloadBasedControls(payloadType);
  97 + this.mqttDeviceProfileTransportConfigurationFormGroup.updateValueAndValidity();
97 98 });
98 99 this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
99 100 this.updateModel();
... ... @@ -109,15 +110,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
109 110 }
110 111 }
111 112
112   - protoPayloadType(): boolean {
113   - let configuration = this.mqttDeviceProfileTransportConfigurationFormGroup.getRawValue().configuration;
114   - return configuration.transportPayloadType === MqttTransportPayloadType.PROTOBUF;
  113 + get protoPayloadType(): boolean {
  114 + let transportPayloadType = this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration.transportPayloadType').value;
  115 + return transportPayloadType === MqttTransportPayloadType.PROTOBUF;
115 116 }
116 117
117 118 writeValue(value: MqttDeviceProfileTransportConfiguration | null): void {
118 119 if (isDefinedAndNotNull(value)) {
119   - let configurationFormGroup = this.mqttDeviceProfileTransportConfigurationFormGroup.controls.configuration as FormGroup;
120   - this.updateTransportPayloadBasedControls(value.transportPayloadType, configurationFormGroup);
  120 + this.updateTransportPayloadBasedControls(value.transportPayloadTypeConfiguration.transportPayloadType);
121 121 this.mqttDeviceProfileTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
122 122 }
123 123 }
... ... @@ -131,13 +131,14 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
131 131 this.propagateChange(configuration);
132 132 }
133 133
134   - private updateTransportPayloadBasedControls(type: MqttTransportPayloadType, configurationFormGroup: FormGroup) {
  134 + private updateTransportPayloadBasedControls(type: MqttTransportPayloadType) {
  135 + const transportPayloadTypeConfigurationFormGroup = this.mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadTypeConfiguration') as FormGroup;
135 136 if (type === MqttTransportPayloadType.PROTOBUF) {
136   - configurationFormGroup.registerControl('deviceTelemetryProtoSchema', this.fb.control(null, Validators.required));
137   - configurationFormGroup.registerControl('deviceAttributesProtoSchema', this.fb.control(null, Validators.required));
  137 + transportPayloadTypeConfigurationFormGroup.registerControl('deviceTelemetryProtoSchema', this.fb.control(null, Validators.required));
  138 + transportPayloadTypeConfigurationFormGroup.registerControl('deviceAttributesProtoSchema', this.fb.control(null, Validators.required));
138 139 } else {
139   - configurationFormGroup.removeControl('deviceTelemetryProtoSchema');
140   - configurationFormGroup.removeControl('deviceAttributesProtoSchema');
  140 + transportPayloadTypeConfigurationFormGroup.removeControl('deviceTelemetryProtoSchema');
  141 + transportPayloadTypeConfigurationFormGroup.removeControl('deviceAttributesProtoSchema');
141 142 }
142 143 }
143 144
... ...
... ... @@ -148,7 +148,9 @@ export interface DefaultDeviceProfileTransportConfiguration {
148 148 export interface MqttDeviceProfileTransportConfiguration {
149 149 deviceTelemetryTopic?: string;
150 150 deviceAttributesTopic?: string;
151   - transportPayloadType?: MqttTransportPayloadType;
  151 + transportPayloadTypeConfiguration?: {
  152 + transportPayloadType?: MqttTransportPayloadType;
  153 + };
152 154 [key: string]: any;
153 155 }
154 156
... ... @@ -208,7 +210,7 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
208 210 const mqttTransportConfiguration: MqttDeviceProfileTransportConfiguration = {
209 211 deviceTelemetryTopic: 'v1/devices/me/telemetry',
210 212 deviceAttributesTopic: 'v1/devices/me/attributes',
211   - transportPayloadType: MqttTransportPayloadType.JSON
  213 + transportPayloadTypeConfiguration: {transportPayloadType: MqttTransportPayloadType.JSON}
212 214 };
213 215 transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT};
214 216 break;
... ...