Commit c4133fa41a3a6519ef6c56b07c7ec4a4820ea98b
Committed by
Andrew Shvayka
1 parent
a1336f38
added new dynamic-schema based tests for telemetry and attributes upload
Showing
16 changed files
with
678 additions
and
101 deletions
... | ... | @@ -634,6 +634,72 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
634 | 634 | dynamicMsgToJson(sampleMsgDescriptor, sampleMsgWithOneOfSubMessage.toByteArray())); |
635 | 635 | } |
636 | 636 | |
637 | + @Test | |
638 | + public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsField() throws Exception { | |
639 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + | |
640 | + "\n" + | |
641 | + "package schemavalidation;\n" + | |
642 | + "\n" + | |
643 | + "message PostTelemetry {\n" + | |
644 | + " int64 ts = 1;\n" + | |
645 | + " Values values = 2;\n" + | |
646 | + " \n" + | |
647 | + " message Values {\n" + | |
648 | + " string key1 = 3;\n" + | |
649 | + " bool key2 = 4;\n" + | |
650 | + " double key3 = 5;\n" + | |
651 | + " int32 key4 = 6;\n" + | |
652 | + " JsonObject key5 = 7;\n" + | |
653 | + " }\n" + | |
654 | + " \n" + | |
655 | + " message JsonObject {\n" + | |
656 | + " optional int32 someNumber = 8;\n" + | |
657 | + " repeated int32 someArray = 9;\n" + | |
658 | + " NestedJsonObject someNestedObject = 10;\n" + | |
659 | + " message NestedJsonObject {\n" + | |
660 | + " optional string key = 11;\n" + | |
661 | + " }\n" + | |
662 | + " }\n" + | |
663 | + "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); | |
664 | + } | |
665 | + | |
666 | + @Test | |
667 | + public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsDateType() throws Exception { | |
668 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + | |
669 | + "\n" + | |
670 | + "package schemavalidation;\n" + | |
671 | + "\n" + | |
672 | + "message PostTelemetry {\n" + | |
673 | + " optional int32 ts = 1;\n" + | |
674 | + " Values values = 2;\n" + | |
675 | + " \n" + | |
676 | + " message Values {\n" + | |
677 | + " string key1 = 3;\n" + | |
678 | + " bool key2 = 4;\n" + | |
679 | + " double key3 = 5;\n" + | |
680 | + " int32 key4 = 6;\n" + | |
681 | + " JsonObject key5 = 7;\n" + | |
682 | + " }\n" + | |
683 | + " \n" + | |
684 | + " message JsonObject {\n" + | |
685 | + " optional int32 someNumber = 8;\n" + | |
686 | + " }\n" + | |
687 | + "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid data type. Only int64 type is supported!"); | |
688 | + } | |
689 | + | |
690 | + @Test | |
691 | + public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaValuesDateType() throws Exception { | |
692 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + | |
693 | + "\n" + | |
694 | + "package schemavalidation;\n" + | |
695 | + "\n" + | |
696 | + "message PostTelemetry {\n" + | |
697 | + " optional int64 ts = 1;\n" + | |
698 | + " string values = 2;\n" + | |
699 | + " \n" + | |
700 | + "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'values' has invalid data type. Only message type is supported!"); | |
701 | + } | |
702 | + | |
637 | 703 | private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception { |
638 | 704 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema); |
639 | 705 | MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration); | ... | ... |
... | ... | @@ -42,18 +42,18 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle |
42 | 42 | "package test;\n" + |
43 | 43 | "\n" + |
44 | 44 | "message PostTelemetry {\n" + |
45 | - " string key1 = 1;\n" + | |
46 | - " bool key2 = 2;\n" + | |
47 | - " double key3 = 3;\n" + | |
48 | - " int32 key4 = 4;\n" + | |
45 | + " optional string key1 = 1;\n" + | |
46 | + " optional bool key2 = 2;\n" + | |
47 | + " optional double key3 = 3;\n" + | |
48 | + " optional int32 key4 = 4;\n" + | |
49 | 49 | " JsonObject key5 = 5;\n" + |
50 | 50 | "\n" + |
51 | 51 | " message JsonObject {\n" + |
52 | - " int32 someNumber = 6;\n" + | |
52 | + " optional int32 someNumber = 6;\n" + | |
53 | 53 | " repeated int32 someArray = 7;\n" + |
54 | - " NestedJsonObject someNestedObject = 8;\n" + | |
54 | + " optional NestedJsonObject someNestedObject = 8;\n" + | |
55 | 55 | " message NestedJsonObject {\n" + |
56 | - " string key = 9;\n" + | |
56 | + " optional string key = 9;\n" + | |
57 | 57 | " }\n" + |
58 | 58 | " }\n" + |
59 | 59 | "}"; |
... | ... | @@ -63,18 +63,18 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle |
63 | 63 | "package test;\n" + |
64 | 64 | "\n" + |
65 | 65 | "message PostAttributes {\n" + |
66 | - " string key1 = 1;\n" + | |
67 | - " bool key2 = 2;\n" + | |
68 | - " double key3 = 3;\n" + | |
69 | - " int32 key4 = 4;\n" + | |
66 | + " optional string key1 = 1;\n" + | |
67 | + " optional bool key2 = 2;\n" + | |
68 | + " optional double key3 = 3;\n" + | |
69 | + " optional int32 key4 = 4;\n" + | |
70 | 70 | " JsonObject key5 = 5;\n" + |
71 | 71 | "\n" + |
72 | 72 | " message JsonObject {\n" + |
73 | - " int32 someNumber = 6;\n" + | |
73 | + " optional int32 someNumber = 6;\n" + | |
74 | 74 | " repeated int32 someArray = 7;\n" + |
75 | 75 | " NestedJsonObject someNestedObject = 8;\n" + |
76 | 76 | " message NestedJsonObject {\n" + |
77 | - " string key = 9;\n" + | |
77 | + " optional string key = 9;\n" + | |
78 | 78 | " }\n" + |
79 | 79 | " }\n" + |
80 | 80 | "}"; |
... | ... | @@ -83,16 +83,16 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle |
83 | 83 | "package rpc;\n" + |
84 | 84 | "\n" + |
85 | 85 | "message RpcResponseMsg {\n" + |
86 | - " string payload = 1;\n" + | |
86 | + " optional string payload = 1;\n" + | |
87 | 87 | "}"; |
88 | 88 | |
89 | 89 | protected static final String DEVICE_RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" + |
90 | 90 | "package rpc;\n" + |
91 | 91 | "\n" + |
92 | 92 | "message RpcRequestMsg {\n" + |
93 | - " string method = 1;\n" + | |
94 | - " int32 requestId = 2;\n" + | |
95 | - " string params = 3;\n" + | |
93 | + " optional string method = 1;\n" + | |
94 | + " optional int32 requestId = 2;\n" + | |
95 | + " optional string params = 3;\n" + | |
96 | 96 | "}"; |
97 | 97 | |
98 | 98 | protected Tenant savedTenant; | ... | ... |
... | ... | @@ -61,11 +61,14 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap |
61 | 61 | @Test |
62 | 62 | public void testPushAttributes() throws Exception { |
63 | 63 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
64 | - processAttributesTest(expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | |
64 | + processJsonPayloadAttributesTest(expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | |
65 | 65 | } |
66 | 66 | |
67 | - protected void processAttributesTest(List<String> expectedKeys, byte[] payload) throws Exception { | |
68 | - log.warn("[testPushAttributes] Device: {}, Transport type: {}", savedDevice.getName(), savedDevice.getType()); | |
67 | + protected void processJsonPayloadAttributesTest(List<String> expectedKeys, byte[] payload) throws Exception { | |
68 | + processAttributesTest(expectedKeys, payload, false); | |
69 | + } | |
70 | + | |
71 | + protected void processAttributesTest(List<String> expectedKeys, byte[] payload, boolean presenceFieldsTest) throws Exception { | |
69 | 72 | CoapClient client = getCoapClient(FeatureType.ATTRIBUTES); |
70 | 73 | |
71 | 74 | postAttributes(client, payload); |
... | ... | @@ -94,7 +97,11 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap |
94 | 97 | |
95 | 98 | String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); |
96 | 99 | List<Map<String, Object>> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {}); |
97 | - assertAttributesValues(values, expectedKeySet); | |
100 | + if (presenceFieldsTest) { | |
101 | + assertAttributesProtoValues(values, actualKeySet); | |
102 | + } else { | |
103 | + assertAttributesValues(values, actualKeySet); | |
104 | + } | |
98 | 105 | String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); |
99 | 106 | doDelete(deleteAttributesUrl); |
100 | 107 | } |
... | ... | @@ -108,11 +115,11 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap |
108 | 115 | } |
109 | 116 | |
110 | 117 | @SuppressWarnings("unchecked") |
111 | - protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException { | |
118 | + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> keySet) throws JsonProcessingException { | |
112 | 119 | for (Map<String, Object> map : deviceValues) { |
113 | 120 | String key = (String) map.get("key"); |
114 | 121 | Object value = map.get("value"); |
115 | - assertTrue(expectedKeySet.contains(key)); | |
122 | + assertTrue(keySet.contains(key)); | |
116 | 123 | switch (key) { |
117 | 124 | case "key1": |
118 | 125 | assertEquals("value1", value); |
... | ... | @@ -138,6 +145,35 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap |
138 | 145 | } |
139 | 146 | } |
140 | 147 | |
148 | + private void assertAttributesProtoValues(List<Map<String, Object>> values, Set<String> keySet) { | |
149 | + for (Map<String, Object> map : values) { | |
150 | + String key = (String) map.get("key"); | |
151 | + Object value = map.get("value"); | |
152 | + assertTrue(keySet.contains(key)); | |
153 | + switch (key) { | |
154 | + case "key1": | |
155 | + assertEquals("", value); | |
156 | + break; | |
157 | + case "key2": | |
158 | + assertEquals(false, value); | |
159 | + break; | |
160 | + case "key3": | |
161 | + assertEquals(0.0, value); | |
162 | + break; | |
163 | + case "key4": | |
164 | + assertEquals(0, value); | |
165 | + break; | |
166 | + case "key5": | |
167 | + assertNotNull(value); | |
168 | + assertEquals(2, ((LinkedHashMap) value).size()); | |
169 | + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); | |
170 | + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); | |
171 | + assertEquals("value", someNestedObject.get("key")); | |
172 | + break; | |
173 | + } | |
174 | + } | |
175 | + } | |
176 | + | |
141 | 177 | private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { |
142 | 178 | return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); |
143 | 179 | } | ... | ... |
... | ... | @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo |
32 | 32 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
33 | 33 | |
34 | 34 | import java.util.Arrays; |
35 | -import java.util.List; | |
36 | 35 | |
37 | 36 | import static org.junit.Assert.assertNotNull; |
38 | 37 | import static org.junit.Assert.assertTrue; |
... | ... | @@ -48,7 +47,6 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac |
48 | 47 | @Test |
49 | 48 | public void testPushAttributes() throws Exception { |
50 | 49 | super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); |
51 | - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | |
52 | 50 | DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); |
53 | 51 | assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); |
54 | 52 | CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; |
... | ... | @@ -87,7 +85,50 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac |
87 | 85 | .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) |
88 | 86 | .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) |
89 | 87 | .build(); |
90 | - processAttributesTest(expectedKeys, postAttributesMsg.toByteArray()); | |
88 | + processAttributesTest(Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false); | |
89 | + } | |
90 | + | |
91 | + @Test | |
92 | + public void testPushAttributesWithExplicitPresenceProtoKeys() throws Exception { | |
93 | + super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); | |
94 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
95 | + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); | |
96 | + CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; | |
97 | + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapTransportConfiguration.getCoapDeviceTypeConfiguration(); | |
98 | + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); | |
99 | + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; | |
100 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); | |
101 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
102 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
103 | + ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA); | |
104 | + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | |
105 | + | |
106 | + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); | |
107 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
108 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
109 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
110 | + | |
111 | + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); | |
112 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
113 | + assertNotNull(jsonObjectBuilderDescriptor); | |
114 | + DynamicMessage jsonObject = jsonObjectBuilder | |
115 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
116 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
117 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
118 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
119 | + .build(); | |
120 | + | |
121 | + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); | |
122 | + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); | |
123 | + assertNotNull(postAttributesMsgDescriptor); | |
124 | + DynamicMessage postAttributesMsg = postAttributesBuilder | |
125 | + .setField(postAttributesMsgDescriptor.findFieldByName("key1"), "") | |
126 | + .setField(postAttributesMsgDescriptor.findFieldByName("key2"), false) | |
127 | + .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 0.0) | |
128 | + .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 0) | |
129 | + .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) | |
130 | + .build(); | |
131 | + processAttributesTest(Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), true); | |
91 | 132 | } |
92 | 133 | |
93 | 134 | } | ... | ... |
... | ... | @@ -56,18 +56,21 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap |
56 | 56 | |
57 | 57 | @Test |
58 | 58 | public void testPushTelemetry() throws Exception { |
59 | - processTestPostTelemetry(null, false); | |
59 | + processJsonPayloadTelemetryTest(PAYLOAD_VALUES_STR.getBytes(), false); | |
60 | 60 | } |
61 | 61 | |
62 | 62 | @Test |
63 | 63 | public void testPushTelemetryWithTs() throws Exception { |
64 | 64 | String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; |
65 | - processTestPostTelemetry(payloadStr.getBytes(), true); | |
65 | + processJsonPayloadTelemetryTest(payloadStr.getBytes(), true); | |
66 | 66 | } |
67 | 67 | |
68 | - protected void processTestPostTelemetry(byte[] payloadBytes, boolean withTs) throws Exception { | |
69 | - log.warn("[testPushTelemetry] Device: {}, Transport type: {}", savedDevice.getName(), savedDevice.getType()); | |
68 | + protected void processJsonPayloadTelemetryTest(byte[] payloadBytes, boolean withTs) throws Exception { | |
70 | 69 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
70 | + processTestPostTelemetry(payloadBytes, expectedKeys, withTs, false); | |
71 | + } | |
72 | + | |
73 | + protected void processTestPostTelemetry(byte[] payloadBytes, List<String> expectedKeys, boolean withTs, boolean presenceFieldsTest) throws Exception { | |
71 | 74 | CoapClient coapClient = getCoapClient(FeatureType.TELEMETRY); |
72 | 75 | postTelemetry(coapClient, payloadBytes); |
73 | 76 | |
... | ... | @@ -127,16 +130,22 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap |
127 | 130 | } |
128 | 131 | assertNotNull(values); |
129 | 132 | |
130 | - if (withTs) { | |
131 | - assertTs(values, expectedKeys, 10000, 0); | |
133 | + if (presenceFieldsTest) { | |
134 | + if (withTs) { | |
135 | + assertTsForExplicitProtoFieldValues(values, expectedKeys, 10000, 0); | |
136 | + assertExplicitProtoFieldValuesWithTs(values); | |
137 | + } else { | |
138 | + assertExplicitProtoFieldValues(values); | |
139 | + } | |
140 | + } else { | |
141 | + if (withTs) { | |
142 | + assertTs(values, expectedKeys, 10000, 0); | |
143 | + } | |
144 | + assertValues(values, 0); | |
132 | 145 | } |
133 | - assertValues(values, 0); | |
134 | 146 | } |
135 | 147 | |
136 | 148 | private void postTelemetry(CoapClient client, byte[] payload) throws IOException, ConnectorException { |
137 | - if (payload == null) { | |
138 | - payload = PAYLOAD_VALUES_STR.getBytes(); | |
139 | - } | |
140 | 149 | CoapResponse coapResponse = client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); |
141 | 150 | assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); |
142 | 151 | } |
... | ... | @@ -174,5 +183,39 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap |
174 | 183 | } |
175 | 184 | } |
176 | 185 | |
186 | + private void assertExplicitProtoFieldValues(Map<String, List<Map<String, Object>>> deviceValues) { | |
187 | + for (Map.Entry<String, List<Map<String, Object>>> entry : deviceValues.entrySet()) { | |
188 | + String key = entry.getKey(); | |
189 | + List<Map<String, Object>> tsKv = entry.getValue(); | |
190 | + String value = (String) tsKv.get(0).get("value"); | |
191 | + switch (key) { | |
192 | + case "key1": | |
193 | + assertEquals("", value); | |
194 | + break; | |
195 | + case "key2": | |
196 | + assertEquals("false", value); | |
197 | + break; | |
198 | + case "key3": | |
199 | + assertEquals("0.0", value); | |
200 | + break; | |
201 | + case "key4": | |
202 | + assertEquals("0", value); | |
203 | + break; | |
204 | + case "key5": | |
205 | + assertEquals("{\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value); | |
206 | + break; | |
207 | + } | |
208 | + } | |
209 | + } | |
210 | + | |
211 | + private void assertExplicitProtoFieldValuesWithTs(Map<String, List<Map<String, Object>>> deviceValues) { | |
212 | + assertEquals(1, deviceValues.size()); | |
213 | + List<Map<String, Object>> tsKv = deviceValues.get("key5"); | |
214 | + assertEquals("{\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", tsKv.get(0).get("value")); | |
215 | + } | |
216 | + | |
217 | + private void assertTsForExplicitProtoFieldValues(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { | |
218 | + assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); | |
219 | + } | |
177 | 220 | |
178 | 221 | } | ... | ... |
... | ... | @@ -32,6 +32,9 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC |
32 | 32 | import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; |
33 | 33 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
34 | 34 | |
35 | +import java.util.Arrays; | |
36 | +import java.util.Collections; | |
37 | + | |
35 | 38 | import static org.junit.Assert.assertNotNull; |
36 | 39 | import static org.junit.Assert.assertTrue; |
37 | 40 | |
... | ... | @@ -84,7 +87,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac |
84 | 87 | .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) |
85 | 88 | .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) |
86 | 89 | .build(); |
87 | - processTestPostTelemetry(postTelemetryMsg.toByteArray(), false); | |
90 | + processTestPostTelemetry(postTelemetryMsg.toByteArray(), Arrays.asList("key1", "key2", "key3", "key4", "key5"), false, false); | |
88 | 91 | } |
89 | 92 | |
90 | 93 | @Test |
... | ... | @@ -94,23 +97,23 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac |
94 | 97 | "package test;\n" + |
95 | 98 | "\n" + |
96 | 99 | "message PostTelemetry {\n" + |
97 | - " int64 ts = 1;\n" + | |
100 | + " optional int64 ts = 1;\n" + | |
98 | 101 | " Values values = 2;\n" + |
99 | 102 | " \n" + |
100 | 103 | " message Values {\n" + |
101 | - " string key1 = 3;\n" + | |
102 | - " bool key2 = 4;\n" + | |
103 | - " double key3 = 5;\n" + | |
104 | - " int32 key4 = 6;\n" + | |
104 | + " optional string key1 = 3;\n" + | |
105 | + " optional bool key2 = 4;\n" + | |
106 | + " optional double key3 = 5;\n" + | |
107 | + " optional int32 key4 = 6;\n" + | |
105 | 108 | " JsonObject key5 = 7;\n" + |
106 | 109 | " }\n" + |
107 | 110 | " \n" + |
108 | 111 | " message JsonObject {\n" + |
109 | - " int32 someNumber = 8;\n" + | |
112 | + " optional int32 someNumber = 8;\n" + | |
110 | 113 | " repeated int32 someArray = 9;\n" + |
111 | 114 | " NestedJsonObject someNestedObject = 10;\n" + |
112 | 115 | " message NestedJsonObject {\n" + |
113 | - " string key = 11;\n" + | |
116 | + " optional string key = 11;\n" + | |
114 | 117 | " }\n" + |
115 | 118 | " }\n" + |
116 | 119 | "}"; |
... | ... | @@ -164,7 +167,126 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac |
164 | 167 | .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) |
165 | 168 | .build(); |
166 | 169 | |
167 | - processTestPostTelemetry(postTelemetryMsg.toByteArray(), true); | |
170 | + processTestPostTelemetry(postTelemetryMsg.toByteArray(), Arrays.asList("key1", "key2", "key3", "key4", "key5"), true, false); | |
171 | + } | |
172 | + | |
173 | + @Test | |
174 | + public void testPushTelemetryWithExplicitPresenceProtoKeys() throws Exception { | |
175 | + super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); | |
176 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
177 | + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); | |
178 | + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; | |
179 | + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); | |
180 | + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); | |
181 | + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; | |
182 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); | |
183 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
184 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
185 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA); | |
186 | + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | |
187 | + | |
188 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | |
189 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
190 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
191 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
192 | + | |
193 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | |
194 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
195 | + assertNotNull(jsonObjectBuilderDescriptor); | |
196 | + DynamicMessage jsonObject = jsonObjectBuilder | |
197 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
198 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
199 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
200 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
201 | + .build(); | |
202 | + | |
203 | + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | |
204 | + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | |
205 | + assertNotNull(postTelemetryMsgDescriptor); | |
206 | + DynamicMessage postTelemetryMsg = postTelemetryBuilder | |
207 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key1"), "") | |
208 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), false) | |
209 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 0.0) | |
210 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 0) | |
211 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) | |
212 | + .build(); | |
213 | + processTestPostTelemetry(postTelemetryMsg.toByteArray(), Arrays.asList("key1", "key2", "key3", "key4", "key5"), false, true); | |
214 | + } | |
215 | + | |
216 | + @Test | |
217 | + public void testPushTelemetryWithTsAndNoPresenceFields() throws Exception { | |
218 | + String schemaStr = "syntax =\"proto3\";\n" + | |
219 | + "\n" + | |
220 | + "package test;\n" + | |
221 | + "\n" + | |
222 | + "message PostTelemetry {\n" + | |
223 | + " optional int64 ts = 1;\n" + | |
224 | + " Values values = 2;\n" + | |
225 | + " \n" + | |
226 | + " message Values {\n" + | |
227 | + " string key1 = 3;\n" + | |
228 | + " bool key2 = 4;\n" + | |
229 | + " double key3 = 5;\n" + | |
230 | + " int32 key4 = 6;\n" + | |
231 | + " JsonObject key5 = 7;\n" + | |
232 | + " }\n" + | |
233 | + " \n" + | |
234 | + " message JsonObject {\n" + | |
235 | + " optional int32 someNumber = 8;\n" + | |
236 | + " repeated int32 someArray = 9;\n" + | |
237 | + " NestedJsonObject someNestedObject = 10;\n" + | |
238 | + " message NestedJsonObject {\n" + | |
239 | + " optional string key = 11;\n" + | |
240 | + " }\n" + | |
241 | + " }\n" + | |
242 | + "}"; | |
243 | + super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED); | |
244 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
245 | + assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); | |
246 | + CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; | |
247 | + CoapDeviceTypeConfiguration coapDeviceTypeConfiguration = coapDeviceProfileTransportConfiguration.getCoapDeviceTypeConfiguration(); | |
248 | + assertTrue(coapDeviceTypeConfiguration instanceof DefaultCoapDeviceTypeConfiguration); | |
249 | + DefaultCoapDeviceTypeConfiguration defaultCoapDeviceTypeConfiguration = (DefaultCoapDeviceTypeConfiguration) coapDeviceTypeConfiguration; | |
250 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = defaultCoapDeviceTypeConfiguration.getTransportPayloadTypeConfiguration(); | |
251 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
252 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
253 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr); | |
254 | + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | |
255 | + | |
256 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | |
257 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
258 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
259 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
260 | + | |
261 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | |
262 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
263 | + assertNotNull(jsonObjectBuilderDescriptor); | |
264 | + DynamicMessage jsonObject = jsonObjectBuilder | |
265 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
266 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
267 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
268 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
269 | + .build(); | |
270 | + | |
271 | + | |
272 | + DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values"); | |
273 | + Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType(); | |
274 | + assertNotNull(valuesDescriptor); | |
275 | + | |
276 | + DynamicMessage valuesMsg = valuesBuilder | |
277 | + .setField(valuesDescriptor.findFieldByName("key4"), 0) | |
278 | + .setField(valuesDescriptor.findFieldByName("key5"), jsonObject) | |
279 | + .build(); | |
280 | + | |
281 | + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | |
282 | + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | |
283 | + assertNotNull(postTelemetryMsgDescriptor); | |
284 | + DynamicMessage postTelemetryMsg = postTelemetryBuilder | |
285 | + .setField(postTelemetryMsgDescriptor.findFieldByName("ts"), 10000L) | |
286 | + .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) | |
287 | + .build(); | |
288 | + | |
289 | + processTestPostTelemetry(postTelemetryMsg.toByteArray(), Collections.singletonList("key5"), true, true); | |
168 | 290 | } |
169 | 291 | |
170 | 292 | } | ... | ... |
... | ... | @@ -55,13 +55,13 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
55 | 55 | } |
56 | 56 | |
57 | 57 | @Test |
58 | - public void testPushMqttAttributes() throws Exception { | |
58 | + public void testPushAttributes() throws Exception { | |
59 | 59 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
60 | - processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | |
60 | + processJsonPayloadAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | |
61 | 61 | } |
62 | 62 | |
63 | 63 | @Test |
64 | - public void testPushMqttAttributesGateway() throws Exception { | |
64 | + public void testPushAttributesGateway() throws Exception { | |
65 | 65 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
66 | 66 | String deviceName1 = "Device A"; |
67 | 67 | String deviceName2 = "Device B"; |
... | ... | @@ -69,7 +69,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
69 | 69 | processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); |
70 | 70 | } |
71 | 71 | |
72 | - protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload) throws Exception { | |
72 | + protected void processJsonPayloadAttributesTest(String topic, List<String> expectedKeys, byte[] payload) throws Exception { | |
73 | + processAttributesTest(topic, expectedKeys, payload, false); | |
74 | + } | |
75 | + | |
76 | + protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload, boolean presenceFieldsTest) throws Exception { | |
73 | 77 | MqttAsyncClient client = getMqttAsyncClient(accessToken); |
74 | 78 | |
75 | 79 | publishMqttMsg(client, payload, topic); |
... | ... | @@ -98,7 +102,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
98 | 102 | |
99 | 103 | String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); |
100 | 104 | List<Map<String, Object>> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {}); |
101 | - assertAttributesValues(values, expectedKeySet); | |
105 | + if (presenceFieldsTest) { | |
106 | + assertAttributesProtoValues(values, actualKeySet); | |
107 | + } else { | |
108 | + assertAttributesValues(values, actualKeySet); | |
109 | + } | |
102 | 110 | String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); |
103 | 111 | doDelete(deleteAttributesUrl); |
104 | 112 | } |
... | ... | @@ -145,11 +153,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
145 | 153 | } |
146 | 154 | |
147 | 155 | @SuppressWarnings("unchecked") |
148 | - protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException { | |
156 | + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> keySet) throws JsonProcessingException { | |
149 | 157 | for (Map<String, Object> map : deviceValues) { |
150 | 158 | String key = (String) map.get("key"); |
151 | 159 | Object value = map.get("value"); |
152 | - assertTrue(expectedKeySet.contains(key)); | |
160 | + assertTrue(keySet.contains(key)); | |
153 | 161 | switch (key) { |
154 | 162 | case "key1": |
155 | 163 | assertEquals("value1", value); |
... | ... | @@ -175,6 +183,35 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
175 | 183 | } |
176 | 184 | } |
177 | 185 | |
186 | + private void assertAttributesProtoValues(List<Map<String, Object>> values, Set<String> keySet) { | |
187 | + for (Map<String, Object> map : values) { | |
188 | + String key = (String) map.get("key"); | |
189 | + Object value = map.get("value"); | |
190 | + assertTrue(keySet.contains(key)); | |
191 | + switch (key) { | |
192 | + case "key1": | |
193 | + assertEquals("", value); | |
194 | + break; | |
195 | + case "key2": | |
196 | + assertEquals(false, value); | |
197 | + break; | |
198 | + case "key3": | |
199 | + assertEquals(0.0, value); | |
200 | + break; | |
201 | + case "key4": | |
202 | + assertEquals(0, value); | |
203 | + break; | |
204 | + case "key5": | |
205 | + assertNotNull(value); | |
206 | + assertEquals(2, ((LinkedHashMap) value).size()); | |
207 | + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); | |
208 | + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); | |
209 | + assertEquals("value", someNestedObject.get("key")); | |
210 | + break; | |
211 | + } | |
212 | + } | |
213 | + } | |
214 | + | |
178 | 215 | protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) { |
179 | 216 | return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}"; |
180 | 217 | } | ... | ... |
... | ... | @@ -40,13 +40,13 @@ public abstract class AbstractMqttAttributesJsonIntegrationTest extends Abstract |
40 | 40 | } |
41 | 41 | |
42 | 42 | @Test |
43 | - public void testPushMqttAttributes() throws Exception { | |
43 | + public void testPushAttributes() throws Exception { | |
44 | 44 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
45 | - processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | |
45 | + processJsonPayloadAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | |
46 | 46 | } |
47 | 47 | |
48 | 48 | @Test |
49 | - public void testPushMqttAttributesGateway() throws Exception { | |
49 | + public void testPushAttributesGateway() throws Exception { | |
50 | 50 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
51 | 51 | String deviceName1 = "Device A"; |
52 | 52 | String deviceName2 = "Device B"; | ... | ... |
... | ... | @@ -47,9 +47,8 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac |
47 | 47 | } |
48 | 48 | |
49 | 49 | @Test |
50 | - public void testPushMqttAttributes() throws Exception { | |
50 | + public void testPushAttributes() throws Exception { | |
51 | 51 | super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); |
52 | - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | |
53 | 52 | DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); |
54 | 53 | assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); |
55 | 54 | MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; |
... | ... | @@ -85,11 +84,51 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac |
85 | 84 | .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) |
86 | 85 | .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) |
87 | 86 | .build(); |
88 | - processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, postAttributesMsg.toByteArray()); | |
87 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false); | |
88 | + } | |
89 | + | |
90 | + @Test | |
91 | + public void testPushAttributesWithExplicitPresenceProtoKeys() throws Exception { | |
92 | + super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); | |
93 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
94 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
95 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
96 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
97 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
98 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
99 | + ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA); | |
100 | + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | |
101 | + | |
102 | + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); | |
103 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
104 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
105 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
106 | + | |
107 | + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); | |
108 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
109 | + assertNotNull(jsonObjectBuilderDescriptor); | |
110 | + DynamicMessage jsonObject = jsonObjectBuilder | |
111 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
112 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
113 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
114 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
115 | + .build(); | |
116 | + | |
117 | + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); | |
118 | + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); | |
119 | + assertNotNull(postAttributesMsgDescriptor); | |
120 | + DynamicMessage postAttributesMsg = postAttributesBuilder | |
121 | + .setField(postAttributesMsgDescriptor.findFieldByName("key1"), "") | |
122 | + .setField(postAttributesMsgDescriptor.findFieldByName("key2"), false) | |
123 | + .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 0.0) | |
124 | + .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 0) | |
125 | + .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) | |
126 | + .build(); | |
127 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), true); | |
89 | 128 | } |
90 | 129 | |
91 | 130 | @Test |
92 | - public void testPushMqttAttributesGateway() throws Exception { | |
131 | + public void testPushAttributesGateway() throws Exception { | |
93 | 132 | super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null); |
94 | 133 | TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); |
95 | 134 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ... | ... |
... | ... | @@ -61,20 +61,20 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt |
61 | 61 | } |
62 | 62 | |
63 | 63 | @Test |
64 | - public void testPushMqttTelemetry() throws Exception { | |
64 | + public void testPushTelemetry() throws Exception { | |
65 | 65 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
66 | - processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | |
66 | + processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | |
67 | 67 | } |
68 | 68 | |
69 | 69 | @Test |
70 | - public void testPushMqttTelemetryWithTs() throws Exception { | |
70 | + public void testPushTelemetryWithTs() throws Exception { | |
71 | 71 | String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; |
72 | 72 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
73 | - processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | |
73 | + processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | |
74 | 74 | } |
75 | 75 | |
76 | 76 | @Test |
77 | - public void testPushMqttTelemetryGateway() throws Exception { | |
77 | + public void testPushTelemetryGateway() throws Exception { | |
78 | 78 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
79 | 79 | String deviceName1 = "Device A"; |
80 | 80 | String deviceName2 = "Device B"; |
... | ... | @@ -97,7 +97,11 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt |
97 | 97 | assertNotNull(device); |
98 | 98 | } |
99 | 99 | |
100 | - protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception { | |
100 | + protected void processJsonPayloadTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception { | |
101 | + processTelemetryTest(topic, expectedKeys, payload, withTs, false); | |
102 | + } | |
103 | + | |
104 | + protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs, boolean presenceFieldsTest) throws Exception { | |
101 | 105 | MqttAsyncClient client = getMqttAsyncClient(accessToken); |
102 | 106 | publishMqttMsg(client, payload, topic); |
103 | 107 | |
... | ... | @@ -157,10 +161,19 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt |
157 | 161 | } |
158 | 162 | assertNotNull(values); |
159 | 163 | |
160 | - if (withTs) { | |
161 | - assertTs(values, expectedKeys, 10000, 0); | |
164 | + if (presenceFieldsTest) { | |
165 | + if (withTs) { | |
166 | + assertTsForExplicitProtoFieldValues(values, expectedKeys, 10000, 0); | |
167 | + assertExplicitProtoFieldValuesWithTs(values); | |
168 | + } else { | |
169 | + assertExplicitProtoFieldValues(values); | |
170 | + } | |
171 | + } else { | |
172 | + if (withTs) { | |
173 | + assertTs(values, expectedKeys, 10000, 0); | |
174 | + } | |
175 | + assertValues(values, 0); | |
162 | 176 | } |
163 | - assertValues(values, 0); | |
164 | 177 | } |
165 | 178 | |
166 | 179 | protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { |
... | ... | @@ -254,6 +267,41 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt |
254 | 267 | } |
255 | 268 | } |
256 | 269 | |
270 | + private void assertExplicitProtoFieldValues(Map<String, List<Map<String, Object>>> deviceValues) { | |
271 | + for (Map.Entry<String, List<Map<String, Object>>> entry : deviceValues.entrySet()) { | |
272 | + String key = entry.getKey(); | |
273 | + List<Map<String, Object>> tsKv = entry.getValue(); | |
274 | + String value = (String) tsKv.get(0).get("value"); | |
275 | + switch (key) { | |
276 | + case "key1": | |
277 | + assertEquals("", value); | |
278 | + break; | |
279 | + case "key2": | |
280 | + assertEquals("false", value); | |
281 | + break; | |
282 | + case "key3": | |
283 | + assertEquals("0.0", value); | |
284 | + break; | |
285 | + case "key4": | |
286 | + assertEquals("0", value); | |
287 | + break; | |
288 | + case "key5": | |
289 | + assertEquals("{\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value); | |
290 | + break; | |
291 | + } | |
292 | + } | |
293 | + } | |
294 | + | |
295 | + private void assertExplicitProtoFieldValuesWithTs(Map<String, List<Map<String, Object>>> deviceValues) { | |
296 | + assertEquals(1, deviceValues.size()); | |
297 | + List<Map<String, Object>> tsKv = deviceValues.get("key5"); | |
298 | + assertEquals("{\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", tsKv.get(0).get("value")); | |
299 | + } | |
300 | + | |
301 | + private void assertTsForExplicitProtoFieldValues(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { | |
302 | + assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); | |
303 | + } | |
304 | + | |
257 | 305 | private void assertTs(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { |
258 | 306 | assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); |
259 | 307 | assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); | ... | ... |
... | ... | @@ -45,20 +45,20 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract |
45 | 45 | } |
46 | 46 | |
47 | 47 | @Test |
48 | - public void testPushMqttTelemetry() throws Exception { | |
48 | + public void testPushTelemetry() throws Exception { | |
49 | 49 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
50 | - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | |
50 | + processJsonPayloadTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | |
51 | 51 | } |
52 | 52 | |
53 | 53 | @Test |
54 | - public void testPushMqttTelemetryWithTs() throws Exception { | |
54 | + public void testPushTelemetryWithTs() throws Exception { | |
55 | 55 | String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; |
56 | 56 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
57 | - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | |
57 | + processJsonPayloadTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | |
58 | 58 | } |
59 | 59 | |
60 | 60 | @Test |
61 | - public void testPushMqttTelemetryGateway() throws Exception { | |
61 | + public void testPushTelemetryGateway() throws Exception { | |
62 | 62 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
63 | 63 | String deviceName1 = "Device A"; |
64 | 64 | String deviceName2 = "Device B"; | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import com.squareup.wire.schema.internal.parser.ProtoFileElement; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
24 | 24 | import org.junit.After; |
25 | +import org.junit.Ignore; | |
25 | 26 | import org.junit.Test; |
26 | 27 | import org.thingsboard.server.common.data.Device; |
27 | 28 | import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
... | ... | @@ -35,6 +36,7 @@ import org.thingsboard.server.gen.transport.TransportApiProtos; |
35 | 36 | import org.thingsboard.server.gen.transport.TransportProtos; |
36 | 37 | |
37 | 38 | import java.util.Arrays; |
39 | +import java.util.Collections; | |
38 | 40 | import java.util.List; |
39 | 41 | |
40 | 42 | import static org.junit.Assert.assertNotNull; |
... | ... | @@ -51,9 +53,8 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac |
51 | 53 | } |
52 | 54 | |
53 | 55 | @Test |
54 | - public void testPushMqttTelemetry() throws Exception { | |
56 | + public void testPushTelemetry() throws Exception { | |
55 | 57 | super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); |
56 | - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | |
57 | 58 | DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); |
58 | 59 | assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); |
59 | 60 | MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; |
... | ... | @@ -89,38 +90,37 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac |
89 | 90 | .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) |
90 | 91 | .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) |
91 | 92 | .build(); |
92 | - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), false); | |
93 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false); | |
93 | 94 | } |
94 | 95 | |
95 | 96 | @Test |
96 | - public void testPushMqttTelemetryWithTs() throws Exception { | |
97 | + public void testPushTelemetryWithTs() throws Exception { | |
97 | 98 | String schemaStr = "syntax =\"proto3\";\n" + |
98 | 99 | "\n" + |
99 | 100 | "package test;\n" + |
100 | 101 | "\n" + |
101 | 102 | "message PostTelemetry {\n" + |
102 | - " int64 ts = 1;\n" + | |
103 | + " optional int64 ts = 1;\n" + | |
103 | 104 | " Values values = 2;\n" + |
104 | 105 | " \n" + |
105 | 106 | " message Values {\n" + |
106 | - " string key1 = 3;\n" + | |
107 | - " bool key2 = 4;\n" + | |
108 | - " double key3 = 5;\n" + | |
109 | - " int32 key4 = 6;\n" + | |
107 | + " optional string key1 = 3;\n" + | |
108 | + " optional bool key2 = 4;\n" + | |
109 | + " optional double key3 = 5;\n" + | |
110 | + " optional int32 key4 = 6;\n" + | |
110 | 111 | " JsonObject key5 = 7;\n" + |
111 | 112 | " }\n" + |
112 | 113 | " \n" + |
113 | 114 | " message JsonObject {\n" + |
114 | - " int32 someNumber = 8;\n" + | |
115 | + " optional int32 someNumber = 8;\n" + | |
115 | 116 | " repeated int32 someArray = 9;\n" + |
116 | 117 | " NestedJsonObject someNestedObject = 10;\n" + |
117 | 118 | " message NestedJsonObject {\n" + |
118 | - " string key = 11;\n" + | |
119 | + " optional string key = 11;\n" + | |
119 | 120 | " }\n" + |
120 | 121 | " }\n" + |
121 | 122 | "}"; |
122 | 123 | super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED); |
123 | - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | |
124 | 124 | DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); |
125 | 125 | assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); |
126 | 126 | MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; |
... | ... | @@ -167,11 +167,124 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac |
167 | 167 | .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) |
168 | 168 | .build(); |
169 | 169 | |
170 | - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), true); | |
170 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), true, false); | |
171 | + } | |
172 | + | |
173 | + @Test | |
174 | + public void testPushTelemetryWithExplicitPresenceProtoKeys() throws Exception { | |
175 | + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); | |
176 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
177 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
178 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
179 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
180 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
181 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
182 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA); | |
183 | + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | |
184 | + | |
185 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | |
186 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
187 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
188 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
189 | + | |
190 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | |
191 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
192 | + assertNotNull(jsonObjectBuilderDescriptor); | |
193 | + DynamicMessage jsonObject = jsonObjectBuilder | |
194 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
195 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
196 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
197 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
198 | + .build(); | |
199 | + | |
200 | + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | |
201 | + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | |
202 | + assertNotNull(postTelemetryMsgDescriptor); | |
203 | + DynamicMessage postTelemetryMsg = postTelemetryBuilder | |
204 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key1"), "") | |
205 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), false) | |
206 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 0.0) | |
207 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 0) | |
208 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) | |
209 | + .build(); | |
210 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, true); | |
211 | + } | |
212 | + | |
213 | + @Test | |
214 | + public void testPushTelemetryWithTsAndNoPresenceFields() throws Exception { | |
215 | + String schemaStr = "syntax =\"proto3\";\n" + | |
216 | + "\n" + | |
217 | + "package test;\n" + | |
218 | + "\n" + | |
219 | + "message PostTelemetry {\n" + | |
220 | + " optional int64 ts = 1;\n" + | |
221 | + " Values values = 2;\n" + | |
222 | + " \n" + | |
223 | + " message Values {\n" + | |
224 | + " string key1 = 3;\n" + | |
225 | + " bool key2 = 4;\n" + | |
226 | + " double key3 = 5;\n" + | |
227 | + " int32 key4 = 6;\n" + | |
228 | + " JsonObject key5 = 7;\n" + | |
229 | + " }\n" + | |
230 | + " \n" + | |
231 | + " message JsonObject {\n" + | |
232 | + " optional int32 someNumber = 8;\n" + | |
233 | + " repeated int32 someArray = 9;\n" + | |
234 | + " NestedJsonObject someNestedObject = 10;\n" + | |
235 | + " message NestedJsonObject {\n" + | |
236 | + " optional string key = 11;\n" + | |
237 | + " }\n" + | |
238 | + " }\n" + | |
239 | + "}"; | |
240 | + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED); | |
241 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
242 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
243 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
244 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
245 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
246 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
247 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr); | |
248 | + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | |
249 | + | |
250 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | |
251 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
252 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
253 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
254 | + | |
255 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | |
256 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
257 | + assertNotNull(jsonObjectBuilderDescriptor); | |
258 | + DynamicMessage jsonObject = jsonObjectBuilder | |
259 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
260 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
261 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
262 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
263 | + .build(); | |
264 | + | |
265 | + | |
266 | + DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values"); | |
267 | + Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType(); | |
268 | + assertNotNull(valuesDescriptor); | |
269 | + | |
270 | + DynamicMessage valuesMsg = valuesBuilder | |
271 | + .setField(valuesDescriptor.findFieldByName("key4"), 0) | |
272 | + .setField(valuesDescriptor.findFieldByName("key5"), jsonObject) | |
273 | + .build(); | |
274 | + | |
275 | + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | |
276 | + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | |
277 | + assertNotNull(postTelemetryMsgDescriptor); | |
278 | + DynamicMessage postTelemetryMsg = postTelemetryBuilder | |
279 | + .setField(postTelemetryMsgDescriptor.findFieldByName("ts"), 10000L) | |
280 | + .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) | |
281 | + .build(); | |
282 | + | |
283 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, Collections.singletonList("key5"), postTelemetryMsg.toByteArray(), true, true); | |
171 | 284 | } |
172 | 285 | |
173 | 286 | @Test |
174 | - public void testPushMqttTelemetryGateway() throws Exception { | |
287 | + public void testPushTelemetryGateway() throws Exception { | |
175 | 288 | super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED); |
176 | 289 | TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); |
177 | 290 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ... | ... |
... | ... | @@ -47,6 +47,7 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC |
47 | 47 | public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; |
48 | 48 | public static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema"; |
49 | 49 | public static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema"; |
50 | + private static final String PROTO_3_SYNTAX = "proto3"; | |
50 | 51 | |
51 | 52 | private String deviceTelemetryProtoSchema; |
52 | 53 | private String deviceAttributesProtoSchema; |
... | ... | @@ -123,6 +124,7 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC |
123 | 124 | public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) { |
124 | 125 | DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); |
125 | 126 | schemaBuilder.setName(schemaName); |
127 | + schemaBuilder.setSyntax(PROTO_3_SYNTAX); | |
126 | 128 | schemaBuilder.setPackage(!isEmptyStr(protoFileElement.getPackageName()) ? |
127 | 129 | protoFileElement.getPackageName() : schemaName.toLowerCase()); |
128 | 130 | List<TypeElement> types = protoFileElement.getTypes(); | ... | ... |
... | ... | @@ -160,7 +160,7 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { |
160 | 160 | |
161 | 161 | private String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException { |
162 | 162 | DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, bytes); |
163 | - return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage); | |
163 | + return JsonFormat.printer().print(dynamicMessage); | |
164 | 164 | } |
165 | 165 | |
166 | 166 | } | ... | ... |
... | ... | @@ -82,6 +82,7 @@ import java.util.concurrent.locks.Lock; |
82 | 82 | import java.util.concurrent.locks.ReentrantLock; |
83 | 83 | import java.util.stream.Collectors; |
84 | 84 | |
85 | +import static com.google.protobuf.FieldType.MESSAGE; | |
85 | 86 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; |
86 | 87 | import static org.thingsboard.server.dao.service.Validator.validateId; |
87 | 88 | |
... | ... | @@ -381,6 +382,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
381 | 382 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = |
382 | 383 | (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); |
383 | 384 | validateProtoSchemas(protoTransportPayloadConfiguration); |
385 | + validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); | |
384 | 386 | validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); |
385 | 387 | } |
386 | 388 | } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { |
... | ... | @@ -392,6 +394,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
392 | 394 | if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { |
393 | 395 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; |
394 | 396 | validateProtoSchemas(protoTransportPayloadConfiguration); |
397 | + validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration); | |
395 | 398 | validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); |
396 | 399 | } |
397 | 400 | } |
... | ... | @@ -609,6 +612,33 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
609 | 612 | |
610 | 613 | }; |
611 | 614 | |
615 | + private void validateTelemetryDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { | |
616 | + String deviceTelemetryProtoSchema = protoTransportPayloadTypeConfiguration.getDeviceTelemetryProtoSchema(); | |
617 | + Descriptors.Descriptor telemetryDynamicMessageDescriptor = protoTransportPayloadTypeConfiguration.getTelemetryDynamicMessageDescriptor(deviceTelemetryProtoSchema); | |
618 | + if (telemetryDynamicMessageDescriptor == null) { | |
619 | + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Failed to get telemetryDynamicMessageDescriptor!"); | |
620 | + } else { | |
621 | + List<Descriptors.FieldDescriptor> fields = telemetryDynamicMessageDescriptor.getFields(); | |
622 | + if (CollectionUtils.isEmpty(fields)) { | |
623 | + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " " + telemetryDynamicMessageDescriptor.getName() + " fields is empty!"); | |
624 | + } else if (fields.size() == 2) { | |
625 | + Descriptors.FieldDescriptor tsFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("ts"); | |
626 | + Descriptors.FieldDescriptor valuesFieldDescriptor = telemetryDynamicMessageDescriptor.findFieldByName("values"); | |
627 | + if (tsFieldDescriptor != null && valuesFieldDescriptor != null) { | |
628 | + if (!Descriptors.FieldDescriptor.Type.MESSAGE.equals(valuesFieldDescriptor.getType())) { | |
629 | + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'values' has invalid data type. Only message type is supported!"); | |
630 | + } | |
631 | + if (!Descriptors.FieldDescriptor.Type.INT64.equals(tsFieldDescriptor.getType())) { | |
632 | + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid data type. Only int64 type is supported!"); | |
633 | + } | |
634 | + if (!tsFieldDescriptor.hasOptionalKeyword()) { | |
635 | + throw new DataValidationException(invalidSchemaProvidedMessage(TELEMETRY_PROTO_SCHEMA) + " Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); | |
636 | + } | |
637 | + } | |
638 | + } | |
639 | + } | |
640 | + } | |
641 | + | |
612 | 642 | private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { |
613 | 643 | DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema()); |
614 | 644 | Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType(); | ... | ... |
... | ... | @@ -133,16 +133,16 @@ export const defaultTelemetrySchema = |
133 | 133 | '\n' + |
134 | 134 | 'message SensorDataReading {\n' + |
135 | 135 | '\n' + |
136 | - ' double temperature = 1;\n' + | |
137 | - ' double humidity = 2;\n' + | |
136 | + ' optional double temperature = 1;\n' + | |
137 | + ' optional double humidity = 2;\n' + | |
138 | 138 | ' InnerObject innerObject = 3;\n' + |
139 | 139 | '\n' + |
140 | 140 | ' message InnerObject {\n' + |
141 | - ' string key1 = 1;\n' + | |
142 | - ' bool key2 = 2;\n' + | |
143 | - ' double key3 = 3;\n' + | |
144 | - ' int32 key4 = 4;\n' + | |
145 | - ' string key5 = 5;\n' + | |
141 | + ' optional string key1 = 1;\n' + | |
142 | + ' optional bool key2 = 2;\n' + | |
143 | + ' optional double key3 = 3;\n' + | |
144 | + ' optional int32 key4 = 4;\n' + | |
145 | + ' optional string key5 = 5;\n' + | |
146 | 146 | ' }\n' + |
147 | 147 | '}\n'; |
148 | 148 | |
... | ... | @@ -151,8 +151,8 @@ export const defaultAttributesSchema = |
151 | 151 | 'package attributes;\n' + |
152 | 152 | '\n' + |
153 | 153 | 'message SensorConfiguration {\n' + |
154 | - ' string firmwareVersion = 1;\n' + | |
155 | - ' string serialNumber = 2;\n' + | |
154 | + ' optional string firmwareVersion = 1;\n' + | |
155 | + ' optional string serialNumber = 2;\n' + | |
156 | 156 | '}'; |
157 | 157 | |
158 | 158 | export const defaultRpcRequestSchema = |
... | ... | @@ -160,9 +160,9 @@ export const defaultRpcRequestSchema = |
160 | 160 | 'package rpc;\n' + |
161 | 161 | '\n' + |
162 | 162 | 'message RpcRequestMsg {\n' + |
163 | - ' string method = 1;\n' + | |
164 | - ' int32 requestId = 2;\n' + | |
165 | - ' string params = 3;\n' + | |
163 | + ' optional string method = 1;\n' + | |
164 | + ' optional int32 requestId = 2;\n' + | |
165 | + ' optional string params = 3;\n' + | |
166 | 166 | '}'; |
167 | 167 | |
168 | 168 | export const defaultRpcResponseSchema = |
... | ... | @@ -170,7 +170,7 @@ export const defaultRpcResponseSchema = |
170 | 170 | 'package rpc;\n' + |
171 | 171 | '\n' + |
172 | 172 | 'message RpcResponseMsg {\n' + |
173 | - ' string payload = 1;\n' + | |
173 | + ' optional string payload = 1;\n' + | |
174 | 174 | '}'; |
175 | 175 | |
176 | 176 | export const coapDeviceTypeTranslationMap = new Map<CoapTransportDeviceType, string>( | ... | ... |