Commit 4a1878130493753063101c8d03ec1b13ca015dbe
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() { | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTransportConfigurationDeserializer.java
deleted
100644 → 0
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 | } | ... | ... |
common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/util/MqttDynamicProtoSchemaTest.java
deleted
100644 → 0
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; | ... | ... |