Commit c4133fa41a3a6519ef6c56b07c7ec4a4820ea98b

Authored by ShvaykaD
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>(
... ...