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,6 +634,72 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
634 dynamicMsgToJson(sampleMsgDescriptor, sampleMsgWithOneOfSubMessage.toByteArray())); 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 private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception { 703 private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception {
638 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema); 704 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema);
639 MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration); 705 MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration);
@@ -42,18 +42,18 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle @@ -42,18 +42,18 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle
42 "package test;\n" + 42 "package test;\n" +
43 "\n" + 43 "\n" +
44 "message PostTelemetry {\n" + 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 " JsonObject key5 = 5;\n" + 49 " JsonObject key5 = 5;\n" +
50 "\n" + 50 "\n" +
51 " message JsonObject {\n" + 51 " message JsonObject {\n" +
52 - " int32 someNumber = 6;\n" + 52 + " optional int32 someNumber = 6;\n" +
53 " repeated int32 someArray = 7;\n" + 53 " repeated int32 someArray = 7;\n" +
54 - " NestedJsonObject someNestedObject = 8;\n" + 54 + " optional NestedJsonObject someNestedObject = 8;\n" +
55 " message NestedJsonObject {\n" + 55 " message NestedJsonObject {\n" +
56 - " string key = 9;\n" + 56 + " optional string key = 9;\n" +
57 " }\n" + 57 " }\n" +
58 " }\n" + 58 " }\n" +
59 "}"; 59 "}";
@@ -63,18 +63,18 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle @@ -63,18 +63,18 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle
63 "package test;\n" + 63 "package test;\n" +
64 "\n" + 64 "\n" +
65 "message PostAttributes {\n" + 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 " JsonObject key5 = 5;\n" + 70 " JsonObject key5 = 5;\n" +
71 "\n" + 71 "\n" +
72 " message JsonObject {\n" + 72 " message JsonObject {\n" +
73 - " int32 someNumber = 6;\n" + 73 + " optional int32 someNumber = 6;\n" +
74 " repeated int32 someArray = 7;\n" + 74 " repeated int32 someArray = 7;\n" +
75 " NestedJsonObject someNestedObject = 8;\n" + 75 " NestedJsonObject someNestedObject = 8;\n" +
76 " message NestedJsonObject {\n" + 76 " message NestedJsonObject {\n" +
77 - " string key = 9;\n" + 77 + " optional string key = 9;\n" +
78 " }\n" + 78 " }\n" +
79 " }\n" + 79 " }\n" +
80 "}"; 80 "}";
@@ -83,16 +83,16 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle @@ -83,16 +83,16 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle
83 "package rpc;\n" + 83 "package rpc;\n" +
84 "\n" + 84 "\n" +
85 "message RpcResponseMsg {\n" + 85 "message RpcResponseMsg {\n" +
86 - " string payload = 1;\n" + 86 + " optional string payload = 1;\n" +
87 "}"; 87 "}";
88 88
89 protected static final String DEVICE_RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" + 89 protected static final String DEVICE_RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" +
90 "package rpc;\n" + 90 "package rpc;\n" +
91 "\n" + 91 "\n" +
92 "message RpcRequestMsg {\n" + 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 protected Tenant savedTenant; 98 protected Tenant savedTenant;
@@ -61,11 +61,14 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap @@ -61,11 +61,14 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
61 @Test 61 @Test
62 public void testPushAttributes() throws Exception { 62 public void testPushAttributes() throws Exception {
63 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 CoapClient client = getCoapClient(FeatureType.ATTRIBUTES); 72 CoapClient client = getCoapClient(FeatureType.ATTRIBUTES);
70 73
71 postAttributes(client, payload); 74 postAttributes(client, payload);
@@ -94,7 +97,11 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap @@ -94,7 +97,11 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
94 97
95 String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); 98 String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet);
96 List<Map<String, Object>> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {}); 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 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); 105 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
99 doDelete(deleteAttributesUrl); 106 doDelete(deleteAttributesUrl);
100 } 107 }
@@ -108,11 +115,11 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap @@ -108,11 +115,11 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap
108 } 115 }
109 116
110 @SuppressWarnings("unchecked") 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 for (Map<String, Object> map : deviceValues) { 119 for (Map<String, Object> map : deviceValues) {
113 String key = (String) map.get("key"); 120 String key = (String) map.get("key");
114 Object value = map.get("value"); 121 Object value = map.get("value");
115 - assertTrue(expectedKeySet.contains(key)); 122 + assertTrue(keySet.contains(key));
116 switch (key) { 123 switch (key) {
117 case "key1": 124 case "key1":
118 assertEquals("value1", value); 125 assertEquals("value1", value);
@@ -138,6 +145,35 @@ public abstract class AbstractCoapAttributesIntegrationTest extends AbstractCoap @@ -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 private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { 177 private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) {
142 return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); 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,7 +32,6 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
32 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; 32 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
33 33
34 import java.util.Arrays; 34 import java.util.Arrays;
35 -import java.util.List;  
36 35
37 import static org.junit.Assert.assertNotNull; 36 import static org.junit.Assert.assertNotNull;
38 import static org.junit.Assert.assertTrue; 37 import static org.junit.Assert.assertTrue;
@@ -48,7 +47,6 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac @@ -48,7 +47,6 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac
48 @Test 47 @Test
49 public void testPushAttributes() throws Exception { 48 public void testPushAttributes() throws Exception {
50 super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF); 49 super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
51 - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");  
52 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 50 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
53 assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration); 51 assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
54 CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration; 52 CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@@ -87,7 +85,50 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac @@ -87,7 +85,50 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac
87 .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) 85 .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4)
88 .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) 86 .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject)
89 .build(); 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,18 +56,21 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap
56 56
57 @Test 57 @Test
58 public void testPushTelemetry() throws Exception { 58 public void testPushTelemetry() throws Exception {
59 - processTestPostTelemetry(null, false); 59 + processJsonPayloadTelemetryTest(PAYLOAD_VALUES_STR.getBytes(), false);
60 } 60 }
61 61
62 @Test 62 @Test
63 public void testPushTelemetryWithTs() throws Exception { 63 public void testPushTelemetryWithTs() throws Exception {
64 String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; 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 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 CoapClient coapClient = getCoapClient(FeatureType.TELEMETRY); 74 CoapClient coapClient = getCoapClient(FeatureType.TELEMETRY);
72 postTelemetry(coapClient, payloadBytes); 75 postTelemetry(coapClient, payloadBytes);
73 76
@@ -127,16 +130,22 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap @@ -127,16 +130,22 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap
127 } 130 }
128 assertNotNull(values); 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 private void postTelemetry(CoapClient client, byte[] payload) throws IOException, ConnectorException { 148 private void postTelemetry(CoapClient client, byte[] payload) throws IOException, ConnectorException {
137 - if (payload == null) {  
138 - payload = PAYLOAD_VALUES_STR.getBytes();  
139 - }  
140 CoapResponse coapResponse = client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON); 149 CoapResponse coapResponse = client.setTimeout((long) 60000).post(payload, MediaTypeRegistry.APPLICATION_JSON);
141 assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); 150 assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode());
142 } 151 }
@@ -174,5 +183,39 @@ public abstract class AbstractCoapTimeseriesIntegrationTest extends AbstractCoap @@ -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,6 +32,9 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
32 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; 32 import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
33 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; 33 import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
34 34
  35 +import java.util.Arrays;
  36 +import java.util.Collections;
  37 +
35 import static org.junit.Assert.assertNotNull; 38 import static org.junit.Assert.assertNotNull;
36 import static org.junit.Assert.assertTrue; 39 import static org.junit.Assert.assertTrue;
37 40
@@ -84,7 +87,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac @@ -84,7 +87,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
84 .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) 87 .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4)
85 .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) 88 .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject)
86 .build(); 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 @Test 93 @Test
@@ -94,23 +97,23 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac @@ -94,23 +97,23 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
94 "package test;\n" + 97 "package test;\n" +
95 "\n" + 98 "\n" +
96 "message PostTelemetry {\n" + 99 "message PostTelemetry {\n" +
97 - " int64 ts = 1;\n" + 100 + " optional int64 ts = 1;\n" +
98 " Values values = 2;\n" + 101 " Values values = 2;\n" +
99 " \n" + 102 " \n" +
100 " message Values {\n" + 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 " JsonObject key5 = 7;\n" + 108 " JsonObject key5 = 7;\n" +
106 " }\n" + 109 " }\n" +
107 " \n" + 110 " \n" +
108 " message JsonObject {\n" + 111 " message JsonObject {\n" +
109 - " int32 someNumber = 8;\n" + 112 + " optional int32 someNumber = 8;\n" +
110 " repeated int32 someArray = 9;\n" + 113 " repeated int32 someArray = 9;\n" +
111 " NestedJsonObject someNestedObject = 10;\n" + 114 " NestedJsonObject someNestedObject = 10;\n" +
112 " message NestedJsonObject {\n" + 115 " message NestedJsonObject {\n" +
113 - " string key = 11;\n" + 116 + " optional string key = 11;\n" +
114 " }\n" + 117 " }\n" +
115 " }\n" + 118 " }\n" +
116 "}"; 119 "}";
@@ -164,7 +167,126 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac @@ -164,7 +167,126 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
164 .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) 167 .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg)
165 .build(); 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,13 +55,13 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
55 } 55 }
56 56
57 @Test 57 @Test
58 - public void testPushMqttAttributes() throws Exception { 58 + public void testPushAttributes() throws Exception {
59 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 @Test 63 @Test
64 - public void testPushMqttAttributesGateway() throws Exception { 64 + public void testPushAttributesGateway() throws Exception {
65 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 65 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
66 String deviceName1 = "Device A"; 66 String deviceName1 = "Device A";
67 String deviceName2 = "Device B"; 67 String deviceName2 = "Device B";
@@ -69,7 +69,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -69,7 +69,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
69 processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); 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 MqttAsyncClient client = getMqttAsyncClient(accessToken); 77 MqttAsyncClient client = getMqttAsyncClient(accessToken);
74 78
75 publishMqttMsg(client, payload, topic); 79 publishMqttMsg(client, payload, topic);
@@ -98,7 +102,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -98,7 +102,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
98 102
99 String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); 103 String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet);
100 List<Map<String, Object>> values = doGetAsyncTyped(getAttributesValuesUrl, new TypeReference<>() {}); 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 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); 110 String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet);
103 doDelete(deleteAttributesUrl); 111 doDelete(deleteAttributesUrl);
104 } 112 }
@@ -145,11 +153,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -145,11 +153,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
145 } 153 }
146 154
147 @SuppressWarnings("unchecked") 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 for (Map<String, Object> map : deviceValues) { 157 for (Map<String, Object> map : deviceValues) {
150 String key = (String) map.get("key"); 158 String key = (String) map.get("key");
151 Object value = map.get("value"); 159 Object value = map.get("value");
152 - assertTrue(expectedKeySet.contains(key)); 160 + assertTrue(keySet.contains(key));
153 switch (key) { 161 switch (key) {
154 case "key1": 162 case "key1":
155 assertEquals("value1", value); 163 assertEquals("value1", value);
@@ -175,6 +183,35 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt @@ -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 protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) { 215 protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) {
179 return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}"; 216 return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}";
180 } 217 }
@@ -40,13 +40,13 @@ public abstract class AbstractMqttAttributesJsonIntegrationTest extends Abstract @@ -40,13 +40,13 @@ public abstract class AbstractMqttAttributesJsonIntegrationTest extends Abstract
40 } 40 }
41 41
42 @Test 42 @Test
43 - public void testPushMqttAttributes() throws Exception { 43 + public void testPushAttributes() throws Exception {
44 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 @Test 48 @Test
49 - public void testPushMqttAttributesGateway() throws Exception { 49 + public void testPushAttributesGateway() throws Exception {
50 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 50 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
51 String deviceName1 = "Device A"; 51 String deviceName1 = "Device A";
52 String deviceName2 = "Device B"; 52 String deviceName2 = "Device B";
@@ -47,9 +47,8 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac @@ -47,9 +47,8 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
47 } 47 }
48 48
49 @Test 49 @Test
50 - public void testPushMqttAttributes() throws Exception { 50 + public void testPushAttributes() throws Exception {
51 super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); 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 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 52 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
54 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); 53 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
55 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; 54 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
@@ -85,11 +84,51 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac @@ -85,11 +84,51 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
85 .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) 84 .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4)
86 .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) 85 .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject)
87 .build(); 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 @Test 130 @Test
92 - public void testPushMqttAttributesGateway() throws Exception { 131 + public void testPushAttributesGateway() throws Exception {
93 super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null); 132 super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null);
94 TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); 133 TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder();
95 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 134 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
@@ -61,20 +61,20 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -61,20 +61,20 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
61 } 61 }
62 62
63 @Test 63 @Test
64 - public void testPushMqttTelemetry() throws Exception { 64 + public void testPushTelemetry() throws Exception {
65 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 @Test 69 @Test
70 - public void testPushMqttTelemetryWithTs() throws Exception { 70 + public void testPushTelemetryWithTs() throws Exception {
71 String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; 71 String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}";
72 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 @Test 76 @Test
77 - public void testPushMqttTelemetryGateway() throws Exception { 77 + public void testPushTelemetryGateway() throws Exception {
78 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 78 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
79 String deviceName1 = "Device A"; 79 String deviceName1 = "Device A";
80 String deviceName2 = "Device B"; 80 String deviceName2 = "Device B";
@@ -97,7 +97,11 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -97,7 +97,11 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
97 assertNotNull(device); 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 MqttAsyncClient client = getMqttAsyncClient(accessToken); 105 MqttAsyncClient client = getMqttAsyncClient(accessToken);
102 publishMqttMsg(client, payload, topic); 106 publishMqttMsg(client, payload, topic);
103 107
@@ -157,10 +161,19 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt @@ -157,10 +161,19 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
157 } 161 }
158 assertNotNull(values); 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 protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { 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,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 private void assertTs(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { 305 private void assertTs(Map<String, List<Map<String, Object>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) {
258 assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); 306 assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts"));
259 assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); 307 assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts"));
@@ -45,20 +45,20 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract @@ -45,20 +45,20 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract
45 } 45 }
46 46
47 @Test 47 @Test
48 - public void testPushMqttTelemetry() throws Exception { 48 + public void testPushTelemetry() throws Exception {
49 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 @Test 53 @Test
54 - public void testPushMqttTelemetryWithTs() throws Exception { 54 + public void testPushTelemetryWithTs() throws Exception {
55 String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; 55 String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}";
56 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 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 @Test 60 @Test
61 - public void testPushMqttTelemetryGateway() throws Exception { 61 + public void testPushTelemetryGateway() throws Exception {
62 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 62 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
63 String deviceName1 = "Device A"; 63 String deviceName1 = "Device A";
64 String deviceName2 = "Device B"; 64 String deviceName2 = "Device B";
@@ -22,6 +22,7 @@ import com.squareup.wire.schema.internal.parser.ProtoFileElement; @@ -22,6 +22,7 @@ import com.squareup.wire.schema.internal.parser.ProtoFileElement;
22 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
23 import org.eclipse.paho.client.mqttv3.MqttAsyncClient; 23 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
24 import org.junit.After; 24 import org.junit.After;
  25 +import org.junit.Ignore;
25 import org.junit.Test; 26 import org.junit.Test;
26 import org.thingsboard.server.common.data.Device; 27 import org.thingsboard.server.common.data.Device;
27 import org.thingsboard.server.common.data.DeviceProfileProvisionType; 28 import org.thingsboard.server.common.data.DeviceProfileProvisionType;
@@ -35,6 +36,7 @@ import org.thingsboard.server.gen.transport.TransportApiProtos; @@ -35,6 +36,7 @@ import org.thingsboard.server.gen.transport.TransportApiProtos;
35 import org.thingsboard.server.gen.transport.TransportProtos; 36 import org.thingsboard.server.gen.transport.TransportProtos;
36 37
37 import java.util.Arrays; 38 import java.util.Arrays;
  39 +import java.util.Collections;
38 import java.util.List; 40 import java.util.List;
39 41
40 import static org.junit.Assert.assertNotNull; 42 import static org.junit.Assert.assertNotNull;
@@ -51,9 +53,8 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac @@ -51,9 +53,8 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
51 } 53 }
52 54
53 @Test 55 @Test
54 - public void testPushMqttTelemetry() throws Exception { 56 + public void testPushTelemetry() throws Exception {
55 super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); 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 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 58 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
58 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); 59 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
59 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; 60 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
@@ -89,38 +90,37 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac @@ -89,38 +90,37 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
89 .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) 90 .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4)
90 .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) 91 .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject)
91 .build(); 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 @Test 96 @Test
96 - public void testPushMqttTelemetryWithTs() throws Exception { 97 + public void testPushTelemetryWithTs() throws Exception {
97 String schemaStr = "syntax =\"proto3\";\n" + 98 String schemaStr = "syntax =\"proto3\";\n" +
98 "\n" + 99 "\n" +
99 "package test;\n" + 100 "package test;\n" +
100 "\n" + 101 "\n" +
101 "message PostTelemetry {\n" + 102 "message PostTelemetry {\n" +
102 - " int64 ts = 1;\n" + 103 + " optional int64 ts = 1;\n" +
103 " Values values = 2;\n" + 104 " Values values = 2;\n" +
104 " \n" + 105 " \n" +
105 " message Values {\n" + 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 " JsonObject key5 = 7;\n" + 111 " JsonObject key5 = 7;\n" +
111 " }\n" + 112 " }\n" +
112 " \n" + 113 " \n" +
113 " message JsonObject {\n" + 114 " message JsonObject {\n" +
114 - " int32 someNumber = 8;\n" + 115 + " optional int32 someNumber = 8;\n" +
115 " repeated int32 someArray = 9;\n" + 116 " repeated int32 someArray = 9;\n" +
116 " NestedJsonObject someNestedObject = 10;\n" + 117 " NestedJsonObject someNestedObject = 10;\n" +
117 " message NestedJsonObject {\n" + 118 " message NestedJsonObject {\n" +
118 - " string key = 11;\n" + 119 + " optional string key = 11;\n" +
119 " }\n" + 120 " }\n" +
120 " }\n" + 121 " }\n" +
121 "}"; 122 "}";
122 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 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 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); 124 DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
125 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); 125 assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
126 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; 126 MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
@@ -167,11 +167,124 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac @@ -167,11 +167,124 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
167 .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) 167 .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg)
168 .build(); 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 @Test 286 @Test
174 - public void testPushMqttTelemetryGateway() throws Exception { 287 + public void testPushTelemetryGateway() throws Exception {
175 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); 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 TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); 289 TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder();
177 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); 290 List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
@@ -47,6 +47,7 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC @@ -47,6 +47,7 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC
47 public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; 47 public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema";
48 public static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema"; 48 public static final String RPC_RESPONSE_PROTO_SCHEMA = "rpc response proto schema";
49 public static final String RPC_REQUEST_PROTO_SCHEMA = "rpc request proto schema"; 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 private String deviceTelemetryProtoSchema; 52 private String deviceTelemetryProtoSchema;
52 private String deviceAttributesProtoSchema; 53 private String deviceAttributesProtoSchema;
@@ -123,6 +124,7 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC @@ -123,6 +124,7 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC
123 public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) { 124 public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) {
124 DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); 125 DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder();
125 schemaBuilder.setName(schemaName); 126 schemaBuilder.setName(schemaName);
  127 + schemaBuilder.setSyntax(PROTO_3_SYNTAX);
126 schemaBuilder.setPackage(!isEmptyStr(protoFileElement.getPackageName()) ? 128 schemaBuilder.setPackage(!isEmptyStr(protoFileElement.getPackageName()) ?
127 protoFileElement.getPackageName() : schemaName.toLowerCase()); 129 protoFileElement.getPackageName() : schemaName.toLowerCase());
128 List<TypeElement> types = protoFileElement.getTypes(); 130 List<TypeElement> types = protoFileElement.getTypes();
@@ -160,7 +160,7 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor { @@ -160,7 +160,7 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
160 160
161 private String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException { 161 private String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException {
162 DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, bytes); 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,6 +82,7 @@ import java.util.concurrent.locks.Lock;
82 import java.util.concurrent.locks.ReentrantLock; 82 import java.util.concurrent.locks.ReentrantLock;
83 import java.util.stream.Collectors; 83 import java.util.stream.Collectors;
84 84
  85 +import static com.google.protobuf.FieldType.MESSAGE;
85 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; 86 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE;
86 import static org.thingsboard.server.dao.service.Validator.validateId; 87 import static org.thingsboard.server.dao.service.Validator.validateId;
87 88
@@ -381,6 +382,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -381,6 +382,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
381 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = 382 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration =
382 (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); 383 (ProtoTransportPayloadConfiguration) mqttTransportConfiguration.getTransportPayloadTypeConfiguration();
383 validateProtoSchemas(protoTransportPayloadConfiguration); 384 validateProtoSchemas(protoTransportPayloadConfiguration);
  385 + validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration);
384 validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); 386 validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration);
385 } 387 }
386 } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) { 388 } else if (transportConfiguration instanceof CoapDeviceProfileTransportConfiguration) {
@@ -392,6 +394,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -392,6 +394,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
392 if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) { 394 if (transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration) {
393 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; 395 ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
394 validateProtoSchemas(protoTransportPayloadConfiguration); 396 validateProtoSchemas(protoTransportPayloadConfiguration);
  397 + validateTelemetryDynamicMessageFields(protoTransportPayloadConfiguration);
395 validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration); 398 validateRpcRequestDynamicMessageFields(protoTransportPayloadConfiguration);
396 } 399 }
397 } 400 }
@@ -609,6 +612,33 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D @@ -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 private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) { 642 private void validateRpcRequestDynamicMessageFields(ProtoTransportPayloadConfiguration protoTransportPayloadTypeConfiguration) {
613 DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema()); 643 DynamicMessage.Builder rpcRequestDynamicMessageBuilder = protoTransportPayloadTypeConfiguration.getRpcRequestDynamicMessageBuilder(protoTransportPayloadTypeConfiguration.getDeviceRpcRequestProtoSchema());
614 Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType(); 644 Descriptors.Descriptor rpcRequestDynamicMessageDescriptor = rpcRequestDynamicMessageBuilder.getDescriptorForType();
@@ -133,16 +133,16 @@ export const defaultTelemetrySchema = @@ -133,16 +133,16 @@ export const defaultTelemetrySchema =
133 '\n' + 133 '\n' +
134 'message SensorDataReading {\n' + 134 'message SensorDataReading {\n' +
135 '\n' + 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 ' InnerObject innerObject = 3;\n' + 138 ' InnerObject innerObject = 3;\n' +
139 '\n' + 139 '\n' +
140 ' message InnerObject {\n' + 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 ' }\n' + 146 ' }\n' +
147 '}\n'; 147 '}\n';
148 148
@@ -151,8 +151,8 @@ export const defaultAttributesSchema = @@ -151,8 +151,8 @@ export const defaultAttributesSchema =
151 'package attributes;\n' + 151 'package attributes;\n' +
152 '\n' + 152 '\n' +
153 'message SensorConfiguration {\n' + 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 export const defaultRpcRequestSchema = 158 export const defaultRpcRequestSchema =
@@ -160,9 +160,9 @@ export const defaultRpcRequestSchema = @@ -160,9 +160,9 @@ export const defaultRpcRequestSchema =
160 'package rpc;\n' + 160 'package rpc;\n' +
161 '\n' + 161 '\n' +
162 'message RpcRequestMsg {\n' + 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 export const defaultRpcResponseSchema = 168 export const defaultRpcResponseSchema =
@@ -170,7 +170,7 @@ export const defaultRpcResponseSchema = @@ -170,7 +170,7 @@ export const defaultRpcResponseSchema =
170 'package rpc;\n' + 170 'package rpc;\n' +
171 '\n' + 171 '\n' +
172 'message RpcResponseMsg {\n' + 172 'message RpcResponseMsg {\n' +
173 - ' string payload = 1;\n' + 173 + ' optional string payload = 1;\n' +
174 '}'; 174 '}';
175 175
176 export const coapDeviceTypeTranslationMap = new Map<CoapTransportDeviceType, string>( 176 export const coapDeviceTypeTranslationMap = new Map<CoapTransportDeviceType, string>(