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