Commit 9fef6b02a3a3c94066e41ec10ee3b57da85b6f28
Committed by
GitHub
1 parent
1b1dac30
[3.2] Feature/Proto Converter (#3423)
* device post-telemetry & post-attributes & claim * device rpc to/from server & attributes request, attributes updates * refactoring & added implementation for gateway protos api * added timeseries/attributes mqtt tests * fix MqttTimseriesIntegrationTest values asserts * mqtt attributes tests improvements * optimized time for telemetry & attributes tests * update proto files, refactoring converter, attribute requests tests * added claim tests, attribute request test * added deleted keys to gateway response on attributes request & refactored tests * added attribute updates test & refactored attribute requests tests * added attribute updates tests for gateways * added tests for RPC * fix tests & cleanup code * fix typo & cleanup transport.proto file * added more timeouts * revert handleGetAttributesRequest method * revert package-locks * fix getJsonObjectForGateway method * fix validateSharedResponseGateway method in AbstractMqttAttributesRequestIntegrationTest * fix mqtt topics * fix license headers * refactor tests * remove todo and lck files from pull * improvements for claiming tests * update device creation logic from gateway request * refactoring * extract TransportService process calls to private methods * fix duplicates & removed empty lines
Showing
75 changed files
with
4397 additions
and
534 deletions
@@ -225,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -225,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService { | ||
225 | device.setName(requestMsg.getDeviceName()); | 225 | device.setName(requestMsg.getDeviceName()); |
226 | device.setType(requestMsg.getDeviceType()); | 226 | device.setType(requestMsg.getDeviceType()); |
227 | device.setCustomerId(gateway.getCustomerId()); | 227 | device.setCustomerId(gateway.getCustomerId()); |
228 | + DeviceProfile deviceProfile = deviceProfileService.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType()); | ||
229 | + device.setDeviceProfileId(deviceProfile.getId()); | ||
228 | device = deviceService.saveDevice(device); | 230 | device = deviceService.saveDevice(device); |
229 | relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); | 231 | relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); |
230 | deviceStateService.onDeviceAdded(device); | 232 | deviceStateService.onDeviceAdded(device); |
@@ -226,6 +226,10 @@ public abstract class AbstractWebTest { | @@ -226,6 +226,10 @@ public abstract class AbstractWebTest { | ||
226 | login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); | 226 | login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); |
227 | } | 227 | } |
228 | 228 | ||
229 | + protected void loginUser(String userName, String password) throws Exception { | ||
230 | + login(userName, password); | ||
231 | + } | ||
232 | + | ||
229 | private Tenant savedDifferentTenant; | 233 | private Tenant savedDifferentTenant; |
230 | 234 | ||
231 | protected void loginDifferentTenant() throws Exception { | 235 | protected void loginDifferentTenant() throws Exception { |
@@ -251,15 +255,27 @@ public abstract class AbstractWebTest { | @@ -251,15 +255,27 @@ public abstract class AbstractWebTest { | ||
251 | protected User createUserAndLogin(User user, String password) throws Exception { | 255 | protected User createUserAndLogin(User user, String password) throws Exception { |
252 | User savedUser = doPost("/api/user", user, User.class); | 256 | User savedUser = doPost("/api/user", user, User.class); |
253 | logout(); | 257 | logout(); |
258 | + JsonNode activateRequest = getActivateRequest(password); | ||
259 | + JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class); | ||
260 | + validateAndSetJwtToken(tokenInfo, user.getEmail()); | ||
261 | + return savedUser; | ||
262 | + } | ||
263 | + | ||
264 | + protected User createUser(User user, String password) throws Exception { | ||
265 | + User savedUser = doPost("/api/user", user, User.class); | ||
266 | + JsonNode activateRequest = getActivateRequest(password); | ||
267 | + ResultActions resultActions = doPost("/api/noauth/activate", activateRequest); | ||
268 | + resultActions.andExpect(status().isOk()); | ||
269 | + return savedUser; | ||
270 | + } | ||
271 | + | ||
272 | + private JsonNode getActivateRequest(String password) throws Exception { | ||
254 | doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) | 273 | doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) |
255 | .andExpect(status().isSeeOther()) | 274 | .andExpect(status().isSeeOther()) |
256 | .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); | 275 | .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); |
257 | - JsonNode activateRequest = new ObjectMapper().createObjectNode() | 276 | + return new ObjectMapper().createObjectNode() |
258 | .put("activateToken", TestMailService.currentActivateToken) | 277 | .put("activateToken", TestMailService.currentActivateToken) |
259 | .put("password", password); | 278 | .put("password", password); |
260 | - JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class); | ||
261 | - validateAndSetJwtToken(tokenInfo, user.getEmail()); | ||
262 | - return savedUser; | ||
263 | } | 279 | } |
264 | 280 | ||
265 | protected void login(String username, String password) throws Exception { | 281 | protected void login(String username, String password) throws Exception { |
@@ -442,6 +458,10 @@ public abstract class AbstractWebTest { | @@ -442,6 +458,10 @@ public abstract class AbstractWebTest { | ||
442 | return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); | 458 | return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); |
443 | } | 459 | } |
444 | 460 | ||
461 | + protected <T> T doPostClaimAsync(String urlTemplate, Object content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { | ||
462 | + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass); | ||
463 | + } | ||
464 | + | ||
445 | protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { | 465 | protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { |
446 | return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); | 466 | return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); |
447 | } | 467 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
24 | +import org.junit.Assert; | ||
25 | +import org.springframework.util.StringUtils; | ||
26 | +import org.thingsboard.server.common.data.Device; | ||
27 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
28 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
29 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
30 | +import org.thingsboard.server.common.data.Tenant; | ||
31 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
32 | +import org.thingsboard.server.common.data.User; | ||
33 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
34 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
35 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
36 | +import org.thingsboard.server.common.data.security.Authority; | ||
37 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
38 | +import org.thingsboard.server.controller.AbstractControllerTest; | ||
39 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
40 | + | ||
41 | +import java.util.ArrayList; | ||
42 | +import java.util.List; | ||
43 | +import java.util.concurrent.atomic.AtomicInteger; | ||
44 | + | ||
45 | +import static org.junit.Assert.assertEquals; | ||
46 | +import static org.junit.Assert.assertNotNull; | ||
47 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
48 | + | ||
49 | +@Slf4j | ||
50 | +public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest { | ||
51 | + | ||
52 | + protected static final String MQTT_URL = "tcp://localhost:1883"; | ||
53 | + | ||
54 | + private static final AtomicInteger atomicInteger = new AtomicInteger(2); | ||
55 | + | ||
56 | + protected Tenant savedTenant; | ||
57 | + protected User tenantAdmin; | ||
58 | + | ||
59 | + protected Device savedDevice; | ||
60 | + protected String accessToken; | ||
61 | + | ||
62 | + protected Device savedGateway; | ||
63 | + protected String gatewayAccessToken; | ||
64 | + | ||
65 | + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { | ||
66 | + loginSysAdmin(); | ||
67 | + | ||
68 | + Tenant tenant = new Tenant(); | ||
69 | + tenant.setTitle("My tenant"); | ||
70 | + savedTenant = doPost("/api/tenant", tenant, Tenant.class); | ||
71 | + Assert.assertNotNull(savedTenant); | ||
72 | + | ||
73 | + tenantAdmin = new User(); | ||
74 | + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); | ||
75 | + tenantAdmin.setTenantId(savedTenant.getId()); | ||
76 | + tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); | ||
77 | + tenantAdmin.setFirstName("Joe"); | ||
78 | + tenantAdmin.setLastName("Downs"); | ||
79 | + | ||
80 | + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); | ||
81 | + | ||
82 | + Device device = new Device(); | ||
83 | + device.setName(deviceName); | ||
84 | + device.setType("default"); | ||
85 | + | ||
86 | + Device gateway = new Device(); | ||
87 | + gateway.setName(gatewayName); | ||
88 | + gateway.setType("default"); | ||
89 | + ObjectNode additionalInfo = mapper.createObjectNode(); | ||
90 | + additionalInfo.put("gateway", true); | ||
91 | + gateway.setAdditionalInfo(additionalInfo); | ||
92 | + | ||
93 | + if (payloadType != null) { | ||
94 | + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic); | ||
95 | + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); | ||
96 | + device.setType(savedDeviceProfile.getName()); | ||
97 | + device.setDeviceProfileId(savedDeviceProfile.getId()); | ||
98 | + gateway.setType(savedDeviceProfile.getName()); | ||
99 | + gateway.setDeviceProfileId(savedDeviceProfile.getId()); | ||
100 | + } | ||
101 | + | ||
102 | + savedDevice = doPost("/api/device", device, Device.class); | ||
103 | + | ||
104 | + DeviceCredentials deviceCredentials = | ||
105 | + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
106 | + | ||
107 | + savedGateway = doPost("/api/device", gateway, Device.class); | ||
108 | + | ||
109 | + DeviceCredentials gatewayCredentials = | ||
110 | + doGet("/api/device/" + savedGateway.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
111 | + | ||
112 | + assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
113 | + accessToken = deviceCredentials.getCredentialsId(); | ||
114 | + assertNotNull(accessToken); | ||
115 | + | ||
116 | + assertEquals(savedGateway.getId(), gatewayCredentials.getDeviceId()); | ||
117 | + gatewayAccessToken = gatewayCredentials.getCredentialsId(); | ||
118 | + assertNotNull(gatewayAccessToken); | ||
119 | + | ||
120 | + } | ||
121 | + | ||
122 | + protected void processAfterTest() throws Exception { | ||
123 | + loginSysAdmin(); | ||
124 | + if (savedTenant != null) { | ||
125 | + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); | ||
126 | + } | ||
127 | + } | ||
128 | + | ||
129 | + protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException { | ||
130 | + String clientId = MqttAsyncClient.generateClientId(); | ||
131 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
132 | + | ||
133 | + MqttConnectOptions options = new MqttConnectOptions(); | ||
134 | + options.setUserName(accessToken); | ||
135 | + client.connect(options).waitForCompletion(); | ||
136 | + return client; | ||
137 | + } | ||
138 | + | ||
139 | + protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException { | ||
140 | + MqttMessage message = new MqttMessage(); | ||
141 | + message.setPayload(payload); | ||
142 | + client.publish(topic, message); | ||
143 | + } | ||
144 | + | ||
145 | + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { | ||
146 | + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); | ||
147 | + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); | ||
148 | + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); | ||
149 | + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "3.0", TransportProtos.KeyValueType.DOUBLE_V); | ||
150 | + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "4", TransportProtos.KeyValueType.LONG_V); | ||
151 | + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); | ||
152 | + keyValueProtos.add(strKeyValueProto); | ||
153 | + keyValueProtos.add(boolKeyValueProto); | ||
154 | + keyValueProtos.add(dblKeyValueProto); | ||
155 | + keyValueProtos.add(longKeyValueProto); | ||
156 | + keyValueProtos.add(jsonKeyValueProto); | ||
157 | + return keyValueProtos; | ||
158 | + } | ||
159 | + | ||
160 | + protected TransportProtos.KeyValueProto getKeyValueProto(String key, String strValue, TransportProtos.KeyValueType type) { | ||
161 | + TransportProtos.KeyValueProto.Builder keyValueProtoBuilder = TransportProtos.KeyValueProto.newBuilder(); | ||
162 | + keyValueProtoBuilder.setKey(key); | ||
163 | + keyValueProtoBuilder.setType(type); | ||
164 | + switch (type) { | ||
165 | + case BOOLEAN_V: | ||
166 | + keyValueProtoBuilder.setBoolV(Boolean.parseBoolean(strValue)); | ||
167 | + break; | ||
168 | + case LONG_V: | ||
169 | + keyValueProtoBuilder.setLongV(Long.parseLong(strValue)); | ||
170 | + break; | ||
171 | + case DOUBLE_V: | ||
172 | + keyValueProtoBuilder.setDoubleV(Double.parseDouble(strValue)); | ||
173 | + break; | ||
174 | + case STRING_V: | ||
175 | + keyValueProtoBuilder.setStringV(strValue); | ||
176 | + break; | ||
177 | + case JSON_V: | ||
178 | + keyValueProtoBuilder.setJsonV(strValue); | ||
179 | + break; | ||
180 | + } | ||
181 | + return keyValueProtoBuilder.build(); | ||
182 | + } | ||
183 | + | ||
184 | + protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) { | ||
185 | + DeviceProfile deviceProfile = new DeviceProfile(); | ||
186 | + deviceProfile.setName(transportPayloadType.name()); | ||
187 | + deviceProfile.setType(DeviceProfileType.DEFAULT); | ||
188 | + deviceProfile.setTransportType(DeviceTransportType.MQTT); | ||
189 | + deviceProfile.setDescription(transportPayloadType.name() + " Test"); | ||
190 | + DeviceProfileData deviceProfileData = new DeviceProfileData(); | ||
191 | + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | ||
192 | + MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); | ||
193 | + transportConfiguration.setTransportPayloadType(transportPayloadType); | ||
194 | + if (!StringUtils.isEmpty(telemetryTopic)) { | ||
195 | + transportConfiguration.setDeviceTelemetryTopic(telemetryTopic); | ||
196 | + } | ||
197 | + if (!StringUtils.isEmpty(attributesTopic)) { | ||
198 | + transportConfiguration.setDeviceAttributesTopic(attributesTopic); | ||
199 | + } | ||
200 | + deviceProfileData.setTransportConfiguration(transportConfiguration); | ||
201 | + deviceProfileData.setConfiguration(configuration); | ||
202 | + deviceProfile.setProfileData(deviceProfileData); | ||
203 | + deviceProfile.setDefault(false); | ||
204 | + deviceProfile.setDefaultRuleChainId(null); | ||
205 | + return deviceProfile; | ||
206 | + } | ||
207 | + | ||
208 | + protected TransportProtos.PostAttributeMsg getPostAttributeMsg(List<String> expectedKeys) { | ||
209 | + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); | ||
210 | + TransportProtos.PostAttributeMsg.Builder builder = TransportProtos.PostAttributeMsg.newBuilder(); | ||
211 | + builder.addAllKv(kvProtos); | ||
212 | + return builder.build(); | ||
213 | + } | ||
214 | + | ||
215 | +} |
@@ -27,7 +27,11 @@ import java.util.Arrays; | @@ -27,7 +27,11 @@ import java.util.Arrays; | ||
27 | @RunWith(ClasspathSuite.class) | 27 | @RunWith(ClasspathSuite.class) |
28 | @ClasspathSuite.ClassnameFilters({ | 28 | @ClasspathSuite.ClassnameFilters({ |
29 | "org.thingsboard.server.mqtt.rpc.sql.*Test", | 29 | "org.thingsboard.server.mqtt.rpc.sql.*Test", |
30 | - "org.thingsboard.server.mqtt.telemetry.sql.*Test" | 30 | + "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test", |
31 | + "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", | ||
32 | + "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", | ||
33 | + "org.thingsboard.server.mqtt.attributes.request.sql.*Test", | ||
34 | + "org.thingsboard.server.mqtt.claim.sql.*Test" | ||
31 | }) | 35 | }) |
32 | public class MqttSqlTestSuite { | 36 | public class MqttSqlTestSuite { |
33 | 37 |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
24 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
25 | + | ||
26 | +import java.util.ArrayList; | ||
27 | +import java.util.List; | ||
28 | +import java.util.concurrent.CountDownLatch; | ||
29 | + | ||
30 | +@Slf4j | ||
31 | +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { | ||
32 | + | ||
33 | + protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," + | ||
34 | + "\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}"; | ||
35 | + | ||
36 | + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { | ||
37 | + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic); | ||
38 | + } | ||
39 | + | ||
40 | + protected void processAfterTest() throws Exception { | ||
41 | + super.processAfterTest(); | ||
42 | + } | ||
43 | + | ||
44 | + protected List<TransportProtos.TsKvProto> getTsKvProtoList() { | ||
45 | + TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V); | ||
46 | + TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V); | ||
47 | + TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V); | ||
48 | + TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V); | ||
49 | + TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V); | ||
50 | + List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>(); | ||
51 | + tsKvProtoList.add(tsKvProtoAttribute1); | ||
52 | + tsKvProtoList.add(tsKvProtoAttribute2); | ||
53 | + tsKvProtoList.add(tsKvProtoAttribute3); | ||
54 | + tsKvProtoList.add(tsKvProtoAttribute4); | ||
55 | + tsKvProtoList.add(tsKvProtoAttribute5); | ||
56 | + return tsKvProtoList; | ||
57 | + } | ||
58 | + | ||
59 | + | ||
60 | + protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) { | ||
61 | + TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder(); | ||
62 | + TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType); | ||
63 | + tsKvProtoBuilder.setKv(keyValueProto); | ||
64 | + return tsKvProtoBuilder.build(); | ||
65 | + } | ||
66 | + | ||
67 | + protected TestMqttCallback getTestMqttCallback() { | ||
68 | + CountDownLatch latch = new CountDownLatch(1); | ||
69 | + return new TestMqttCallback(latch); | ||
70 | + } | ||
71 | + | ||
72 | + protected static class TestMqttCallback implements MqttCallback { | ||
73 | + | ||
74 | + private final CountDownLatch latch; | ||
75 | + private Integer qoS; | ||
76 | + private byte[] payloadBytes; | ||
77 | + | ||
78 | + TestMqttCallback(CountDownLatch latch) { | ||
79 | + this.latch = latch; | ||
80 | + } | ||
81 | + | ||
82 | + public int getQoS() { | ||
83 | + return qoS; | ||
84 | + } | ||
85 | + | ||
86 | + public byte[] getPayloadBytes() { | ||
87 | + return payloadBytes; | ||
88 | + } | ||
89 | + | ||
90 | + public CountDownLatch getLatch() { | ||
91 | + return latch; | ||
92 | + } | ||
93 | + | ||
94 | + @Override | ||
95 | + public void connectionLost(Throwable throwable) { | ||
96 | + } | ||
97 | + | ||
98 | + @Override | ||
99 | + public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { | ||
100 | + qoS = mqttMessage.getQos(); | ||
101 | + payloadBytes = mqttMessage.getPayload(); | ||
102 | + latch.countDown(); | ||
103 | + } | ||
104 | + | ||
105 | + @Override | ||
106 | + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
107 | + | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
25 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
31 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
32 | +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; | ||
33 | + | ||
34 | +import java.nio.charset.StandardCharsets; | ||
35 | +import java.util.concurrent.CountDownLatch; | ||
36 | +import java.util.concurrent.TimeUnit; | ||
37 | + | ||
38 | +import static org.junit.Assert.assertEquals; | ||
39 | +import static org.junit.Assert.assertFalse; | ||
40 | +import static org.junit.Assert.assertNotNull; | ||
41 | +import static org.junit.Assert.assertTrue; | ||
42 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
43 | + | ||
44 | +@Slf4j | ||
45 | +public abstract class AbstractMqttAttributesRequestIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
46 | + | ||
47 | + @Before | ||
48 | + public void beforeTest() throws Exception { | ||
49 | + processBeforeTest("Test Request attribute values from the server", "Gateway Test Request attribute values from the server", null, null, null); | ||
50 | + } | ||
51 | + | ||
52 | + @After | ||
53 | + public void afterTest() throws Exception { | ||
54 | + processAfterTest(); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void testRequestAttributesValuesFromTheServer() throws Exception { | ||
59 | + processTestRequestAttributesValuesFromTheServer(); | ||
60 | + } | ||
61 | + | ||
62 | + @Test | ||
63 | + public void testRequestAttributesValuesFromTheServerGateway() throws Exception { | ||
64 | + processTestGatewayRequestAttributesValuesFromTheServer(); | ||
65 | + } | ||
66 | + | ||
67 | + protected void processTestRequestAttributesValuesFromTheServer() throws Exception { | ||
68 | + | ||
69 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
70 | + | ||
71 | + postAttributesAndSubscribeToTopic(savedDevice, client); | ||
72 | + | ||
73 | + Thread.sleep(1000); | ||
74 | + | ||
75 | + TestMqttCallback callback = getTestMqttCallback(); | ||
76 | + client.setCallback(callback); | ||
77 | + | ||
78 | + validateResponse(client, callback.getLatch(), callback); | ||
79 | + } | ||
80 | + | ||
81 | + protected void processTestGatewayRequestAttributesValuesFromTheServer() throws Exception { | ||
82 | + | ||
83 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
84 | + | ||
85 | + postGatewayDeviceClientAttributes(client); | ||
86 | + | ||
87 | + Thread.sleep(1000); | ||
88 | + | ||
89 | + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class); | ||
90 | + assertNotNull(savedDevice); | ||
91 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
92 | + | ||
93 | + Thread.sleep(1000); | ||
94 | + | ||
95 | + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value()); | ||
96 | + | ||
97 | + TestMqttCallback clientAttributesCallback = getTestMqttCallback(); | ||
98 | + client.setCallback(clientAttributesCallback); | ||
99 | + validateClientResponseGateway(client, clientAttributesCallback); | ||
100 | + | ||
101 | + TestMqttCallback sharedAttributesCallback = getTestMqttCallback(); | ||
102 | + client.setCallback(sharedAttributesCallback); | ||
103 | + validateSharedResponseGateway(client, sharedAttributesCallback); | ||
104 | + } | ||
105 | + | ||
106 | + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { | ||
107 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
108 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes())); | ||
109 | + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
110 | + } | ||
111 | + | ||
112 | + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { | ||
113 | + String postClientAttributes = "{\"" + "Gateway Device Request Attributes" + "\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
114 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes())); | ||
115 | + } | ||
116 | + | ||
117 | + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
118 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
119 | + String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}"; | ||
120 | + MqttMessage mqttMessage = new MqttMessage(); | ||
121 | + mqttMessage.setPayload(payloadStr.getBytes()); | ||
122 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); | ||
123 | + latch.await(3, TimeUnit.SECONDS); | ||
124 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
125 | + String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
126 | + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | ||
127 | + } | ||
128 | + | ||
129 | + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
130 | + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; | ||
131 | + MqttMessage mqttMessage = new MqttMessage(); | ||
132 | + mqttMessage.setPayload(payloadStr.getBytes()); | ||
133 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); | ||
134 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
135 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
136 | + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
137 | + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | ||
138 | + } | ||
139 | + | ||
140 | + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
141 | + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; | ||
142 | + MqttMessage mqttMessage = new MqttMessage(); | ||
143 | + mqttMessage.setPayload(payloadStr.getBytes()); | ||
144 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); | ||
145 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
146 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
147 | + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
148 | + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | ||
149 | + } | ||
150 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Ignore; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
24 | + | ||
25 | +import static org.junit.Assert.assertEquals; | ||
26 | +import static org.junit.Assert.assertNotNull; | ||
27 | +import static org.junit.Assert.assertTrue; | ||
28 | + | ||
29 | +@Slf4j | ||
30 | +public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
31 | + | ||
32 | + @Before | ||
33 | + public void beforeTest() throws Exception { | ||
34 | + processBeforeTest("Test Request attribute values from the server json", "Gateway Test Request attribute values from the server json", TransportPayloadType.JSON, null, null); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void afterTest() throws Exception { | ||
39 | + processAfterTest(); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void testRequestAttributesValuesFromTheServer() throws Exception { | ||
44 | + processTestRequestAttributesValuesFromTheServer(); | ||
45 | + } | ||
46 | + | ||
47 | + @Test | ||
48 | + public void testRequestAttributesValuesFromTheServerGateway() throws Exception { | ||
49 | + processTestGatewayRequestAttributesValuesFromTheServer(); | ||
50 | + } | ||
51 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
24 | +import org.junit.After; | ||
25 | +import org.junit.Before; | ||
26 | +import org.junit.Test; | ||
27 | +import org.thingsboard.server.common.data.Device; | ||
28 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
29 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
30 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
31 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
32 | + | ||
33 | +import java.util.ArrayList; | ||
34 | +import java.util.Arrays; | ||
35 | +import java.util.List; | ||
36 | +import java.util.concurrent.CountDownLatch; | ||
37 | +import java.util.concurrent.TimeUnit; | ||
38 | +import java.util.stream.Collectors; | ||
39 | + | ||
40 | +import static org.junit.Assert.assertEquals; | ||
41 | +import static org.junit.Assert.assertTrue; | ||
42 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
43 | + | ||
44 | +@Slf4j | ||
45 | +public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
46 | + | ||
47 | + @Before | ||
48 | + public void beforeTest() throws Exception { | ||
49 | + processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null); | ||
50 | + } | ||
51 | + | ||
52 | + @After | ||
53 | + public void afterTest() throws Exception { | ||
54 | + processAfterTest(); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void testRequestAttributesValuesFromTheServer() throws Exception { | ||
59 | + processTestRequestAttributesValuesFromTheServer(); | ||
60 | + } | ||
61 | + | ||
62 | + | ||
63 | + @Test | ||
64 | + public void testRequestAttributesValuesFromTheServerGateway() throws Exception { | ||
65 | + processTestGatewayRequestAttributesValuesFromTheServer(); | ||
66 | + } | ||
67 | + | ||
68 | + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { | ||
69 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
70 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
71 | + List<String> expectedKeys = Arrays.asList(keys.split(",")); | ||
72 | + TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); | ||
73 | + byte[] payload = postAttributeMsg.toByteArray(); | ||
74 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); | ||
75 | + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
76 | + } | ||
77 | + | ||
78 | + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { | ||
79 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
80 | + List<String> expectedKeys = Arrays.asList(keys.split(",")); | ||
81 | + TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); | ||
82 | + TransportApiProtos.AttributesMsg.Builder attributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); | ||
83 | + attributesMsgBuilder.setDeviceName("Gateway Device Request Attributes"); | ||
84 | + attributesMsgBuilder.setMsg(postAttributeMsg); | ||
85 | + TransportApiProtos.AttributesMsg attributesMsg = attributesMsgBuilder.build(); | ||
86 | + TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributeMsgBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); | ||
87 | + gatewayAttributeMsgBuilder.addMsg(attributesMsg); | ||
88 | + byte[] bytes = gatewayAttributeMsgBuilder.build().toByteArray(); | ||
89 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes)); | ||
90 | + } | ||
91 | + | ||
92 | + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
93 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
94 | + TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder(); | ||
95 | + attributesRequestBuilder.setClientKeys(keys); | ||
96 | + attributesRequestBuilder.setSharedKeys(keys); | ||
97 | + TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build(); | ||
98 | + MqttMessage mqttMessage = new MqttMessage(); | ||
99 | + mqttMessage.setPayload(attributesRequest.toByteArray()); | ||
100 | + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); | ||
101 | + latch.await(3, TimeUnit.SECONDS); | ||
102 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
103 | + TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg(); | ||
104 | + TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); | ||
105 | + assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId()); | ||
106 | + List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
107 | + List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
108 | + List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
109 | + List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
110 | + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); | ||
111 | + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); | ||
112 | + } | ||
113 | + | ||
114 | + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
115 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
116 | + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, true); | ||
117 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); | ||
118 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
119 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
120 | + TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(true); | ||
121 | + TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); | ||
122 | + assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); | ||
123 | + | ||
124 | + TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); | ||
125 | + TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); | ||
126 | + assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); | ||
127 | + | ||
128 | + List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
129 | + List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
130 | + assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); | ||
131 | + } | ||
132 | + | ||
133 | + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { | ||
134 | + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | ||
135 | + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, false); | ||
136 | + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); | ||
137 | + callback.getLatch().await(3, TimeUnit.SECONDS); | ||
138 | + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); | ||
139 | + TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(false); | ||
140 | + TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); | ||
141 | + assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); | ||
142 | + | ||
143 | + TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); | ||
144 | + TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); | ||
145 | + assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); | ||
146 | + | ||
147 | + List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
148 | + List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
149 | + | ||
150 | + assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); | ||
151 | + } | ||
152 | + | ||
153 | + private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String keys, boolean client) { | ||
154 | + return TransportApiProtos.GatewayAttributesRequestMsg.newBuilder() | ||
155 | + .setClient(client) | ||
156 | + .addAllKeys(Arrays.asList(keys.split(","))) | ||
157 | + .setDeviceName("Gateway Device Request Attributes") | ||
158 | + .setId(1).build(); | ||
159 | + } | ||
160 | + | ||
161 | + private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { | ||
162 | + TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder(); | ||
163 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
164 | + result.addAllClientAttributeList(tsKvProtoList); | ||
165 | + result.addAllSharedAttributeList(tsKvProtoList); | ||
166 | + result.setRequestId(1); | ||
167 | + return result.build(); | ||
168 | + } | ||
169 | + | ||
170 | + private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(boolean client) { | ||
171 | + TransportApiProtos.GatewayAttributeResponseMsg.Builder gatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder(); | ||
172 | + TransportProtos.GetAttributeResponseMsg.Builder getAttributeResponseMsgBuilder = TransportProtos.GetAttributeResponseMsg.newBuilder(); | ||
173 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
174 | + if (client) { | ||
175 | + getAttributeResponseMsgBuilder.addAllClientAttributeList(tsKvProtoList); | ||
176 | + } else { | ||
177 | + getAttributeResponseMsgBuilder.addAllSharedAttributeList(tsKvProtoList); | ||
178 | + } | ||
179 | + getAttributeResponseMsgBuilder.setRequestId(1); | ||
180 | + TransportProtos.GetAttributeResponseMsg getAttributeResponseMsg = getAttributeResponseMsgBuilder.build(); | ||
181 | + gatewayAttributeResponseMsg.setDeviceName("Gateway Device Request Attributes"); | ||
182 | + gatewayAttributeResponseMsg.setResponseMsg(getAttributeResponseMsg); | ||
183 | + return gatewayAttributeResponseMsg.build(); | ||
184 | + } | ||
185 | + | ||
186 | + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { | ||
187 | + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); | ||
188 | + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); | ||
189 | + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); | ||
190 | + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "42.0", TransportProtos.KeyValueType.DOUBLE_V); | ||
191 | + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "73", TransportProtos.KeyValueType.LONG_V); | ||
192 | + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); | ||
193 | + keyValueProtos.add(strKeyValueProto); | ||
194 | + keyValueProtos.add(boolKeyValueProto); | ||
195 | + keyValueProtos.add(dblKeyValueProto); | ||
196 | + keyValueProtos.add(longKeyValueProto); | ||
197 | + keyValueProtos.add(jsonKeyValueProto); | ||
198 | + return keyValueProtos; | ||
199 | + } | ||
200 | + | ||
201 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesRequestNoSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesRequestJsonSqlIntegrationTest extends AbstractMqttAttributesRequestJsonIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesRequestProtoSqlIntegrationTest extends AbstractMqttAttributesRequestProtoIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.request.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesRequestSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
25 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
26 | +import org.junit.After; | ||
27 | +import org.junit.Before; | ||
28 | +import org.junit.Test; | ||
29 | +import org.thingsboard.server.common.data.Device; | ||
30 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
31 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
32 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
33 | +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; | ||
34 | + | ||
35 | +import java.nio.charset.StandardCharsets; | ||
36 | +import java.util.concurrent.CountDownLatch; | ||
37 | +import java.util.concurrent.TimeUnit; | ||
38 | + | ||
39 | +import static org.junit.Assert.assertEquals; | ||
40 | +import static org.junit.Assert.assertNotNull; | ||
41 | +import static org.junit.Assert.assertTrue; | ||
42 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
43 | + | ||
44 | +@Slf4j | ||
45 | +public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
46 | + | ||
47 | + private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}"; | ||
48 | + | ||
49 | + private static String getResponseGatewayAttributesUpdatedPayload() { | ||
50 | + return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\"," + | ||
51 | + "\"data\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; | ||
52 | + } | ||
53 | + | ||
54 | + private static String getResponseGatewayAttributesDeletedPayload() { | ||
55 | + return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\",\"data\":{\"deleted\":[\"attribute5\"]}}"; | ||
56 | + } | ||
57 | + | ||
58 | + @Before | ||
59 | + public void beforeTest() throws Exception { | ||
60 | + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); | ||
61 | + } | ||
62 | + | ||
63 | + @After | ||
64 | + public void afterTest() throws Exception { | ||
65 | + processAfterTest(); | ||
66 | + } | ||
67 | + | ||
68 | + @Test | ||
69 | + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { | ||
70 | + processTestSubscribeToAttributesUpdates(); | ||
71 | + } | ||
72 | + | ||
73 | + @Test | ||
74 | + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { | ||
75 | + processGatewayTestSubscribeToAttributesUpdates(); | ||
76 | + } | ||
77 | + | ||
78 | + protected void processTestSubscribeToAttributesUpdates() throws Exception { | ||
79 | + | ||
80 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
81 | + | ||
82 | + TestMqttCallback onUpdateCallback = getTestMqttCallback(); | ||
83 | + client.setCallback(onUpdateCallback); | ||
84 | + | ||
85 | + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
86 | + | ||
87 | + Thread.sleep(2000); | ||
88 | + | ||
89 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
90 | + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
91 | + | ||
92 | + validateUpdateAttributesResponse(onUpdateCallback); | ||
93 | + | ||
94 | + TestMqttCallback onDeleteCallback = getTestMqttCallback(); | ||
95 | + client.setCallback(onDeleteCallback); | ||
96 | + | ||
97 | + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); | ||
98 | + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
99 | + | ||
100 | + validateDeleteAttributesResponse(onDeleteCallback); | ||
101 | + } | ||
102 | + | ||
103 | + protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
104 | + assertNotNull(callback.getPayloadBytes()); | ||
105 | + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
106 | + assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response)); | ||
107 | + } | ||
108 | + | ||
109 | + protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
110 | + assertNotNull(callback.getPayloadBytes()); | ||
111 | + String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
112 | + assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response)); | ||
113 | + } | ||
114 | + | ||
115 | + protected void processGatewayTestSubscribeToAttributesUpdates() throws Exception { | ||
116 | + | ||
117 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
118 | + | ||
119 | + TestMqttCallback onUpdateCallback = getTestMqttCallback(); | ||
120 | + client.setCallback(onUpdateCallback); | ||
121 | + | ||
122 | + Device device = new Device(); | ||
123 | + device.setName("Gateway Device Subscribe to attribute updates"); | ||
124 | + device.setType("default"); | ||
125 | + | ||
126 | + byte[] connectPayloadBytes = getConnectPayloadBytes(); | ||
127 | + | ||
128 | + publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
129 | + | ||
130 | + Thread.sleep(1000); | ||
131 | + | ||
132 | + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class); | ||
133 | + assertNotNull(savedDevice); | ||
134 | + | ||
135 | + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); | ||
136 | + | ||
137 | + Thread.sleep(2000); | ||
138 | + | ||
139 | + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); | ||
140 | + onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
141 | + | ||
142 | + validateGatewayUpdateAttributesResponse(onUpdateCallback); | ||
143 | + | ||
144 | + TestMqttCallback onDeleteCallback = getTestMqttCallback(); | ||
145 | + client.setCallback(onDeleteCallback); | ||
146 | + | ||
147 | + doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); | ||
148 | + onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); | ||
149 | + | ||
150 | + validateGatewayDeleteAttributesResponse(onDeleteCallback); | ||
151 | + | ||
152 | + } | ||
153 | + | ||
154 | + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
155 | + assertNotNull(callback.getPayloadBytes()); | ||
156 | + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
157 | + assertEquals(getResponseGatewayAttributesUpdatedPayload(), s); | ||
158 | + } | ||
159 | + | ||
160 | + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
161 | + assertNotNull(callback.getPayloadBytes()); | ||
162 | + String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); | ||
163 | + assertEquals(s, getResponseGatewayAttributesDeletedPayload()); | ||
164 | + } | ||
165 | + | ||
166 | + protected byte[] getConnectPayloadBytes() { | ||
167 | + String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; | ||
168 | + return connectPayload.getBytes(); | ||
169 | + } | ||
170 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | + | ||
24 | +import static org.junit.Assert.assertEquals; | ||
25 | +import static org.junit.Assert.assertFalse; | ||
26 | +import static org.junit.Assert.assertNotNull; | ||
27 | +import static org.junit.Assert.assertTrue; | ||
28 | + | ||
29 | +@Slf4j | ||
30 | +public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { | ||
31 | + | ||
32 | + @Before | ||
33 | + public void beforeTest() throws Exception { | ||
34 | + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void afterTest() throws Exception { | ||
39 | + processAfterTest(); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { | ||
44 | + processTestSubscribeToAttributesUpdates(); | ||
45 | + } | ||
46 | + | ||
47 | + @Test | ||
48 | + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { | ||
49 | + processGatewayTestSubscribeToAttributesUpdates(); | ||
50 | + } | ||
51 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
24 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
25 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
26 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
27 | + | ||
28 | +import java.nio.charset.StandardCharsets; | ||
29 | +import java.util.List; | ||
30 | +import java.util.stream.Collectors; | ||
31 | + | ||
32 | +import static org.junit.Assert.assertEquals; | ||
33 | +import static org.junit.Assert.assertNotNull; | ||
34 | +import static org.junit.Assert.assertTrue; | ||
35 | + | ||
36 | +@Slf4j | ||
37 | +public abstract class AbstractMqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { | ||
38 | + | ||
39 | + @Before | ||
40 | + public void beforeTest() throws Exception { | ||
41 | + processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.PROTOBUF, null, null); | ||
42 | + } | ||
43 | + | ||
44 | + @After | ||
45 | + public void afterTest() throws Exception { | ||
46 | + processAfterTest(); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
50 | + public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { | ||
51 | + processTestSubscribeToAttributesUpdates(); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { | ||
56 | + processGatewayTestSubscribeToAttributesUpdates(); | ||
57 | + } | ||
58 | + | ||
59 | + protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
60 | + assertNotNull(callback.getPayloadBytes()); | ||
61 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
62 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
63 | + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); | ||
64 | + | ||
65 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
66 | + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
67 | + | ||
68 | + List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
69 | + List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
70 | + | ||
71 | + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); | ||
72 | + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); | ||
73 | + | ||
74 | + } | ||
75 | + | ||
76 | + protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
77 | + assertNotNull(callback.getPayloadBytes()); | ||
78 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
79 | + attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); | ||
80 | + | ||
81 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
82 | + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
83 | + | ||
84 | + assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); | ||
85 | + assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); | ||
86 | + | ||
87 | + } | ||
88 | + | ||
89 | + protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
90 | + assertNotNull(callback.getPayloadBytes()); | ||
91 | + | ||
92 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
93 | + List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); | ||
94 | + attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); | ||
95 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
96 | + | ||
97 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); | ||
98 | + gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); | ||
99 | + gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(expectedAttributeUpdateNotificationMsg); | ||
100 | + | ||
101 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); | ||
102 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
103 | + | ||
104 | + assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); | ||
105 | + | ||
106 | + List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
107 | + List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); | ||
108 | + | ||
109 | + assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); | ||
110 | + assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); | ||
111 | + | ||
112 | + } | ||
113 | + | ||
114 | + protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { | ||
115 | + assertNotNull(callback.getPayloadBytes()); | ||
116 | + TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); | ||
117 | + attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); | ||
118 | + TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); | ||
119 | + | ||
120 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); | ||
121 | + gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); | ||
122 | + gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(attributeUpdateNotificationMsg); | ||
123 | + | ||
124 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); | ||
125 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); | ||
126 | + | ||
127 | + assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); | ||
128 | + | ||
129 | + TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); | ||
130 | + TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); | ||
131 | + | ||
132 | + assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); | ||
133 | + assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); | ||
134 | + | ||
135 | + } | ||
136 | + | ||
137 | + protected byte[] getConnectPayloadBytes() { | ||
138 | + TransportApiProtos.ConnectMsg connectProto = getConnectProto(); | ||
139 | + return connectProto.toByteArray(); | ||
140 | + } | ||
141 | + | ||
142 | + private TransportApiProtos.ConnectMsg getConnectProto() { | ||
143 | + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); | ||
144 | + builder.setDeviceName("Gateway Device Subscribe to attribute updates"); | ||
145 | + builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); | ||
146 | + return builder.build(); | ||
147 | + } | ||
148 | + | ||
149 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesUpdatesNoSqlIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesUpdatesSqlIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesUpdatesSqlJsonIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.attributes.updates.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesUpdatesSqlProtoIntegrationTest extends AbstractMqttAttributesUpdatesProtoIntegrationTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
21 | +import org.junit.After; | ||
22 | +import org.junit.Before; | ||
23 | +import org.junit.Test; | ||
24 | +import org.thingsboard.server.common.data.ClaimRequest; | ||
25 | +import org.thingsboard.server.common.data.Customer; | ||
26 | +import org.thingsboard.server.common.data.Device; | ||
27 | +import org.thingsboard.server.common.data.User; | ||
28 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
29 | +import org.thingsboard.server.common.data.security.Authority; | ||
30 | +import org.thingsboard.server.dao.device.claim.ClaimResponse; | ||
31 | +import org.thingsboard.server.dao.device.claim.ClaimResult; | ||
32 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
33 | + | ||
34 | +import static org.junit.Assert.assertEquals; | ||
35 | +import static org.junit.Assert.assertNotNull; | ||
36 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
37 | + | ||
38 | +@Slf4j | ||
39 | +public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegrationTest { | ||
40 | + | ||
41 | + protected static final String CUSTOMER_USER_PASSWORD = "customerUser123!"; | ||
42 | + | ||
43 | + protected User customerAdmin; | ||
44 | + protected Customer savedCustomer; | ||
45 | + | ||
46 | + @Before | ||
47 | + public void beforeTest() throws Exception { | ||
48 | + super.processBeforeTest("Test Claim device", "Test Claim gateway", null, null, null); | ||
49 | + createCustomerAndUser(); | ||
50 | + } | ||
51 | + | ||
52 | + protected void createCustomerAndUser() throws Exception { | ||
53 | + Customer customer = new Customer(); | ||
54 | + customer.setTenantId(savedTenant.getId()); | ||
55 | + customer.setTitle("Test Claiming Customer"); | ||
56 | + savedCustomer = doPost("/api/customer", customer, Customer.class); | ||
57 | + assertNotNull(savedCustomer); | ||
58 | + assertEquals(savedTenant.getId(), savedCustomer.getTenantId()); | ||
59 | + | ||
60 | + User user = new User(); | ||
61 | + user.setAuthority(Authority.CUSTOMER_USER); | ||
62 | + user.setTenantId(savedTenant.getId()); | ||
63 | + user.setCustomerId(savedCustomer.getId()); | ||
64 | + user.setEmail("customer@thingsboard.org"); | ||
65 | + | ||
66 | + customerAdmin = createUser(user, CUSTOMER_USER_PASSWORD); | ||
67 | + assertNotNull(customerAdmin); | ||
68 | + assertEquals(customerAdmin.getCustomerId(), savedCustomer.getId()); | ||
69 | + } | ||
70 | + | ||
71 | + @After | ||
72 | + public void afterTest() throws Exception { | ||
73 | + super.processAfterTest(); | ||
74 | + } | ||
75 | + | ||
76 | + @Test | ||
77 | + public void testClaimingDevice() throws Exception { | ||
78 | + processTestClaimingDevice(false); | ||
79 | + } | ||
80 | + | ||
81 | + @Test | ||
82 | + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
83 | + processTestClaimingDevice(true); | ||
84 | + } | ||
85 | + | ||
86 | + @Test | ||
87 | + public void testGatewayClaimingDevice() throws Exception { | ||
88 | + processTestGatewayClaimingDevice("Test claiming gateway device", false); | ||
89 | + } | ||
90 | + | ||
91 | + @Test | ||
92 | + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
93 | + processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true); | ||
94 | + } | ||
95 | + | ||
96 | + | ||
97 | + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { | ||
98 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
99 | + byte[] payloadBytes; | ||
100 | + byte[] failurePayloadBytes; | ||
101 | + if (emptyPayload) { | ||
102 | + payloadBytes = "{}".getBytes(); | ||
103 | + failurePayloadBytes = "{\"durationMs\":1}".getBytes(); | ||
104 | + } else { | ||
105 | + payloadBytes = "{\"secretKey\":\"value\", \"durationMs\":60000}".getBytes(); | ||
106 | + failurePayloadBytes = "{\"secretKey\":\"value\", \"durationMs\":1}".getBytes(); | ||
107 | + } | ||
108 | + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); | ||
109 | + } | ||
110 | + | ||
111 | + protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception { | ||
112 | + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); | ||
113 | + | ||
114 | + Thread.sleep(2000); | ||
115 | + | ||
116 | + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); | ||
117 | + ClaimRequest claimRequest; | ||
118 | + if (!emptyPayload) { | ||
119 | + claimRequest = new ClaimRequest("value"); | ||
120 | + } else { | ||
121 | + claimRequest = new ClaimRequest(null); | ||
122 | + } | ||
123 | + | ||
124 | + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
125 | + assertEquals(claimResponse, ClaimResponse.FAILURE); | ||
126 | + | ||
127 | + client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes)); | ||
128 | + | ||
129 | + Thread.sleep(2000); | ||
130 | + | ||
131 | + ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()); | ||
132 | + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); | ||
133 | + Device claimedDevice = claimResult.getDevice(); | ||
134 | + assertNotNull(claimedDevice); | ||
135 | + assertNotNull(claimedDevice.getCustomerId()); | ||
136 | + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); | ||
137 | + | ||
138 | + claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
139 | + assertEquals(claimResponse, ClaimResponse.CLAIMED); | ||
140 | + } | ||
141 | + | ||
142 | + protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception { | ||
143 | + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); | ||
144 | + | ||
145 | + Thread.sleep(2000); | ||
146 | + | ||
147 | + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); | ||
148 | + assertNotNull(savedDevice); | ||
149 | + | ||
150 | + loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); | ||
151 | + ClaimRequest claimRequest; | ||
152 | + if (!emptyPayload) { | ||
153 | + claimRequest = new ClaimRequest("value"); | ||
154 | + } else { | ||
155 | + claimRequest = new ClaimRequest(null); | ||
156 | + } | ||
157 | + | ||
158 | + ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
159 | + assertEquals(claimResponse, ClaimResponse.FAILURE); | ||
160 | + | ||
161 | + client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes)); | ||
162 | + | ||
163 | + Thread.sleep(2000); | ||
164 | + | ||
165 | + ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()); | ||
166 | + assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); | ||
167 | + Device claimedDevice = claimResult.getDevice(); | ||
168 | + assertNotNull(claimedDevice); | ||
169 | + assertNotNull(claimedDevice.getCustomerId()); | ||
170 | + assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); | ||
171 | + | ||
172 | + claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); | ||
173 | + assertEquals(claimResponse, ClaimResponse.CLAIMED); | ||
174 | + } | ||
175 | + | ||
176 | + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { | ||
177 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
178 | + byte[] failurePayloadBytes; | ||
179 | + byte[] payloadBytes; | ||
180 | + String failurePayload; | ||
181 | + String payload; | ||
182 | + if (emptyPayload) { | ||
183 | + failurePayload = "{\"" + deviceName + "\": " + "{\"durationMs\":1}" + "}"; | ||
184 | + payload = "{\"" + deviceName + "\": " + "{}" + "}"; | ||
185 | + } else { | ||
186 | + failurePayload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":1}" + "}"; | ||
187 | + payload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":60000}" + "}"; | ||
188 | + } | ||
189 | + payloadBytes = payload.getBytes(); | ||
190 | + failurePayloadBytes = failurePayload.getBytes(); | ||
191 | + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); | ||
192 | + } | ||
193 | + | ||
194 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | + | ||
24 | +@Slf4j | ||
25 | +public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimDeviceTest { | ||
26 | + | ||
27 | + @Before | ||
28 | + public void beforeTest() throws Exception { | ||
29 | + super.processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.JSON, null, null); | ||
30 | + createCustomerAndUser(); | ||
31 | + } | ||
32 | + | ||
33 | + @After | ||
34 | + public void afterTest() throws Exception { | ||
35 | + super.afterTest(); | ||
36 | + } | ||
37 | + | ||
38 | + @Test | ||
39 | + public void testClaimingDevice() throws Exception { | ||
40 | + processTestClaimingDevice(false); | ||
41 | + } | ||
42 | + | ||
43 | + @Test | ||
44 | + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
45 | + processTestClaimingDevice(true); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testGatewayClaimingDevice() throws Exception { | ||
50 | + processTestGatewayClaimingDevice("Test claiming gateway device Json", false); | ||
51 | + } | ||
52 | + | ||
53 | + @Test | ||
54 | + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
55 | + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true); | ||
56 | + } | ||
57 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
24 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
25 | + | ||
26 | +@Slf4j | ||
27 | +public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaimDeviceTest { | ||
28 | + | ||
29 | + @Before | ||
30 | + public void beforeTest() throws Exception { | ||
31 | + processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.PROTOBUF, null, null); | ||
32 | + createCustomerAndUser(); | ||
33 | + } | ||
34 | + | ||
35 | + @After | ||
36 | + public void afterTest() throws Exception { super.afterTest(); } | ||
37 | + | ||
38 | + @Test | ||
39 | + public void testClaimingDevice() throws Exception { | ||
40 | + processTestClaimingDevice(false); | ||
41 | + } | ||
42 | + | ||
43 | + @Test | ||
44 | + public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
45 | + processTestClaimingDevice(true); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testGatewayClaimingDevice() throws Exception { | ||
50 | + processTestGatewayClaimingDevice("Test claiming gateway device Proto", false); | ||
51 | + } | ||
52 | + | ||
53 | + @Test | ||
54 | + public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { | ||
55 | + processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true); | ||
56 | + } | ||
57 | + | ||
58 | + protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { | ||
59 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
60 | + byte[] payloadBytes; | ||
61 | + if (emptyPayload) { | ||
62 | + payloadBytes = getClaimDevice(0, emptyPayload).toByteArray(); | ||
63 | + } else { | ||
64 | + payloadBytes = getClaimDevice(60000, emptyPayload).toByteArray(); | ||
65 | + } | ||
66 | + byte[] failurePayloadBytes = getClaimDevice(1, emptyPayload).toByteArray(); | ||
67 | + validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); | ||
68 | + } | ||
69 | + | ||
70 | + protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { | ||
71 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
72 | + byte[] failurePayloadBytes; | ||
73 | + byte[] payloadBytes; | ||
74 | + if (emptyPayload) { | ||
75 | + payloadBytes = getGatewayClaimMsg(deviceName, 0, emptyPayload).toByteArray(); | ||
76 | + } else { | ||
77 | + payloadBytes = getGatewayClaimMsg(deviceName, 60000, emptyPayload).toByteArray(); | ||
78 | + } | ||
79 | + failurePayloadBytes = getGatewayClaimMsg(deviceName, 1, emptyPayload).toByteArray(); | ||
80 | + | ||
81 | + validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); | ||
82 | + } | ||
83 | + | ||
84 | + private TransportApiProtos.GatewayClaimMsg getGatewayClaimMsg(String deviceName, long duration, boolean emptyPayload) { | ||
85 | + TransportApiProtos.GatewayClaimMsg.Builder gatewayClaimMsgBuilder = TransportApiProtos.GatewayClaimMsg.newBuilder(); | ||
86 | + TransportApiProtos.ClaimDeviceMsg.Builder claimDeviceMsgBuilder = TransportApiProtos.ClaimDeviceMsg.newBuilder(); | ||
87 | + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); | ||
88 | + if (!emptyPayload) { | ||
89 | + claimDeviceBuilder.setSecretKey("value"); | ||
90 | + } | ||
91 | + if (duration > 0) { | ||
92 | + claimDeviceBuilder.setDurationMs(duration); | ||
93 | + } | ||
94 | + TransportApiProtos.ClaimDevice claimDevice = claimDeviceBuilder.build(); | ||
95 | + claimDeviceMsgBuilder.setClaimRequest(claimDevice); | ||
96 | + claimDeviceMsgBuilder.setDeviceName(deviceName); | ||
97 | + TransportApiProtos.ClaimDeviceMsg claimDeviceMsg = claimDeviceMsgBuilder.build(); | ||
98 | + gatewayClaimMsgBuilder.addMsg(claimDeviceMsg); | ||
99 | + return gatewayClaimMsgBuilder.build(); | ||
100 | + } | ||
101 | + | ||
102 | + private TransportApiProtos.ClaimDevice getClaimDevice(long duration, boolean emptyPayload) { | ||
103 | + TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); | ||
104 | + if (!emptyPayload) { | ||
105 | + claimDeviceBuilder.setSecretKey("value"); | ||
106 | + } | ||
107 | + if (duration > 0) { | ||
108 | + claimDeviceBuilder.setSecretKey("value"); | ||
109 | + claimDeviceBuilder.setDurationMs(duration); | ||
110 | + } | ||
111 | + return claimDeviceBuilder.build(); | ||
112 | + } | ||
113 | + | ||
114 | + | ||
115 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/nosql/MqttClaimDeviceNoSqlTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttClaimDeviceNoSqlTest extends AbstractMqttClaimDeviceTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceJsonSqlTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | ||
20 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttClaimDeviceJsonSqlTest extends AbstractMqttClaimJsonDeviceTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceProtoSqlTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; | ||
20 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttClaimDeviceProtoSqlTest extends AbstractMqttClaimProtoDeviceTest { | ||
24 | +} |
application/src/test/java/org/thingsboard/server/mqtt/claim/sql/MqttClaimDeviceSqlTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.claim.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttClaimDeviceSqlTest extends AbstractMqttClaimDeviceTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.rpc; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
20 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
21 | +import com.nimbusds.jose.util.StandardCharset; | ||
22 | +import com.datastax.oss.driver.api.core.uuid.Uuids; | ||
23 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.apache.commons.lang3.StringUtils; | ||
26 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
27 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
28 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
29 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
30 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
31 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
32 | +import org.junit.After; | ||
33 | +import org.junit.Assert; | ||
34 | +import org.junit.Before; | ||
35 | +import org.junit.Ignore; | ||
36 | +import org.junit.Test; | ||
37 | +import org.thingsboard.server.common.data.Device; | ||
38 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
39 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
40 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
41 | +import org.thingsboard.server.common.data.Tenant; | ||
42 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
43 | +import org.thingsboard.server.common.data.User; | ||
44 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
45 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
46 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
47 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
48 | +import org.thingsboard.server.common.data.security.Authority; | ||
49 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
50 | +import org.thingsboard.server.controller.AbstractControllerTest; | ||
51 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | ||
52 | +import org.thingsboard.server.service.security.AccessValidator; | ||
53 | + | ||
54 | +import java.util.Arrays; | ||
55 | +import java.util.concurrent.CountDownLatch; | ||
56 | +import java.util.concurrent.TimeUnit; | ||
57 | +import java.util.concurrent.atomic.AtomicInteger; | ||
58 | + | ||
59 | +import static org.junit.Assert.assertEquals; | ||
60 | +import static org.junit.Assert.assertNotNull; | ||
61 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
62 | + | ||
63 | +/** | ||
64 | + * @author Valerii Sosliuk | ||
65 | + */ | ||
66 | +@Slf4j | ||
67 | +public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | ||
68 | + | ||
69 | + @Before | ||
70 | + public void beforeTest() throws Exception { | ||
71 | + processBeforeTest("RPC test device", "RPC test gateway", null, null, null); | ||
72 | + } | ||
73 | + | ||
74 | + @After | ||
75 | + public void afterTest() throws Exception { | ||
76 | + super.processAfterTest(); | ||
77 | + } | ||
78 | + | ||
79 | + @Test | ||
80 | + public void testServerMqttOneWayRpcDeviceOffline() throws Exception { | ||
81 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; | ||
82 | + String deviceId = savedDevice.getId().getId().toString(); | ||
83 | + | ||
84 | + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
85 | + asyncContextTimeoutToUseRpcPlugin); | ||
86 | + } | ||
87 | + | ||
88 | + @Test | ||
89 | + public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { | ||
90 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; | ||
91 | + String nonExistentDeviceId = Uuids.timeBased().toString(); | ||
92 | + | ||
93 | + String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
94 | + status().isNotFound()); | ||
95 | + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | ||
96 | + } | ||
97 | + | ||
98 | + @Test | ||
99 | + public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { | ||
100 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; | ||
101 | + String deviceId = savedDevice.getId().getId().toString(); | ||
102 | + | ||
103 | + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
104 | + asyncContextTimeoutToUseRpcPlugin); | ||
105 | + } | ||
106 | + | ||
107 | + @Test | ||
108 | + public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { | ||
109 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; | ||
110 | + String nonExistentDeviceId = Uuids.timeBased().toString(); | ||
111 | + | ||
112 | + String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
113 | + status().isNotFound()); | ||
114 | + Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | ||
115 | + } | ||
116 | + | ||
117 | + @Test | ||
118 | + public void testServerMqttOneWayRpc() throws Exception { | ||
119 | + processOneWayRpcTest(); | ||
120 | + } | ||
121 | + | ||
122 | + @Test | ||
123 | + public void testServerMqttTwoWayRpc() throws Exception { | ||
124 | + processTwoWayRpcTest(); | ||
125 | + } | ||
126 | + | ||
127 | + @Test | ||
128 | + public void testGatewayServerMqttOneWayRpc() throws Exception { | ||
129 | + processOneWayRpcTestGateway("Gateway Device OneWay RPC"); | ||
130 | + } | ||
131 | + | ||
132 | + @Test | ||
133 | + public void testGatewayServerMqttTwoWayRpc() throws Exception { | ||
134 | + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC"); | ||
135 | + } | ||
136 | + | ||
137 | +} |
@@ -16,6 +16,10 @@ | @@ -16,6 +16,10 @@ | ||
16 | package org.thingsboard.server.mqtt.rpc; | 16 | package org.thingsboard.server.mqtt.rpc; |
17 | 17 | ||
18 | import com.datastax.oss.driver.api.core.uuid.Uuids; | 18 | import com.datastax.oss.driver.api.core.uuid.Uuids; |
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
21 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
22 | +import com.nimbusds.jose.util.StandardCharset; | ||
19 | import io.netty.handler.codec.mqtt.MqttQoS; | 23 | import io.netty.handler.codec.mqtt.MqttQoS; |
20 | import lombok.extern.slf4j.Slf4j; | 24 | import lombok.extern.slf4j.Slf4j; |
21 | import org.apache.commons.lang3.StringUtils; | 25 | import org.apache.commons.lang3.StringUtils; |
@@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | @@ -23,15 +27,28 @@ import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
23 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | 27 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
24 | import org.eclipse.paho.client.mqttv3.MqttCallback; | 28 | import org.eclipse.paho.client.mqttv3.MqttCallback; |
25 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | 29 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
30 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
26 | import org.eclipse.paho.client.mqttv3.MqttMessage; | 31 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
27 | -import org.junit.*; | 32 | +import org.junit.After; |
33 | +import org.junit.Assert; | ||
34 | +import org.junit.Before; | ||
35 | +import org.junit.Test; | ||
28 | import org.thingsboard.server.common.data.Device; | 36 | import org.thingsboard.server.common.data.Device; |
37 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
38 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
39 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
29 | import org.thingsboard.server.common.data.Tenant; | 40 | import org.thingsboard.server.common.data.Tenant; |
41 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
30 | import org.thingsboard.server.common.data.User; | 42 | import org.thingsboard.server.common.data.User; |
43 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
44 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
45 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
46 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
31 | import org.thingsboard.server.common.data.security.Authority; | 47 | import org.thingsboard.server.common.data.security.Authority; |
32 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 48 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
33 | import org.thingsboard.server.controller.AbstractControllerTest; | 49 | import org.thingsboard.server.controller.AbstractControllerTest; |
34 | -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; | 50 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
51 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
35 | import org.thingsboard.server.service.security.AccessValidator; | 52 | import org.thingsboard.server.service.security.AccessValidator; |
36 | 53 | ||
37 | import java.util.Arrays; | 54 | import java.util.Arrays; |
@@ -47,74 +64,88 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. | @@ -47,74 +64,88 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. | ||
47 | * @author Valerii Sosliuk | 64 | * @author Valerii Sosliuk |
48 | */ | 65 | */ |
49 | @Slf4j | 66 | @Slf4j |
50 | -public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest { | 67 | +public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractMqttIntegrationTest { |
51 | 68 | ||
52 | - private static final String MQTT_URL = "tcp://localhost:1883"; | ||
53 | - private static final Long TIME_TO_HANDLE_REQUEST = 500L; | 69 | + protected static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; |
54 | 70 | ||
55 | - private Tenant savedTenant; | ||
56 | - private User tenantAdmin; | ||
57 | - private Long asyncContextTimeoutToUseRpcPlugin; | 71 | + protected Long asyncContextTimeoutToUseRpcPlugin; |
58 | 72 | ||
59 | - private static final AtomicInteger atomicInteger = new AtomicInteger(2); | 73 | + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { |
74 | + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic); | ||
75 | + asyncContextTimeoutToUseRpcPlugin = 10000L; | ||
76 | + } | ||
60 | 77 | ||
78 | + protected void processOneWayRpcTest() throws Exception { | ||
79 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
61 | 80 | ||
62 | - @Before | ||
63 | - public void beforeTest() throws Exception { | ||
64 | - loginSysAdmin(); | 81 | + CountDownLatch latch = new CountDownLatch(1); |
82 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
83 | + client.setCallback(callback); | ||
65 | 84 | ||
66 | - asyncContextTimeoutToUseRpcPlugin = 10000L; | 85 | + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
67 | 86 | ||
68 | - Tenant tenant = new Tenant(); | ||
69 | - tenant.setTitle("My tenant"); | ||
70 | - savedTenant = doPost("/api/tenant", tenant, Tenant.class); | ||
71 | - Assert.assertNotNull(savedTenant); | 87 | + Thread.sleep(2000); |
72 | 88 | ||
73 | - tenantAdmin = new User(); | ||
74 | - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); | ||
75 | - tenantAdmin.setTenantId(savedTenant.getId()); | ||
76 | - tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); | ||
77 | - tenantAdmin.setFirstName("Joe"); | ||
78 | - tenantAdmin.setLastName("Downs"); | 89 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; |
90 | + String deviceId = savedDevice.getId().getId().toString(); | ||
91 | + String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); | ||
92 | + Assert.assertTrue(StringUtils.isEmpty(result)); | ||
93 | + latch.await(3, TimeUnit.SECONDS); | ||
94 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
95 | + } | ||
79 | 96 | ||
80 | - createUserAndLogin(tenantAdmin, "testPassword1"); | 97 | + protected void processOneWayRpcTestGateway(String deviceName) throws Exception { |
98 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
99 | + String payload = "{\"device\":\"" + deviceName + "\"}"; | ||
100 | + byte[] payloadBytes = payload.getBytes(); | ||
101 | + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); | ||
81 | } | 102 | } |
82 | 103 | ||
83 | - @After | ||
84 | - public void afterTest() throws Exception { | ||
85 | - loginSysAdmin(); | ||
86 | - if (savedTenant != null) { | ||
87 | - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); | ||
88 | - } | 104 | + protected void processTwoWayRpcTest() throws Exception { |
105 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
106 | + client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, 1); | ||
107 | + | ||
108 | + CountDownLatch latch = new CountDownLatch(1); | ||
109 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
110 | + client.setCallback(callback); | ||
111 | + | ||
112 | + Thread.sleep(2000); | ||
113 | + | ||
114 | + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; | ||
115 | + String deviceId = savedDevice.getId().getId().toString(); | ||
116 | + | ||
117 | + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); | ||
118 | + String expected = "{\"value1\":\"A\",\"value2\":\"B\"}"; | ||
119 | + latch.await(3, TimeUnit.SECONDS); | ||
120 | + Assert.assertEquals(expected, result); | ||
89 | } | 121 | } |
90 | 122 | ||
91 | - @Test | ||
92 | - public void testServerMqttOneWayRpc() throws Exception { | ||
93 | - Device device = new Device(); | ||
94 | - device.setName("Test One-Way Server-Side RPC"); | ||
95 | - device.setType("default"); | ||
96 | - Device savedDevice = getSavedDevice(device); | ||
97 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
98 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
99 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
100 | - assertNotNull(accessToken); | 123 | + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception { |
124 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
125 | + | ||
126 | + String payload = "{\"device\":\"" + deviceName + "\"}"; | ||
127 | + byte[] payloadBytes = payload.getBytes(); | ||
101 | 128 | ||
102 | - String clientId = MqttAsyncClient.generateClientId(); | ||
103 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | 129 | + validateTwoWayRpcGateway(deviceName, client, payloadBytes); |
130 | + } | ||
104 | 131 | ||
105 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
106 | - options.setUserName(accessToken); | ||
107 | - client.connect(options).waitForCompletion(); | 132 | + protected void validateOneWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception { |
133 | + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
134 | + | ||
135 | + Thread.sleep(2000); | ||
136 | + | ||
137 | + Device savedDevice = getDeviceByName(deviceName); | ||
138 | + assertNotNull(savedDevice); | ||
108 | 139 | ||
109 | CountDownLatch latch = new CountDownLatch(1); | 140 | CountDownLatch latch = new CountDownLatch(1); |
110 | TestMqttCallback callback = new TestMqttCallback(client, latch); | 141 | TestMqttCallback callback = new TestMqttCallback(client, latch); |
111 | client.setCallback(callback); | 142 | client.setCallback(callback); |
112 | 143 | ||
113 | - client.subscribe("v1/devices/me/rpc/request/+", MqttQoS.AT_MOST_ONCE.value()); | 144 | + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
114 | 145 | ||
115 | Thread.sleep(2000); | 146 | Thread.sleep(2000); |
116 | 147 | ||
117 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; | 148 | + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}"; |
118 | String deviceId = savedDevice.getId().getId().toString(); | 149 | String deviceId = savedDevice.getId().getId().toString(); |
119 | String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); | 150 | String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk()); |
120 | Assert.assertTrue(StringUtils.isEmpty(result)); | 151 | Assert.assertTrue(StringUtils.isEmpty(result)); |
@@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | @@ -122,100 +153,49 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | ||
122 | assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | 153 | assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
123 | } | 154 | } |
124 | 155 | ||
125 | - @Test | ||
126 | - public void testServerMqttOneWayRpcDeviceOffline() throws Exception { | ||
127 | - Device device = new Device(); | ||
128 | - device.setName("Test One-Way Server-Side RPC Device Offline"); | ||
129 | - device.setType("default"); | ||
130 | - Device savedDevice = getSavedDevice(device); | ||
131 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
132 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
133 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
134 | - assertNotNull(accessToken); | ||
135 | - | ||
136 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; | ||
137 | - String deviceId = savedDevice.getId().getId().toString(); | 156 | + protected void validateTwoWayRpcGateway(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception { |
157 | + publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
138 | 158 | ||
139 | - doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
140 | - asyncContextTimeoutToUseRpcPlugin); | ||
141 | - } | 159 | + Thread.sleep(2000); |
142 | 160 | ||
143 | - @Test | ||
144 | - public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { | ||
145 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; | ||
146 | - String nonExistentDeviceId = Uuids.timeBased().toString(); | 161 | + Device savedDevice = getDeviceByName(deviceName); |
162 | + assertNotNull(savedDevice); | ||
147 | 163 | ||
148 | - String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
149 | - status().isNotFound()); | ||
150 | - Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | ||
151 | - } | 164 | + CountDownLatch latch = new CountDownLatch(1); |
165 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
166 | + client.setCallback(callback); | ||
152 | 167 | ||
153 | - @Test | ||
154 | - public void testServerMqttTwoWayRpc() throws Exception { | ||
155 | - Device device = new Device(); | ||
156 | - device.setName("Test Two-Way Server-Side RPC"); | ||
157 | - device.setType("default"); | ||
158 | - Device savedDevice = getSavedDevice(device); | ||
159 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
160 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
161 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
162 | - assertNotNull(accessToken); | ||
163 | - | ||
164 | - String clientId = MqttAsyncClient.generateClientId(); | ||
165 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
166 | - | ||
167 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
168 | - options.setUserName(accessToken); | ||
169 | - client.connect(options).waitForCompletion(); | ||
170 | - client.subscribe("v1/devices/me/rpc/request/+", 1); | ||
171 | - client.setCallback(new TestMqttCallback(client, new CountDownLatch(1))); | 168 | + client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
172 | 169 | ||
173 | Thread.sleep(2000); | 170 | Thread.sleep(2000); |
174 | 171 | ||
175 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; | 172 | + String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}"; |
176 | String deviceId = savedDevice.getId().getId().toString(); | 173 | String deviceId = savedDevice.getId().getId().toString(); |
177 | - | ||
178 | String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); | 174 | String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); |
179 | - Assert.assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", result); | ||
180 | - } | ||
181 | - | ||
182 | - @Test | ||
183 | - public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { | ||
184 | - Device device = new Device(); | ||
185 | - device.setName("Test Two-Way Server-Side RPC Device Offline"); | ||
186 | - device.setType("default"); | ||
187 | - Device savedDevice = getSavedDevice(device); | ||
188 | - DeviceCredentials deviceCredentials = getDeviceCredentials(savedDevice); | ||
189 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
190 | - String accessToken = deviceCredentials.getCredentialsId(); | ||
191 | - assertNotNull(accessToken); | ||
192 | - | ||
193 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; | ||
194 | - String deviceId = savedDevice.getId().getId().toString(); | ||
195 | - | ||
196 | - doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), | ||
197 | - asyncContextTimeoutToUseRpcPlugin); | ||
198 | - } | ||
199 | - | ||
200 | - @Test | ||
201 | - public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { | ||
202 | - String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; | ||
203 | - String nonExistentDeviceId = Uuids.timeBased().toString(); | ||
204 | - | ||
205 | - String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, | ||
206 | - status().isNotFound()); | ||
207 | - Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | 175 | + latch.await(3, TimeUnit.SECONDS); |
176 | + String expected = "{\"success\":true}"; | ||
177 | + assertEquals(expected, result); | ||
178 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
208 | } | 179 | } |
209 | 180 | ||
210 | - private Device getSavedDevice(Device device) throws Exception { | ||
211 | - return doPost("/api/device", device, Device.class); | 181 | + private Device getDeviceByName(String deviceName) throws Exception { |
182 | + return doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); | ||
212 | } | 183 | } |
213 | 184 | ||
214 | - private DeviceCredentials getDeviceCredentials(Device savedDevice) throws Exception { | ||
215 | - return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); | 185 | + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { |
186 | + MqttMessage message = new MqttMessage(); | ||
187 | + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { | ||
188 | + message.setPayload(DEVICE_RESPONSE.getBytes(StandardCharset.UTF_8)); | ||
189 | + } else { | ||
190 | + JsonNode requestMsgNode = JacksonUtil.toJsonNode(new String(mqttMessage.getPayload(), StandardCharset.UTF_8)); | ||
191 | + String deviceName = requestMsgNode.get("device").asText(); | ||
192 | + int requestId = requestMsgNode.get("data").get("id").asInt(); | ||
193 | + message.setPayload(("{\"device\": \"" + deviceName + "\", \"id\": " + requestId + ", \"data\": {\"success\": true}}").getBytes(StandardCharset.UTF_8)); | ||
194 | + } | ||
195 | + return message; | ||
216 | } | 196 | } |
217 | 197 | ||
218 | - private static class TestMqttCallback implements MqttCallback { | 198 | + private class TestMqttCallback implements MqttCallback { |
219 | 199 | ||
220 | private final MqttAsyncClient client; | 200 | private final MqttAsyncClient client; |
221 | private final CountDownLatch latch; | 201 | private final CountDownLatch latch; |
@@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | @@ -237,11 +217,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC | ||
237 | @Override | 217 | @Override |
238 | public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { | 218 | public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
239 | log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); | 219 | log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); |
240 | - MqttMessage message = new MqttMessage(); | ||
241 | String responseTopic = requestTopic.replace("request", "response"); | 220 | String responseTopic = requestTopic.replace("request", "response"); |
242 | - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8")); | ||
243 | qoS = mqttMessage.getQos(); | 221 | qoS = mqttMessage.getQos(); |
244 | - client.publish(responseTopic, message); | 222 | + client.publish(responseTopic, processMessageArrived(requestTopic, mqttMessage)); |
245 | latch.countDown(); | 223 | latch.countDown(); |
246 | } | 224 | } |
247 | 225 |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.rpc; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Ignore; | ||
23 | +import org.junit.Test; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | + | ||
26 | +@Slf4j | ||
27 | +public abstract class AbstractMqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | ||
28 | + | ||
29 | + @Before | ||
30 | + public void beforeTest() throws Exception { | ||
31 | + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.JSON, null, null); | ||
32 | + } | ||
33 | + | ||
34 | + @After | ||
35 | + public void afterTest() throws Exception { | ||
36 | + super.processAfterTest(); | ||
37 | + } | ||
38 | + | ||
39 | + @Test | ||
40 | + public void testServerMqttOneWayRpc() throws Exception { | ||
41 | + processOneWayRpcTest(); | ||
42 | + } | ||
43 | + | ||
44 | + @Test | ||
45 | + public void testServerMqttTwoWayRpc() throws Exception { | ||
46 | + processTwoWayRpcTest(); | ||
47 | + } | ||
48 | + | ||
49 | + @Test | ||
50 | + public void testGatewayServerMqttOneWayRpc() throws Exception { | ||
51 | + processOneWayRpcTestGateway("Gateway Device OneWay RPC Json"); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testGatewayServerMqttTwoWayRpc() throws Exception { | ||
56 | + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Json"); | ||
57 | + } | ||
58 | + | ||
59 | + protected void processOneWayRpcTestGateway(String deviceName) throws Exception { | ||
60 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
61 | + String payload = "{\"device\": \"" + deviceName + "\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; | ||
62 | + byte[] payloadBytes = payload.getBytes(); | ||
63 | + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); | ||
64 | + } | ||
65 | + | ||
66 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.rpc; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttException; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
23 | +import org.junit.After; | ||
24 | +import org.junit.Before; | ||
25 | +import org.junit.Ignore; | ||
26 | +import org.junit.Test; | ||
27 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
28 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
29 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
30 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
31 | + | ||
32 | +import static org.junit.Assert.assertEquals; | ||
33 | +import static org.junit.Assert.assertNotNull; | ||
34 | + | ||
35 | +@Slf4j | ||
36 | +public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | ||
37 | + | ||
38 | + @Before | ||
39 | + public void beforeTest() throws Exception { | ||
40 | + processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null); | ||
41 | + } | ||
42 | + | ||
43 | + @After | ||
44 | + public void afterTest() throws Exception { | ||
45 | + super.processAfterTest(); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testServerMqttOneWayRpc() throws Exception { | ||
50 | + processOneWayRpcTest(); | ||
51 | + } | ||
52 | + | ||
53 | + @Test | ||
54 | + public void testServerMqttTwoWayRpc() throws Exception { | ||
55 | + processTwoWayRpcTest(); | ||
56 | + } | ||
57 | + | ||
58 | + @Test | ||
59 | + public void testGatewayServerMqttOneWayRpc() throws Exception { | ||
60 | + processOneWayRpcTestGateway("Gateway Device OneWay RPC Proto"); | ||
61 | + } | ||
62 | + | ||
63 | + @Test | ||
64 | + public void testGatewayServerMqttTwoWayRpc() throws Exception { | ||
65 | + processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Proto"); | ||
66 | + } | ||
67 | + | ||
68 | + protected void processTwoWayRpcTestGateway(String deviceName) throws Exception { | ||
69 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
70 | + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); | ||
71 | + byte[] payloadBytes = connectMsgProto.toByteArray(); | ||
72 | + validateTwoWayRpcGateway(deviceName, client, payloadBytes); | ||
73 | + } | ||
74 | + | ||
75 | + protected void processOneWayRpcTestGateway(String deviceName) throws Exception { | ||
76 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
77 | + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); | ||
78 | + byte[] payloadBytes = connectMsgProto.toByteArray(); | ||
79 | + validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); | ||
80 | + } | ||
81 | + | ||
82 | + | ||
83 | + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { | ||
84 | + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); | ||
85 | + builder.setDeviceName(deviceName); | ||
86 | + builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); | ||
87 | + return builder.build(); | ||
88 | + } | ||
89 | + | ||
90 | + protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { | ||
91 | + MqttMessage message = new MqttMessage(); | ||
92 | + if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { | ||
93 | + TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
94 | + .setPayload(DEVICE_RESPONSE) | ||
95 | + .setRequestId(0) | ||
96 | + .build(); | ||
97 | + message.setPayload(toDeviceRpcResponseMsg.toByteArray()); | ||
98 | + } else { | ||
99 | + TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload()); | ||
100 | + String deviceName = msg.getDeviceName(); | ||
101 | + int requestId = msg.getRpcRequestMsg().getRequestId(); | ||
102 | + TransportApiProtos.GatewayRpcResponseMsg gatewayRpcResponseMsg = TransportApiProtos.GatewayRpcResponseMsg.newBuilder() | ||
103 | + .setDeviceName(deviceName) | ||
104 | + .setId(requestId) | ||
105 | + .setData("{\"success\": true}") | ||
106 | + .build(); | ||
107 | + message.setPayload(gatewayRpcResponseMsg.toByteArray()); | ||
108 | + } | ||
109 | + return message; | ||
110 | + } | ||
111 | + | ||
112 | + | ||
113 | + | ||
114 | +} |
@@ -16,11 +16,11 @@ | @@ -16,11 +16,11 @@ | ||
16 | package org.thingsboard.server.mqtt.rpc.nosql; | 16 | package org.thingsboard.server.mqtt.rpc.nosql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
19 | -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
23 | */ | 23 | */ |
24 | @DaoNoSqlTest | 24 | @DaoNoSqlTest |
25 | -public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | 25 | +public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest { |
26 | } | 26 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.rpc.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttServerSideRpcJsonSqlIntegrationTest extends AbstractMqttServerSideRpcJsonIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.rpc.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest; | ||
20 | + | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttServerSideRpcProtoSqlIntegrationTest extends AbstractMqttServerSideRpcProtoIntegrationTest { | ||
24 | +} |
@@ -16,11 +16,11 @@ | @@ -16,11 +16,11 @@ | ||
16 | package org.thingsboard.server.mqtt.rpc.sql; | 16 | package org.thingsboard.server.mqtt.rpc.sql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcDefaultIntegrationTest; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
23 | */ | 23 | */ |
24 | @DaoSqlTest | 24 | @DaoSqlTest |
25 | -public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { | 25 | +public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcDefaultIntegrationTest { |
26 | } | 26 | } |
application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java
deleted
100644 → 0
1 | -/** | ||
2 | - * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.thingsboard.server.mqtt.telemetry; | ||
17 | - | ||
18 | -import io.netty.handler.codec.mqtt.MqttQoS; | ||
19 | -import lombok.extern.slf4j.Slf4j; | ||
20 | -import org.eclipse.paho.client.mqttv3.*; | ||
21 | -import org.junit.Before; | ||
22 | -import org.junit.Ignore; | ||
23 | -import org.junit.Test; | ||
24 | -import org.springframework.web.util.UriComponentsBuilder; | ||
25 | -import org.thingsboard.server.common.data.Device; | ||
26 | -import org.thingsboard.server.common.data.security.DeviceCredentials; | ||
27 | -import org.thingsboard.server.controller.AbstractControllerTest; | ||
28 | -import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
29 | - | ||
30 | -import java.net.URI; | ||
31 | -import java.util.*; | ||
32 | -import java.util.concurrent.CountDownLatch; | ||
33 | -import java.util.concurrent.TimeUnit; | ||
34 | - | ||
35 | -import static org.junit.Assert.assertEquals; | ||
36 | -import static org.junit.Assert.assertNotNull; | ||
37 | -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
38 | - | ||
39 | -/** | ||
40 | - * @author Valerii Sosliuk | ||
41 | - */ | ||
42 | -@Slf4j | ||
43 | -public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractControllerTest { | ||
44 | - | ||
45 | - private static final String MQTT_URL = "tcp://localhost:1883"; | ||
46 | - | ||
47 | - private Device savedDevice; | ||
48 | - private String accessToken; | ||
49 | - | ||
50 | - @Before | ||
51 | - public void beforeTest() throws Exception { | ||
52 | - loginTenantAdmin(); | ||
53 | - | ||
54 | - Device device = new Device(); | ||
55 | - device.setName("Test device"); | ||
56 | - device.setType("default"); | ||
57 | - savedDevice = doPost("/api/device", device, Device.class); | ||
58 | - | ||
59 | - DeviceCredentials deviceCredentials = | ||
60 | - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); | ||
61 | - | ||
62 | - assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); | ||
63 | - accessToken = deviceCredentials.getCredentialsId(); | ||
64 | - assertNotNull(accessToken); | ||
65 | - } | ||
66 | - | ||
67 | - @Test | ||
68 | - public void testPushMqttRpcData() throws Exception { | ||
69 | - String clientId = MqttAsyncClient.generateClientId(); | ||
70 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
71 | - | ||
72 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
73 | - options.setUserName(accessToken); | ||
74 | - client.connect(options); | ||
75 | - Thread.sleep(3000); | ||
76 | - MqttMessage message = new MqttMessage(); | ||
77 | - message.setPayload("{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4}".getBytes()); | ||
78 | - client.publish("v1/devices/me/telemetry", message); | ||
79 | - | ||
80 | - String deviceId = savedDevice.getId().getId().toString(); | ||
81 | - | ||
82 | - Thread.sleep(2000); | ||
83 | - List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); | ||
84 | - Set<String> actualKeySet = new HashSet<>(actualKeys); | ||
85 | - | ||
86 | - List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4"); | ||
87 | - Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
88 | - | ||
89 | - assertEquals(expectedKeySet, actualKeySet); | ||
90 | - | ||
91 | - String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); | ||
92 | - Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); | ||
93 | - | ||
94 | - assertEquals("value1", values.get("key1").get(0).get("value")); | ||
95 | - assertEquals("true", values.get("key2").get(0).get("value")); | ||
96 | - assertEquals("3.0", values.get("key3").get(0).get("value")); | ||
97 | - assertEquals("4", values.get("key4").get(0).get("value")); | ||
98 | - } | ||
99 | - | ||
100 | - | ||
101 | -// @Test - Unstable | ||
102 | - public void testMqttQoSLevel() throws Exception { | ||
103 | - String clientId = MqttAsyncClient.generateClientId(); | ||
104 | - MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
105 | - | ||
106 | - MqttConnectOptions options = new MqttConnectOptions(); | ||
107 | - options.setUserName(accessToken); | ||
108 | - CountDownLatch latch = new CountDownLatch(1); | ||
109 | - TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
110 | - client.setCallback(callback); | ||
111 | - client.connect(options).waitForCompletion(5000); | ||
112 | - client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); | ||
113 | - String payload = "{\"key\":\"uniqueValue\"}"; | ||
114 | -// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue. | ||
115 | -// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed) | ||
116 | -// MqttClient <- SUB_ACK <- Transport | ||
117 | - Thread.sleep(5000); | ||
118 | - doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); | ||
119 | - latch.await(10, TimeUnit.SECONDS); | ||
120 | - assertEquals(payload, callback.getPayload()); | ||
121 | - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
122 | - } | ||
123 | - | ||
124 | - private static class TestMqttCallback implements MqttCallback { | ||
125 | - | ||
126 | - private final MqttAsyncClient client; | ||
127 | - private final CountDownLatch latch; | ||
128 | - private volatile Integer qoS; | ||
129 | - private volatile String payload; | ||
130 | - | ||
131 | - String getPayload() { | ||
132 | - return payload; | ||
133 | - } | ||
134 | - | ||
135 | - TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) { | ||
136 | - this.client = client; | ||
137 | - this.latch = latch; | ||
138 | - } | ||
139 | - | ||
140 | - int getQoS() { | ||
141 | - return qoS; | ||
142 | - } | ||
143 | - | ||
144 | - @Override | ||
145 | - public void connectionLost(Throwable throwable) { | ||
146 | - log.error("Client connection lost", throwable); | ||
147 | - } | ||
148 | - | ||
149 | - @Override | ||
150 | - public void messageArrived(String requestTopic, MqttMessage mqttMessage) { | ||
151 | - payload = new String(mqttMessage.getPayload()); | ||
152 | - qoS = mqttMessage.getQos(); | ||
153 | - latch.countDown(); | ||
154 | - } | ||
155 | - | ||
156 | - @Override | ||
157 | - public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
158 | - | ||
159 | - } | ||
160 | - } | ||
161 | - | ||
162 | - | ||
163 | -} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.Device; | ||
24 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
25 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
26 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
27 | + | ||
28 | +import java.util.Arrays; | ||
29 | +import java.util.HashSet; | ||
30 | +import java.util.LinkedHashMap; | ||
31 | +import java.util.List; | ||
32 | +import java.util.Map; | ||
33 | +import java.util.Set; | ||
34 | + | ||
35 | +import static org.junit.Assert.assertEquals; | ||
36 | +import static org.junit.Assert.assertNotNull; | ||
37 | +import static org.junit.Assert.assertTrue; | ||
38 | + | ||
39 | +@Slf4j | ||
40 | +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { | ||
41 | + | ||
42 | + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + | ||
43 | + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; | ||
44 | + | ||
45 | + @Before | ||
46 | + public void beforeTest() throws Exception { | ||
47 | + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", null, null, null); | ||
48 | + } | ||
49 | + | ||
50 | + @After | ||
51 | + public void afterTest() throws Exception { | ||
52 | + processAfterTest(); | ||
53 | + } | ||
54 | + | ||
55 | + @Test | ||
56 | + public void testPushMqttAttributes() throws Exception { | ||
57 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
58 | + processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | ||
59 | + } | ||
60 | + | ||
61 | + @Test | ||
62 | + public void testPushMqttAttributesGateway() throws Exception { | ||
63 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
64 | + String deviceName1 = "Device A"; | ||
65 | + String deviceName2 = "Device B"; | ||
66 | + String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2); | ||
67 | + processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
68 | + } | ||
69 | + | ||
70 | + protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload) throws Exception { | ||
71 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
72 | + | ||
73 | + publishMqttMsg(client, payload, topic); | ||
74 | + | ||
75 | + DeviceId deviceId = savedDevice.getId(); | ||
76 | + | ||
77 | + long start = System.currentTimeMillis(); | ||
78 | + long end = System.currentTimeMillis() + 2000; | ||
79 | + | ||
80 | + List<String> actualKeys = null; | ||
81 | + while (start <= end) { | ||
82 | + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", List.class); | ||
83 | + if (actualKeys.size() == expectedKeys.size()) { | ||
84 | + break; | ||
85 | + } | ||
86 | + Thread.sleep(100); | ||
87 | + start += 100; | ||
88 | + } | ||
89 | + assertNotNull(actualKeys); | ||
90 | + | ||
91 | + Set<String> actualKeySet = new HashSet<>(actualKeys); | ||
92 | + | ||
93 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
94 | + | ||
95 | + assertEquals(expectedKeySet, actualKeySet); | ||
96 | + | ||
97 | + String getAttributesValuesUrl = getAttributesValuesUrl(deviceId, actualKeySet); | ||
98 | + List<Map<String, Object>> values = doGetAsync(getAttributesValuesUrl, List.class); | ||
99 | + assertAttributesValues(values, expectedKeySet); | ||
100 | + String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); | ||
101 | + doDelete(deleteAttributesUrl); | ||
102 | + } | ||
103 | + | ||
104 | + protected void processGatewayAttributesTest(List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { | ||
105 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
106 | + | ||
107 | + publishMqttMsg(client, payload, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC); | ||
108 | + | ||
109 | + Thread.sleep(2000); | ||
110 | + | ||
111 | + Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class); | ||
112 | + assertNotNull(firstDevice); | ||
113 | + Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class); | ||
114 | + assertNotNull(secondDevice); | ||
115 | + | ||
116 | + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); | ||
117 | + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); | ||
118 | + | ||
119 | + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class); | ||
120 | + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); | ||
121 | + | ||
122 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
123 | + | ||
124 | + assertEquals(expectedKeySet, firstDeviceActualKeySet); | ||
125 | + assertEquals(expectedKeySet, secondDeviceActualKeySet); | ||
126 | + | ||
127 | + String getAttributesValuesUrlFirstDevice = getAttributesValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); | ||
128 | + String getAttributesValuesUrlSecondDevice = getAttributesValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); | ||
129 | + | ||
130 | + List<Map<String, Object>> firstDeviceValues = doGetAsync(getAttributesValuesUrlFirstDevice, List.class); | ||
131 | + List<Map<String, Object>> secondDeviceValues = doGetAsync(getAttributesValuesUrlSecondDevice, List.class); | ||
132 | + | ||
133 | + assertAttributesValues(firstDeviceValues, expectedKeySet); | ||
134 | + assertAttributesValues(secondDeviceValues, expectedKeySet); | ||
135 | + | ||
136 | + } | ||
137 | + | ||
138 | + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) { | ||
139 | + for (Map<String, Object> map : deviceValues) { | ||
140 | + String key = (String) map.get("key"); | ||
141 | + Object value = map.get("value"); | ||
142 | + assertTrue(expectedKeySet.contains(key)); | ||
143 | + switch (key) { | ||
144 | + case "key1": | ||
145 | + assertEquals("value1", value); | ||
146 | + break; | ||
147 | + case "key2": | ||
148 | + assertEquals(true, value); | ||
149 | + break; | ||
150 | + case "key3": | ||
151 | + assertEquals(3.0, value); | ||
152 | + break; | ||
153 | + case "key4": | ||
154 | + assertEquals(4, value); | ||
155 | + break; | ||
156 | + case "key5": | ||
157 | + assertNotNull(value); | ||
158 | + assertEquals(3, ((LinkedHashMap) value).size()); | ||
159 | + assertEquals(42, ((LinkedHashMap) value).get("someNumber")); | ||
160 | + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); | ||
161 | + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); | ||
162 | + assertEquals("value", someNestedObject.get("key")); | ||
163 | + break; | ||
164 | + } | ||
165 | + } | ||
166 | + } | ||
167 | + | ||
168 | + protected String getGatewayAttributesJsonPayload(String deviceA, String deviceB) { | ||
169 | + return "{\"" + deviceA + "\": " + PAYLOAD_VALUES_STR + ", \"" + deviceB + "\": " + PAYLOAD_VALUES_STR + "}"; | ||
170 | + } | ||
171 | + | ||
172 | + private String getAttributesValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { | ||
173 | + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); | ||
174 | + } | ||
175 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | + | ||
24 | +import java.util.Arrays; | ||
25 | +import java.util.List; | ||
26 | + | ||
27 | +@Slf4j | ||
28 | +public abstract class AbstractMqttAttributesJsonIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
29 | + | ||
30 | + private static final String POST_DATA_ATTRIBUTES_TOPIC = "data/attributes"; | ||
31 | + | ||
32 | + @Before | ||
33 | + public void beforeTest() throws Exception { | ||
34 | + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.JSON, null, POST_DATA_ATTRIBUTES_TOPIC); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void afterTest() throws Exception { | ||
39 | + processAfterTest(); | ||
40 | + } | ||
41 | + | ||
42 | + @Test | ||
43 | + public void testPushMqttAttributes() throws Exception { | ||
44 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
45 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes()); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testPushMqttAttributesGateway() throws Exception { | ||
50 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
51 | + String deviceName1 = "Device A"; | ||
52 | + String deviceName2 = "Device B"; | ||
53 | + String payload = getGatewayAttributesJsonPayload(deviceName1, deviceName2); | ||
54 | + processGatewayAttributesTest(expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
55 | + } | ||
56 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.junit.After; | ||
20 | +import org.junit.Before; | ||
21 | +import org.junit.Test; | ||
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
24 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
25 | + | ||
26 | +import java.util.Arrays; | ||
27 | +import java.util.List; | ||
28 | + | ||
29 | +import static org.junit.Assert.assertEquals; | ||
30 | +import static org.junit.Assert.assertNotNull; | ||
31 | +import static org.junit.Assert.assertTrue; | ||
32 | + | ||
33 | +@Slf4j | ||
34 | +public abstract class AbstractMqttAttributesProtoIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
35 | + | ||
36 | + private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes"; | ||
37 | + | ||
38 | + @Before | ||
39 | + public void beforeTest() throws Exception { | ||
40 | + processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); | ||
41 | + } | ||
42 | + | ||
43 | + @After | ||
44 | + public void afterTest() throws Exception { | ||
45 | + processAfterTest(); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testPushMqttAttributes() throws Exception { | ||
50 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
51 | + TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); | ||
52 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, msg.toByteArray()); | ||
53 | + } | ||
54 | + | ||
55 | + @Test | ||
56 | + public void testPushMqttAttributesGateway() throws Exception { | ||
57 | + TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); | ||
58 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
59 | + String deviceName1 = "Device A"; | ||
60 | + String deviceName2 = "Device B"; | ||
61 | + TransportApiProtos.AttributesMsg firstDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName1, expectedKeys); | ||
62 | + TransportApiProtos.AttributesMsg secondDeviceAttributesMsgProto = getDeviceAttributesMsgProto(deviceName2, expectedKeys); | ||
63 | + gatewayAttributesMsgProtoBuilder.addAllMsg(Arrays.asList(firstDeviceAttributesMsgProto, secondDeviceAttributesMsgProto)); | ||
64 | + TransportApiProtos.GatewayAttributesMsg gatewayAttributesMsg = gatewayAttributesMsgProtoBuilder.build(); | ||
65 | + processGatewayAttributesTest(expectedKeys, gatewayAttributesMsg.toByteArray(), deviceName1, deviceName2); | ||
66 | + } | ||
67 | + | ||
68 | + private TransportApiProtos.AttributesMsg getDeviceAttributesMsgProto(String deviceName, List<String> expectedKeys) { | ||
69 | + TransportApiProtos.AttributesMsg.Builder deviceAttributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); | ||
70 | + TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); | ||
71 | + deviceAttributesMsgBuilder.setDeviceName(deviceName); | ||
72 | + deviceAttributesMsgBuilder.setMsg(msg); | ||
73 | + return deviceAttributesMsgBuilder.build(); | ||
74 | + } | ||
75 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class MqttAttributesNoSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesNoSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoNoSqlTest | ||
23 | +public class MqttAttributesNoSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesSqlIntegrationTest extends AbstractMqttAttributesIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class MqttAttributesSqlJsonIntegrationTest extends AbstractMqttAttributesJsonIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.attributes.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; | ||
21 | + | ||
22 | +@DaoSqlTest | ||
23 | +public class MqttAttributesSqlProtoIntegrationTest extends AbstractMqttAttributesProtoIntegrationTest { | ||
24 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries; | ||
17 | + | ||
18 | +import io.netty.handler.codec.mqtt.MqttQoS; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | ||
21 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
22 | +import org.eclipse.paho.client.mqttv3.MqttCallback; | ||
23 | +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | ||
24 | +import org.eclipse.paho.client.mqttv3.MqttMessage; | ||
25 | +import org.junit.After; | ||
26 | +import org.junit.Before; | ||
27 | +import org.junit.Test; | ||
28 | +import org.thingsboard.server.common.data.Device; | ||
29 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
30 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
31 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
32 | +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; | ||
33 | + | ||
34 | +import java.util.Arrays; | ||
35 | +import java.util.HashSet; | ||
36 | +import java.util.List; | ||
37 | +import java.util.Map; | ||
38 | +import java.util.Set; | ||
39 | +import java.util.concurrent.CountDownLatch; | ||
40 | +import java.util.concurrent.TimeUnit; | ||
41 | + | ||
42 | +import static org.junit.Assert.assertEquals; | ||
43 | +import static org.junit.Assert.assertNotNull; | ||
44 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
45 | + | ||
46 | +@Slf4j | ||
47 | +public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest { | ||
48 | + | ||
49 | + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + | ||
50 | + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; | ||
51 | + | ||
52 | + @Before | ||
53 | + public void beforeTest() throws Exception { | ||
54 | + processBeforeTest("Test Post Telemetry device", "Test Post Telemetry gateway", null, null, null); | ||
55 | + } | ||
56 | + | ||
57 | + @After | ||
58 | + public void afterTest() throws Exception { | ||
59 | + processAfterTest(); | ||
60 | + } | ||
61 | + | ||
62 | + @Test | ||
63 | + public void testPushMqttTelemetry() throws Exception { | ||
64 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
65 | + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | ||
66 | + } | ||
67 | + | ||
68 | + @Test | ||
69 | + public void testPushMqttTelemetryWithTs() throws Exception { | ||
70 | + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; | ||
71 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
72 | + processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | ||
73 | + } | ||
74 | + | ||
75 | + @Test | ||
76 | + public void testPushMqttTelemetryGateway() throws Exception { | ||
77 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
78 | + String deviceName1 = "Device A"; | ||
79 | + String deviceName2 = "Device B"; | ||
80 | + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000"); | ||
81 | + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
82 | + } | ||
83 | + | ||
84 | + @Test | ||
85 | + public void testGatewayConnect() throws Exception { | ||
86 | + String payload = "{\"device\":\"Device A\"}"; | ||
87 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
88 | + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
89 | + | ||
90 | + Thread.sleep(2000); | ||
91 | + | ||
92 | + String deviceName = "Device A"; | ||
93 | + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); | ||
94 | + assertNotNull(device); | ||
95 | + } | ||
96 | + | ||
97 | + protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception { | ||
98 | + MqttAsyncClient client = getMqttAsyncClient(accessToken); | ||
99 | + publishMqttMsg(client, payload, topic); | ||
100 | + | ||
101 | + String deviceId = savedDevice.getId().getId().toString(); | ||
102 | + | ||
103 | + long start = System.currentTimeMillis(); | ||
104 | + long end = System.currentTimeMillis() + 2000; | ||
105 | + | ||
106 | + List<String> actualKeys = null; | ||
107 | + while (start <= end) { | ||
108 | + actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class); | ||
109 | + if (actualKeys.size() == expectedKeys.size()) { | ||
110 | + break; | ||
111 | + } | ||
112 | + Thread.sleep(100); | ||
113 | + start += 100; | ||
114 | + } | ||
115 | + assertNotNull(actualKeys); | ||
116 | + | ||
117 | + Set<String> actualKeySet = new HashSet<>(actualKeys); | ||
118 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
119 | + | ||
120 | + assertEquals(expectedKeySet, actualKeySet); | ||
121 | + | ||
122 | + String getTelemetryValuesUrl; | ||
123 | + if (withTs) { | ||
124 | + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=15000&keys=" + String.join(",", actualKeySet); | ||
125 | + } else { | ||
126 | + getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); | ||
127 | + } | ||
128 | + Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); | ||
129 | + | ||
130 | + if (withTs) { | ||
131 | + assertTs(values, expectedKeys, 10000, 0); | ||
132 | + } | ||
133 | + assertValues(values, 0); | ||
134 | + } | ||
135 | + | ||
136 | + protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception { | ||
137 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
138 | + | ||
139 | + publishMqttMsg(client, payload, topic); | ||
140 | + | ||
141 | + Thread.sleep(2000); | ||
142 | + | ||
143 | + Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class); | ||
144 | + assertNotNull(firstDevice); | ||
145 | + Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class); | ||
146 | + assertNotNull(secondDevice); | ||
147 | + | ||
148 | + List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class); | ||
149 | + Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys); | ||
150 | + | ||
151 | + List<String> secondDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", List.class); | ||
152 | + Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys); | ||
153 | + | ||
154 | + Set<String> expectedKeySet = new HashSet<>(expectedKeys); | ||
155 | + | ||
156 | + assertEquals(expectedKeySet, firstDeviceActualKeySet); | ||
157 | + assertEquals(expectedKeySet, secondDeviceActualKeySet); | ||
158 | + | ||
159 | + String getTelemetryValuesUrlFirstDevice = getTelemetryValuesUrl(firstDevice.getId(), firstDeviceActualKeySet); | ||
160 | + String getTelemetryValuesUrlSecondDevice = getTelemetryValuesUrl(firstDevice.getId(), secondDeviceActualKeySet); | ||
161 | + | ||
162 | + Map<String, List<Map<String, String>>> firstDeviceValues = doGetAsync(getTelemetryValuesUrlFirstDevice, Map.class); | ||
163 | + Map<String, List<Map<String, String>>> secondDeviceValues = doGetAsync(getTelemetryValuesUrlSecondDevice, Map.class); | ||
164 | + | ||
165 | + assertGatewayDeviceData(firstDeviceValues, expectedKeys); | ||
166 | + assertGatewayDeviceData(secondDeviceValues, expectedKeys); | ||
167 | + } | ||
168 | + | ||
169 | + protected String getGatewayTelemetryJsonPayload(String deviceA, String deviceB, String firstTsValue, String secondTsValue) { | ||
170 | + String payload = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}, " + | ||
171 | + "{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]"; | ||
172 | + return "{\"" + deviceA + "\": " + payload + ", \"" + deviceB + "\": " + payload + "}"; | ||
173 | + } | ||
174 | + | ||
175 | + private String getTelemetryValuesUrl(DeviceId deviceId, Set<String> actualKeySet) { | ||
176 | + return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet); | ||
177 | + } | ||
178 | + | ||
179 | + private void assertGatewayDeviceData(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys) { | ||
180 | + | ||
181 | + assertEquals(2, deviceValues.get(expectedKeys.get(0)).size()); | ||
182 | + assertEquals(2, deviceValues.get(expectedKeys.get(1)).size()); | ||
183 | + assertEquals(2, deviceValues.get(expectedKeys.get(2)).size()); | ||
184 | + assertEquals(2, deviceValues.get(expectedKeys.get(3)).size()); | ||
185 | + assertEquals(2, deviceValues.get(expectedKeys.get(4)).size()); | ||
186 | + | ||
187 | + assertTs(deviceValues, expectedKeys, 20000, 0); | ||
188 | + assertTs(deviceValues, expectedKeys, 10000, 1); | ||
189 | + | ||
190 | + assertValues(deviceValues, 0); | ||
191 | + assertValues(deviceValues, 1); | ||
192 | + | ||
193 | + } | ||
194 | + | ||
195 | + private void assertValues(Map<String, List<Map<String, String>>> deviceValues, int arrayIndex) { | ||
196 | + for (Map.Entry<String, List<Map<String, String>>> entry : deviceValues.entrySet()) { | ||
197 | + String key = entry.getKey(); | ||
198 | + List<Map<String, String>> tsKv = entry.getValue(); | ||
199 | + String value = tsKv.get(arrayIndex).get("value"); | ||
200 | + switch (key) { | ||
201 | + case "key1": | ||
202 | + assertEquals("value1", value); | ||
203 | + break; | ||
204 | + case "key2": | ||
205 | + assertEquals("true", value); | ||
206 | + break; | ||
207 | + case "key3": | ||
208 | + assertEquals("3.0", value); | ||
209 | + break; | ||
210 | + case "key4": | ||
211 | + assertEquals("4", value); | ||
212 | + break; | ||
213 | + case "key5": | ||
214 | + assertEquals("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", value); | ||
215 | + break; | ||
216 | + } | ||
217 | + } | ||
218 | + } | ||
219 | + | ||
220 | + private void assertTs(Map<String, List<Map<String, String>>> deviceValues, List<String> expectedKeys, int ts, int arrayIndex) { | ||
221 | + assertEquals(ts, deviceValues.get(expectedKeys.get(0)).get(arrayIndex).get("ts")); | ||
222 | + assertEquals(ts, deviceValues.get(expectedKeys.get(1)).get(arrayIndex).get("ts")); | ||
223 | + assertEquals(ts, deviceValues.get(expectedKeys.get(2)).get(arrayIndex).get("ts")); | ||
224 | + assertEquals(ts, deviceValues.get(expectedKeys.get(3)).get(arrayIndex).get("ts")); | ||
225 | + assertEquals(ts, deviceValues.get(expectedKeys.get(4)).get(arrayIndex).get("ts")); | ||
226 | + } | ||
227 | + | ||
228 | + // @Test - Unstable | ||
229 | + public void testMqttQoSLevel() throws Exception { | ||
230 | + String clientId = MqttAsyncClient.generateClientId(); | ||
231 | + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); | ||
232 | + | ||
233 | + MqttConnectOptions options = new MqttConnectOptions(); | ||
234 | + options.setUserName(accessToken); | ||
235 | + CountDownLatch latch = new CountDownLatch(1); | ||
236 | + TestMqttCallback callback = new TestMqttCallback(client, latch); | ||
237 | + client.setCallback(callback); | ||
238 | + client.connect(options).waitForCompletion(5000); | ||
239 | + client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value()); | ||
240 | + String payload = "{\"key\":\"uniqueValue\"}"; | ||
241 | +// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue. | ||
242 | +// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed) | ||
243 | +// MqttClient <- SUB_ACK <- Transport | ||
244 | + Thread.sleep(5000); | ||
245 | + doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); | ||
246 | + latch.await(10, TimeUnit.SECONDS); | ||
247 | + assertEquals(payload, callback.getPayload()); | ||
248 | + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | ||
249 | + } | ||
250 | + | ||
251 | + private static class TestMqttCallback implements MqttCallback { | ||
252 | + | ||
253 | + private final MqttAsyncClient client; | ||
254 | + private final CountDownLatch latch; | ||
255 | + private volatile Integer qoS; | ||
256 | + private volatile String payload; | ||
257 | + | ||
258 | + String getPayload() { | ||
259 | + return payload; | ||
260 | + } | ||
261 | + | ||
262 | + TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) { | ||
263 | + this.client = client; | ||
264 | + this.latch = latch; | ||
265 | + } | ||
266 | + | ||
267 | + int getQoS() { | ||
268 | + return qoS; | ||
269 | + } | ||
270 | + | ||
271 | + @Override | ||
272 | + public void connectionLost(Throwable throwable) { | ||
273 | + log.error("Client connection lost", throwable); | ||
274 | + } | ||
275 | + | ||
276 | + @Override | ||
277 | + public void messageArrived(String requestTopic, MqttMessage mqttMessage) { | ||
278 | + payload = new String(mqttMessage.getPayload()); | ||
279 | + qoS = mqttMessage.getQos(); | ||
280 | + latch.countDown(); | ||
281 | + } | ||
282 | + | ||
283 | + @Override | ||
284 | + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { | ||
285 | + | ||
286 | + } | ||
287 | + } | ||
288 | + | ||
289 | + | ||
290 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.Device; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
26 | + | ||
27 | +import java.util.Arrays; | ||
28 | +import java.util.List; | ||
29 | + | ||
30 | +import static org.junit.Assert.assertEquals; | ||
31 | +import static org.junit.Assert.assertNotNull; | ||
32 | + | ||
33 | +@Slf4j | ||
34 | +public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { | ||
35 | + | ||
36 | + private static final String POST_DATA_TELEMETRY_TOPIC = "data/telemetry"; | ||
37 | + | ||
38 | + @Before | ||
39 | + public void beforeTest() throws Exception { | ||
40 | + processBeforeTest("Test Post Telemetry device json payload", "Test Post Telemetry gateway json payload", TransportPayloadType.JSON, POST_DATA_TELEMETRY_TOPIC, null); | ||
41 | + } | ||
42 | + | ||
43 | + @After | ||
44 | + public void afterTest() throws Exception { | ||
45 | + processAfterTest(); | ||
46 | + } | ||
47 | + | ||
48 | + @Test | ||
49 | + public void testPushMqttTelemetry() throws Exception { | ||
50 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
51 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false); | ||
52 | + } | ||
53 | + | ||
54 | + @Test | ||
55 | + public void testPushMqttTelemetryWithTs() throws Exception { | ||
56 | + String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}"; | ||
57 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
58 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true); | ||
59 | + } | ||
60 | + | ||
61 | + @Test | ||
62 | + public void testPushMqttTelemetryGateway() throws Exception { | ||
63 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
64 | + String deviceName1 = "Device A"; | ||
65 | + String deviceName2 = "Device B"; | ||
66 | + String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000"); | ||
67 | + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + public void testGatewayConnect() throws Exception { | ||
72 | + String payload = "{\"device\":\"Device A\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; | ||
73 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
74 | + publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
75 | + | ||
76 | + Thread.sleep(2000); | ||
77 | + | ||
78 | + String deviceName = "Device A"; | ||
79 | + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); | ||
80 | + assertNotNull(device); | ||
81 | + } | ||
82 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Before; | ||
22 | +import org.junit.Test; | ||
23 | +import org.thingsboard.server.common.data.Device; | ||
24 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
25 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
26 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
27 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
28 | + | ||
29 | +import java.util.Arrays; | ||
30 | +import java.util.List; | ||
31 | + | ||
32 | +import static org.junit.Assert.assertEquals; | ||
33 | +import static org.junit.Assert.assertNotNull; | ||
34 | + | ||
35 | +@Slf4j | ||
36 | +public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { | ||
37 | + | ||
38 | + private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry"; | ||
39 | + | ||
40 | + @Before | ||
41 | + public void beforeTest() throws Exception { | ||
42 | + processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); | ||
43 | + } | ||
44 | + | ||
45 | + @After | ||
46 | + public void afterTest() throws Exception { | ||
47 | + processAfterTest(); | ||
48 | + } | ||
49 | + | ||
50 | + @Test | ||
51 | + public void testPushMqttTelemetry() throws Exception { | ||
52 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
53 | + TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 0); | ||
54 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), false); | ||
55 | + } | ||
56 | + | ||
57 | + @Test | ||
58 | + public void testPushMqttTelemetryWithTs() throws Exception { | ||
59 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
60 | + TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 10000); | ||
61 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), true); | ||
62 | + } | ||
63 | + | ||
64 | + @Test | ||
65 | + public void testPushMqttTelemetryGateway() throws Exception { | ||
66 | + TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); | ||
67 | + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | ||
68 | + String deviceName1 = "Device A"; | ||
69 | + String deviceName2 = "Device B"; | ||
70 | + TransportApiProtos.TelemetryMsg deviceATelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName1, expectedKeys, 10000, 20000); | ||
71 | + TransportApiProtos.TelemetryMsg deviceBTelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName2, expectedKeys, 10000, 20000); | ||
72 | + gatewayTelemetryMsgProtoBuilder.addAllMsg(Arrays.asList(deviceATelemetryMsgProto, deviceBTelemetryMsgProto)); | ||
73 | + TransportApiProtos.GatewayTelemetryMsg gatewayTelemetryMsg = gatewayTelemetryMsgProtoBuilder.build(); | ||
74 | + processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, gatewayTelemetryMsg.toByteArray(), deviceName1, deviceName2); | ||
75 | + } | ||
76 | + | ||
77 | + @Test | ||
78 | + public void testGatewayConnect() throws Exception { | ||
79 | + String deviceName = "Device A"; | ||
80 | + TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); | ||
81 | + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ||
82 | + publishMqttMsg(client, connectMsgProto.toByteArray(), MqttTopics.GATEWAY_CONNECT_TOPIC); | ||
83 | + | ||
84 | + Thread.sleep(2000); | ||
85 | + | ||
86 | + Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class); | ||
87 | + assertNotNull(device); | ||
88 | + } | ||
89 | + | ||
90 | + private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { | ||
91 | + TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); | ||
92 | + builder.setDeviceName(deviceName); | ||
93 | + builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); | ||
94 | + return builder.build(); | ||
95 | + } | ||
96 | + | ||
97 | + private TransportApiProtos.TelemetryMsg getDeviceTelemetryMsgProto(String deviceName, List<String> expectedKeys, long firstTs, long secondTs) { | ||
98 | + TransportApiProtos.TelemetryMsg.Builder deviceTelemetryMsgBuilder = TransportApiProtos.TelemetryMsg.newBuilder(); | ||
99 | + TransportProtos.TsKvListProto tsKvListProto1 = getTsKvListProto(expectedKeys, firstTs); | ||
100 | + TransportProtos.TsKvListProto tsKvListProto2 = getTsKvListProto(expectedKeys, secondTs); | ||
101 | + TransportProtos.PostTelemetryMsg.Builder msg = TransportProtos.PostTelemetryMsg.newBuilder(); | ||
102 | + msg.addAllTsKvList(Arrays.asList(tsKvListProto1, tsKvListProto2)); | ||
103 | + deviceTelemetryMsgBuilder.setDeviceName(deviceName); | ||
104 | + deviceTelemetryMsgBuilder.setMsg(msg); | ||
105 | + return deviceTelemetryMsgBuilder.build(); | ||
106 | + } | ||
107 | + | ||
108 | + private TransportProtos.TsKvListProto getTsKvListProto(List<String> expectedKeys, long ts) { | ||
109 | + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); | ||
110 | + TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder(); | ||
111 | + builder.addAllKv(kvProtos); | ||
112 | + builder.setTs(ts); | ||
113 | + return builder.build(); | ||
114 | + } | ||
115 | +} |
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/nosql/MqttTimeseriesNoSqlIntegrationTest.java
renamed from
application/src/test/java/org/thingsboard/server/mqtt/telemetry/nosql/MqttTelemetryNoSqlIntegrationTest.java
@@ -13,14 +13,14 @@ | @@ -13,14 +13,14 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.server.mqtt.telemetry.nosql; | 16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; |
20 | 20 | ||
21 | /** | 21 | /** |
22 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
23 | */ | 23 | */ |
24 | @DaoNoSqlTest | 24 | @DaoNoSqlTest |
25 | -public class MqttTelemetryNoSqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest { | 25 | +public class MqttTimeseriesNoSqlIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
26 | } | 26 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class MqttTimeseriesNoSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class MqttTimeseriesNoSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { | ||
23 | +} |
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/sql/MqttTimeseriesSqlIntegrationTest.java
renamed from
application/src/test/java/org/thingsboard/server/mqtt/telemetry/sql/MqttTelemetrySqlIntegrationTest.java
@@ -13,15 +13,14 @@ | @@ -13,15 +13,14 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -package org.thingsboard.server.mqtt.telemetry.sql; | 16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.sql; |
17 | 17 | ||
18 | -import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
19 | import org.thingsboard.server.dao.service.DaoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
20 | -import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest; | 19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; |
21 | 20 | ||
22 | /** | 21 | /** |
23 | * Created by Valerii Sosliuk on 8/22/2017. | 22 | * Created by Valerii Sosliuk on 8/22/2017. |
24 | */ | 23 | */ |
25 | @DaoSqlTest | 24 | @DaoSqlTest |
26 | -public class MqttTelemetrySqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest { | 25 | +public class MqttTimeseriesSqlIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
27 | } | 26 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | ||
21 | + | ||
22 | +/** | ||
23 | + * Created by Valerii Sosliuk on 8/22/2017. | ||
24 | + */ | ||
25 | +@DaoSqlTest | ||
26 | +public class MqttTimeseriesSqlJsonIntegrationTest extends AbstractMqttTimeseriesJsonIntegrationTest { | ||
27 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.mqtt.telemetry.timeseries.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
19 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | ||
20 | +import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; | ||
21 | + | ||
22 | +/** | ||
23 | + * Created by Valerii Sosliuk on 8/22/2017. | ||
24 | + */ | ||
25 | +@DaoSqlTest | ||
26 | +public class MqttTimeseriesSqlProtoIntegrationTest extends AbstractMqttTimeseriesProtoIntegrationTest { | ||
27 | +} |
@@ -16,7 +16,6 @@ | @@ -16,7 +16,6 @@ | ||
16 | package org.thingsboard.server.rules.flow.sql; | 16 | package org.thingsboard.server.rules.flow.sql; |
17 | 17 | ||
18 | import org.thingsboard.server.dao.service.DaoSqlTest; | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest; | ||
20 | import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; | 19 | import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest; |
21 | 20 | ||
22 | /** | 21 | /** |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data; | ||
17 | + | ||
18 | +public enum TransportPayloadType { | ||
19 | + JSON, | ||
20 | + PROTOBUF | ||
21 | +} |
@@ -16,11 +16,14 @@ | @@ -16,11 +16,14 @@ | ||
16 | package org.thingsboard.server.common.data.device.profile; | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
19 | import org.thingsboard.server.common.data.DeviceTransportType; | 20 | import org.thingsboard.server.common.data.DeviceTransportType; |
20 | 21 | ||
21 | @Data | 22 | @Data |
22 | public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { | 23 | public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
23 | 24 | ||
25 | + private TransportPayloadType transportPayloadType = TransportPayloadType.JSON; | ||
26 | + | ||
24 | private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; | 27 | private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; |
25 | private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; | 28 | private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; |
26 | 29 |
@@ -20,28 +20,48 @@ package org.thingsboard.server.common.data.device.profile; | @@ -20,28 +20,48 @@ package org.thingsboard.server.common.data.device.profile; | ||
20 | */ | 20 | */ |
21 | public class MqttTopics { | 21 | public class MqttTopics { |
22 | 22 | ||
23 | + private static final String RPC = "/rpc"; | ||
24 | + private static final String CONNECT = "/connect"; | ||
25 | + private static final String DISCONNECT = "/disconnect"; | ||
26 | + private static final String TELEMETRY = "/telemetry"; | ||
27 | + private static final String ATTRIBUTES = "/attributes"; | ||
28 | + private static final String CLAIM = "/claim"; | ||
29 | + private static final String SUB_TOPIC = "+"; | ||
30 | + private static final String ATTRIBUTES_RESPONSE = "/attributes/response"; | ||
31 | + private static final String ATTRIBUTES_REQUEST = "/attributes/request"; | ||
32 | + | ||
33 | + private static final String DEVICE_RPC_RESPONSE = "/rpc/response/"; | ||
34 | + private static final String DEVICE_RPC_REQUEST = "/rpc/request/"; | ||
35 | + | ||
36 | + private static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/"; | ||
37 | + private static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; | ||
38 | + | ||
39 | + // V1_JSON topics | ||
40 | + | ||
23 | public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; | 41 | public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; |
24 | - public static final String DEVICE_RPC_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + "/rpc/response/"; | ||
25 | - public static final String DEVICE_RPC_RESPONSE_SUB_TOPIC = DEVICE_RPC_RESPONSE_TOPIC + "+"; | ||
26 | - public static final String DEVICE_RPC_REQUESTS_TOPIC = BASE_DEVICE_API_TOPIC + "/rpc/request/"; | ||
27 | - public static final String DEVICE_RPC_REQUESTS_SUB_TOPIC = DEVICE_RPC_REQUESTS_TOPIC + "+"; | ||
28 | - public static final String DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + "/attributes/response/"; | ||
29 | - public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + "+"; | ||
30 | - public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + "/attributes/request/"; | ||
31 | - public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + "/telemetry"; | ||
32 | - public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + "/claim"; | ||
33 | - public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + "/attributes"; | ||
34 | 42 | ||
35 | - public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway"; | ||
36 | - public static final String GATEWAY_CONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/connect"; | ||
37 | - public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/disconnect"; | ||
38 | - public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes"; | ||
39 | - public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + "/telemetry"; | ||
40 | - public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + "/claim"; | ||
41 | - public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + "/rpc"; | ||
42 | - public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/request"; | ||
43 | - public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/response"; | 43 | + public static final String DEVICE_RPC_RESPONSE_TOPIC = BASE_DEVICE_API_TOPIC + DEVICE_RPC_RESPONSE; |
44 | + public static final String DEVICE_RPC_RESPONSE_SUB_TOPIC = DEVICE_RPC_RESPONSE_TOPIC + SUB_TOPIC; | ||
45 | + public static final String DEVICE_RPC_REQUESTS_TOPIC = BASE_DEVICE_API_TOPIC + DEVICE_RPC_REQUEST; | ||
46 | + public static final String DEVICE_RPC_REQUESTS_SUB_TOPIC = DEVICE_RPC_REQUESTS_TOPIC + SUB_TOPIC; | ||
47 | + public static final String DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + DEVICE_ATTRIBUTES_RESPONSE; | ||
48 | + public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + SUB_TOPIC; | ||
49 | + public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + DEVICE_ATTRIBUTES_REQUEST; | ||
50 | + public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRY; | ||
51 | + public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + CLAIM; | ||
52 | + public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES; | ||
53 | + | ||
54 | + // V1_JSON gateway topics | ||
44 | 55 | ||
56 | + public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway"; | ||
57 | + public static final String GATEWAY_CONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + CONNECT; | ||
58 | + public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + DISCONNECT; | ||
59 | + public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES; | ||
60 | + public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + TELEMETRY; | ||
61 | + public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + CLAIM; | ||
62 | + public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + RPC; | ||
63 | + public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_REQUEST; | ||
64 | + public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + ATTRIBUTES_RESPONSE; | ||
45 | 65 | ||
46 | private MqttTopics() { | 66 | private MqttTopics() { |
47 | } | 67 | } |
@@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value; | @@ -24,7 +24,8 @@ import org.springframework.beans.factory.annotation.Value; | ||
24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
25 | import org.springframework.stereotype.Component; | 25 | import org.springframework.stereotype.Component; |
26 | import org.thingsboard.server.common.transport.TransportContext; | 26 | import org.thingsboard.server.common.transport.TransportContext; |
27 | -import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; | 27 | +import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; |
28 | +import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor; | ||
28 | 29 | ||
29 | /** | 30 | /** |
30 | * Created by ashvayka on 04.10.18. | 31 | * Created by ashvayka on 04.10.18. |
@@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext { | @@ -40,7 +41,11 @@ public class MqttTransportContext extends TransportContext { | ||
40 | 41 | ||
41 | @Getter | 42 | @Getter |
42 | @Autowired | 43 | @Autowired |
43 | - private MqttTransportAdaptor adaptor; | 44 | + private JsonMqttAdaptor jsonMqttAdaptor; |
45 | + | ||
46 | + @Getter | ||
47 | + @Autowired | ||
48 | + private ProtoMqttAdaptor protoMqttAdaptor; | ||
44 | 49 | ||
45 | @Getter | 50 | @Getter |
46 | @Value("${transport.mqtt.netty.max_payload_size}") | 51 | @Value("${transport.mqtt.netty.max_payload_size}") |
@@ -38,7 +38,6 @@ import io.netty.util.ReferenceCountUtil; | @@ -38,7 +38,6 @@ import io.netty.util.ReferenceCountUtil; | ||
38 | import io.netty.util.concurrent.Future; | 38 | import io.netty.util.concurrent.Future; |
39 | import io.netty.util.concurrent.GenericFutureListener; | 39 | import io.netty.util.concurrent.GenericFutureListener; |
40 | import lombok.extern.slf4j.Slf4j; | 40 | import lombok.extern.slf4j.Slf4j; |
41 | -import org.springframework.util.StringUtils; | ||
42 | import org.thingsboard.server.common.data.DeviceProfile; | 41 | import org.thingsboard.server.common.data.DeviceProfile; |
43 | import org.thingsboard.server.common.data.DeviceTransportType; | 42 | import org.thingsboard.server.common.data.DeviceTransportType; |
44 | import org.thingsboard.server.common.data.device.profile.MqttTopics; | 43 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
@@ -53,8 +52,6 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes | @@ -53,8 +52,6 @@ import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsRes | ||
53 | import org.thingsboard.server.common.transport.service.DefaultTransportService; | 52 | import org.thingsboard.server.common.transport.service.DefaultTransportService; |
54 | import org.thingsboard.server.gen.transport.TransportProtos; | 53 | import org.thingsboard.server.gen.transport.TransportProtos; |
55 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; | 54 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; |
56 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | ||
57 | -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | ||
58 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 55 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
59 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; | 56 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; |
60 | import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; | 57 | import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; |
@@ -66,7 +63,6 @@ import javax.net.ssl.SSLPeerUnverifiedException; | @@ -66,7 +63,6 @@ import javax.net.ssl.SSLPeerUnverifiedException; | ||
66 | import javax.security.cert.X509Certificate; | 63 | import javax.security.cert.X509Certificate; |
67 | import java.io.IOException; | 64 | import java.io.IOException; |
68 | import java.net.InetSocketAddress; | 65 | import java.net.InetSocketAddress; |
69 | -import java.nio.charset.StandardCharsets; | ||
70 | import java.util.ArrayList; | 66 | import java.util.ArrayList; |
71 | import java.util.List; | 67 | import java.util.List; |
72 | import java.util.UUID; | 68 | import java.util.UUID; |
@@ -74,7 +70,6 @@ import java.util.concurrent.ConcurrentHashMap; | @@ -74,7 +70,6 @@ import java.util.concurrent.ConcurrentHashMap; | ||
74 | import java.util.concurrent.ConcurrentMap; | 70 | import java.util.concurrent.ConcurrentMap; |
75 | 71 | ||
76 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; | 72 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; |
77 | -import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; | ||
78 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; | 73 | import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED; |
79 | import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; | 74 | import static io.netty.handler.codec.mqtt.MqttMessageType.CONNACK; |
80 | import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; | 75 | import static io.netty.handler.codec.mqtt.MqttMessageType.PINGRESP; |
@@ -95,7 +90,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -95,7 +90,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
95 | 90 | ||
96 | private final UUID sessionId; | 91 | private final UUID sessionId; |
97 | private final MqttTransportContext context; | 92 | private final MqttTransportContext context; |
98 | - private final MqttTransportAdaptor adaptor; | ||
99 | private final TransportService transportService; | 93 | private final TransportService transportService; |
100 | private final SslHandler sslHandler; | 94 | private final SslHandler sslHandler; |
101 | private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap; | 95 | private final ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap; |
@@ -108,10 +102,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -108,10 +102,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
108 | this.sessionId = UUID.randomUUID(); | 102 | this.sessionId = UUID.randomUUID(); |
109 | this.context = context; | 103 | this.context = context; |
110 | this.transportService = context.getTransportService(); | 104 | this.transportService = context.getTransportService(); |
111 | - this.adaptor = context.getAdaptor(); | ||
112 | this.sslHandler = sslHandler; | 105 | this.sslHandler = sslHandler; |
113 | this.mqttQoSMap = new ConcurrentHashMap<>(); | 106 | this.mqttQoSMap = new ConcurrentHashMap<>(); |
114 | - this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap); | 107 | + this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context); |
115 | } | 108 | } |
116 | 109 | ||
117 | @Override | 110 | @Override |
@@ -215,23 +208,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -215,23 +208,24 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
215 | 208 | ||
216 | private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { | 209 | private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { |
217 | try { | 210 | try { |
211 | + MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor(); | ||
218 | if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) { | 212 | if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) { |
219 | - TransportProtos.PostTelemetryMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); | 213 | + TransportProtos.PostTelemetryMsg postTelemetryMsg = payloadAdaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); |
220 | transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); | 214 | transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); |
221 | } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { | 215 | } else if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { |
222 | - TransportProtos.PostAttributeMsg postAttributeMsg = adaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); | 216 | + TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); |
223 | transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); | 217 | transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); |
224 | } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { | 218 | } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { |
225 | - TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); | 219 | + TransportProtos.GetAttributeRequestMsg getAttributeMsg = payloadAdaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg); |
226 | transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); | 220 | transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); |
227 | } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) { | 221 | } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC)) { |
228 | - TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = adaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg); | 222 | + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = payloadAdaptor.convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg); |
229 | transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); | 223 | transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); |
230 | } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { | 224 | } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { |
231 | - TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); | 225 | + TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = payloadAdaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); |
232 | transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); | 226 | transportService.process(deviceSessionCtx.getSessionInfo(), rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); |
233 | } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { | 227 | } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { |
234 | - TransportProtos.ClaimDeviceMsg claimDeviceMsg = adaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); | 228 | + TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); |
235 | transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); | 229 | transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); |
236 | } else { | 230 | } else { |
237 | transportService.reportActivity(deviceSessionCtx.getSessionInfo()); | 231 | transportService.reportActivity(deviceSessionCtx.getSessionInfo()); |
@@ -244,7 +238,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -244,7 +238,6 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
244 | } | 238 | } |
245 | 239 | ||
246 | 240 | ||
247 | - | ||
248 | private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { | 241 | private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { |
249 | return new TransportServiceCallback<Void>() { | 242 | return new TransportServiceCallback<Void>() { |
250 | @Override | 243 | @Override |
@@ -288,10 +281,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -288,10 +281,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
288 | break; | 281 | break; |
289 | } | 282 | } |
290 | case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC: | 283 | case MqttTopics.DEVICE_RPC_RESPONSE_SUB_TOPIC: |
284 | + case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC: | ||
291 | case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: | 285 | case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: |
292 | case MqttTopics.GATEWAY_RPC_TOPIC: | 286 | case MqttTopics.GATEWAY_RPC_TOPIC: |
293 | case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: | 287 | case MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC: |
294 | - case MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC: | ||
295 | registerSubQoS(topic, grantedQoSList, reqQoS); | 288 | registerSubQoS(topic, grantedQoSList, reqQoS); |
296 | break; | 289 | break; |
297 | default: | 290 | default: |
@@ -490,7 +483,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -490,7 +483,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
490 | if (infoNode != null) { | 483 | if (infoNode != null) { |
491 | JsonNode gatewayNode = infoNode.get("gateway"); | 484 | JsonNode gatewayNode = infoNode.get("gateway"); |
492 | if (gatewayNode != null && gatewayNode.asBoolean()) { | 485 | if (gatewayNode != null && gatewayNode.asBoolean()) { |
493 | - gatewaySessionHandler = new GatewaySessionHandler(context, deviceSessionCtx, sessionId); | 486 | + gatewaySessionHandler = new GatewaySessionHandler(deviceSessionCtx, sessionId); |
494 | } | 487 | } |
495 | } | 488 | } |
496 | } catch (IOException e) { | 489 | } catch (IOException e) { |
@@ -544,7 +537,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -544,7 +537,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
544 | @Override | 537 | @Override |
545 | public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { | 538 | public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { |
546 | try { | 539 | try { |
547 | - adaptor.convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); | 540 | + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, response).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); |
548 | } catch (Exception e) { | 541 | } catch (Exception e) { |
549 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | 542 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); |
550 | } | 543 | } |
@@ -553,7 +546,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -553,7 +546,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
553 | @Override | 546 | @Override |
554 | public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { | 547 | public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { |
555 | try { | 548 | try { |
556 | - adaptor.convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); | 549 | + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, notification).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); |
557 | } catch (Exception e) { | 550 | } catch (Exception e) { |
558 | log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e); | 551 | log.trace("[{}] Failed to convert device attributes update to MQTT msg", sessionId, e); |
559 | } | 552 | } |
@@ -569,17 +562,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -569,17 +562,17 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
569 | public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | 562 | public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { |
570 | log.trace("[{}] Received RPC command to device", sessionId); | 563 | log.trace("[{}] Received RPC command to device", sessionId); |
571 | try { | 564 | try { |
572 | - adaptor.convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); | 565 | + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcRequest).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); |
573 | } catch (Exception e) { | 566 | } catch (Exception e) { |
574 | - log.trace("[{}] Failed to convert device RPC commandto MQTT msg", sessionId, e); | 567 | + log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); |
575 | } | 568 | } |
576 | } | 569 | } |
577 | 570 | ||
578 | @Override | 571 | @Override |
579 | public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) { | 572 | public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg rpcResponse) { |
580 | - log.trace("[{}] Received RPC command to device", sessionId); | 573 | + log.trace("[{}] Received RPC command to server", sessionId); |
581 | try { | 574 | try { |
582 | - adaptor.convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); | 575 | + deviceSessionCtx.getPayloadAdaptor().convertToPublish(deviceSessionCtx, rpcResponse).ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); |
583 | } catch (Exception e) { | 576 | } catch (Exception e) { |
584 | log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); | 577 | log.trace("[{}] Failed to convert device RPC command to MQTT msg", sessionId, e); |
585 | } | 578 | } |
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; | @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
38 | import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; | 38 | import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; |
39 | 39 | ||
40 | import java.nio.charset.Charset; | 40 | import java.nio.charset.Charset; |
41 | +import java.nio.charset.StandardCharsets; | ||
41 | import java.util.Arrays; | 42 | import java.util.Arrays; |
42 | import java.util.HashSet; | 43 | import java.util.HashSet; |
43 | import java.util.Optional; | 44 | import java.util.Optional; |
@@ -47,12 +48,13 @@ import java.util.UUID; | @@ -47,12 +48,13 @@ import java.util.UUID; | ||
47 | /** | 48 | /** |
48 | * @author Andrew Shvayka | 49 | * @author Andrew Shvayka |
49 | */ | 50 | */ |
50 | -@Component("JsonMqttAdaptor") | 51 | +@Component |
51 | @Slf4j | 52 | @Slf4j |
52 | public class JsonMqttAdaptor implements MqttTransportAdaptor { | 53 | public class JsonMqttAdaptor implements MqttTransportAdaptor { |
53 | 54 | ||
55 | + protected static final Charset UTF8 = StandardCharsets.UTF_8; | ||
56 | + | ||
54 | private static final Gson GSON = new Gson(); | 57 | private static final Gson GSON = new Gson(); |
55 | - private static final Charset UTF8 = Charset.forName("UTF-8"); | ||
56 | private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); | 58 | private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); |
57 | 59 | ||
58 | @Override | 60 | @Override |
@@ -76,11 +78,81 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -76,11 +78,81 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
76 | } | 78 | } |
77 | 79 | ||
78 | @Override | 80 | @Override |
81 | + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
82 | + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true); | ||
83 | + try { | ||
84 | + return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload); | ||
85 | + } catch (IllegalStateException | JsonSyntaxException ex) { | ||
86 | + throw new AdaptorException(ex); | ||
87 | + } | ||
88 | + } | ||
89 | + | ||
90 | + @Override | ||
79 | public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 91 | public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
92 | + return processGetAttributeRequestMsg(inbound, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX); | ||
93 | + } | ||
94 | + | ||
95 | + @Override | ||
96 | + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
97 | + return processToDeviceRpcResponseMsg(inbound, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC); | ||
98 | + } | ||
99 | + | ||
100 | + @Override | ||
101 | + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
102 | + return processToServerRpcRequestMsg(ctx, inbound, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC); | ||
103 | + } | ||
104 | + | ||
105 | + @Override | ||
106 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | ||
107 | + return processConvertFromAttributeResponseMsg(ctx, responseMsg, MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX); | ||
108 | + } | ||
109 | + | ||
110 | + @Override | ||
111 | + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | ||
112 | + return processConvertFromGatewayAttributeResponseMsg(ctx, deviceName, responseMsg, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC); | ||
113 | + } | ||
114 | + | ||
115 | + @Override | ||
116 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { | ||
117 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg))); | ||
118 | + } | ||
119 | + | ||
120 | + @Override | ||
121 | + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { | ||
122 | + JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg); | ||
123 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result)); | ||
124 | + } | ||
125 | + | ||
126 | + @Override | ||
127 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | ||
128 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false))); | ||
129 | + } | ||
130 | + | ||
131 | + @Override | ||
132 | + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | ||
133 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest))); | ||
134 | + } | ||
135 | + | ||
136 | + @Override | ||
137 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { | ||
138 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); | ||
139 | + } | ||
140 | + | ||
141 | + public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | ||
142 | + String payload = validatePayload(sessionId, payloadData, false); | ||
143 | + try { | ||
144 | + return new JsonParser().parse(payload); | ||
145 | + } catch (JsonSyntaxException ex) { | ||
146 | + log.warn("Payload is in incorrect format: {}", payload); | ||
147 | + throw new AdaptorException(ex); | ||
148 | + } | ||
149 | + } | ||
150 | + | ||
151 | + protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(MqttPublishMessage inbound, String topic) throws AdaptorException { | ||
80 | String topicName = inbound.variableHeader().topicName(); | 152 | String topicName = inbound.variableHeader().topicName(); |
81 | try { | 153 | try { |
82 | TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); | 154 | TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); |
83 | - result.setRequestId(Integer.valueOf(topicName.substring(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX.length()))); | 155 | + result.setRequestId(getRequestId(topicName, topic)); |
84 | String payload = inbound.payload().toString(UTF8); | 156 | String payload = inbound.payload().toString(UTF8); |
85 | JsonElement requestBody = new JsonParser().parse(payload); | 157 | JsonElement requestBody = new JsonParser().parse(payload); |
86 | Set<String> clientKeys = toStringSet(requestBody, "clientKeys"); | 158 | Set<String> clientKeys = toStringSet(requestBody, "clientKeys"); |
@@ -98,93 +170,53 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -98,93 +170,53 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
98 | } | 170 | } |
99 | } | 171 | } |
100 | 172 | ||
101 | - @Override | ||
102 | - public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 173 | + protected TransportProtos.ToDeviceRpcResponseMsg processToDeviceRpcResponseMsg(MqttPublishMessage inbound, String topic) throws AdaptorException { |
103 | String topicName = inbound.variableHeader().topicName(); | 174 | String topicName = inbound.variableHeader().topicName(); |
104 | try { | 175 | try { |
105 | - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_RESPONSE_TOPIC.length())); | 176 | + int requestId = getRequestId(topicName, topic); |
106 | String payload = inbound.payload().toString(UTF8); | 177 | String payload = inbound.payload().toString(UTF8); |
107 | return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build(); | 178 | return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build(); |
108 | } catch (RuntimeException e) { | 179 | } catch (RuntimeException e) { |
109 | - log.warn("Failed to decode get attributes request", e); | 180 | + log.warn("Failed to decode Rpc response", e); |
110 | throw new AdaptorException(e); | 181 | throw new AdaptorException(e); |
111 | } | 182 | } |
112 | } | 183 | } |
113 | 184 | ||
114 | - @Override | ||
115 | - public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 185 | + protected TransportProtos.ToServerRpcRequestMsg processToServerRpcRequestMsg(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound, String topic) throws AdaptorException { |
116 | String topicName = inbound.variableHeader().topicName(); | 186 | String topicName = inbound.variableHeader().topicName(); |
117 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); | 187 | String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
118 | try { | 188 | try { |
119 | - Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length())); | 189 | + int requestId = getRequestId(topicName, topic); |
120 | return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId); | 190 | return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId); |
121 | } catch (IllegalStateException | JsonSyntaxException ex) { | 191 | } catch (IllegalStateException | JsonSyntaxException ex) { |
122 | throw new AdaptorException(ex); | 192 | throw new AdaptorException(ex); |
123 | } | 193 | } |
124 | } | 194 | } |
125 | 195 | ||
126 | - @Override | ||
127 | - public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
128 | - String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true); | ||
129 | - try { | ||
130 | - return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload); | ||
131 | - } catch (IllegalStateException | JsonSyntaxException ex) { | ||
132 | - throw new AdaptorException(ex); | ||
133 | - } | ||
134 | - } | ||
135 | - | ||
136 | - @Override | ||
137 | - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 196 | + protected Optional<MqttMessage> processConvertFromAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg, String topic) throws AdaptorException { |
138 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 197 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
139 | throw new AdaptorException(responseMsg.getError()); | 198 | throw new AdaptorException(responseMsg.getError()); |
140 | } else { | 199 | } else { |
141 | - Integer requestId = responseMsg.getRequestId(); | 200 | + int requestId = responseMsg.getRequestId(); |
142 | if (requestId >= 0) { | 201 | if (requestId >= 0) { |
143 | return Optional.of(createMqttPublishMsg(ctx, | 202 | return Optional.of(createMqttPublishMsg(ctx, |
144 | - MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId, | 203 | + topic + requestId, |
145 | JsonConverter.toJson(responseMsg))); | 204 | JsonConverter.toJson(responseMsg))); |
146 | } | 205 | } |
147 | return Optional.empty(); | 206 | return Optional.empty(); |
148 | } | 207 | } |
149 | } | 208 | } |
150 | 209 | ||
151 | - @Override | ||
152 | - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 210 | + protected Optional<MqttMessage> processConvertFromGatewayAttributeResponseMsg(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg, String topic) throws AdaptorException { |
153 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 211 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
154 | throw new AdaptorException(responseMsg.getError()); | 212 | throw new AdaptorException(responseMsg.getError()); |
155 | } else { | 213 | } else { |
156 | JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); | 214 | JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, responseMsg); |
157 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, result)); | 215 | + return Optional.of(createMqttPublishMsg(ctx, topic, result)); |
158 | } | 216 | } |
159 | } | 217 | } |
160 | 218 | ||
161 | - @Override | ||
162 | - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { | ||
163 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, JsonConverter.toJson(notificationMsg))); | ||
164 | - } | ||
165 | - | ||
166 | - @Override | ||
167 | - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException { | ||
168 | - JsonObject result = JsonConverter.getJsonObjectForGateway(deviceName, notificationMsg); | ||
169 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, result)); | ||
170 | - } | ||
171 | - | ||
172 | - @Override | ||
173 | - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { | ||
174 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), JsonConverter.toJson(rpcRequest, false))); | ||
175 | - } | ||
176 | - | ||
177 | - @Override | ||
178 | - public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException { | ||
179 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, JsonConverter.toGatewayJson(deviceName, rpcRequest))); | ||
180 | - } | ||
181 | - | ||
182 | - @Override | ||
183 | - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { | ||
184 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), JsonConverter.toJson(rpcResponse))); | ||
185 | - } | ||
186 | - | ||
187 | - private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) { | 219 | + protected MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, JsonElement json) { |
188 | MqttFixedHeader mqttFixedHeader = | 220 | MqttFixedHeader mqttFixedHeader = |
189 | new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); | 221 | new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); |
190 | MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); | 222 | MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); |
@@ -202,16 +234,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -202,16 +234,6 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
202 | } | 234 | } |
203 | } | 235 | } |
204 | 236 | ||
205 | - public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | ||
206 | - String payload = validatePayload(sessionId, payloadData, false); | ||
207 | - try { | ||
208 | - return new JsonParser().parse(payload); | ||
209 | - } catch (JsonSyntaxException ex) { | ||
210 | - log.warn("Payload is in incorrect format: {}", payload); | ||
211 | - throw new AdaptorException(ex); | ||
212 | - } | ||
213 | - } | ||
214 | - | ||
215 | private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException { | 237 | private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException { |
216 | String payload = payloadData.toString(UTF8); | 238 | String payload = payloadData.toString(UTF8); |
217 | if (payload == null) { | 239 | if (payload == null) { |
@@ -223,4 +245,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -223,4 +245,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
223 | return payload; | 245 | return payload; |
224 | } | 246 | } |
225 | 247 | ||
248 | + private int getRequestId(String topicName, String topic) { | ||
249 | + return Integer.parseInt(topicName.substring(topic.length())); | ||
250 | + } | ||
251 | + | ||
226 | } | 252 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.transport.mqtt.adaptors; | ||
17 | + | ||
18 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
19 | +import io.netty.buffer.ByteBuf; | ||
20 | +import io.netty.buffer.ByteBufAllocator; | ||
21 | +import io.netty.buffer.UnpooledByteBufAllocator; | ||
22 | +import io.netty.handler.codec.mqtt.MqttFixedHeader; | ||
23 | +import io.netty.handler.codec.mqtt.MqttMessage; | ||
24 | +import io.netty.handler.codec.mqtt.MqttMessageType; | ||
25 | +import io.netty.handler.codec.mqtt.MqttPublishMessage; | ||
26 | +import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; | ||
27 | +import lombok.extern.slf4j.Slf4j; | ||
28 | +import org.springframework.stereotype.Component; | ||
29 | +import org.springframework.util.StringUtils; | ||
30 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
31 | +import org.thingsboard.server.common.transport.adaptor.AdaptorException; | ||
32 | +import org.thingsboard.server.common.transport.adaptor.ProtoConverter; | ||
33 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
34 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
35 | +import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; | ||
36 | + | ||
37 | +import java.util.Optional; | ||
38 | + | ||
39 | +@Component | ||
40 | +@Slf4j | ||
41 | +public class ProtoMqttAdaptor implements MqttTransportAdaptor { | ||
42 | + | ||
43 | + private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); | ||
44 | + | ||
45 | + @Override | ||
46 | + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
47 | + byte[] bytes = toBytes(inbound.payload()); | ||
48 | + try { | ||
49 | + return ProtoConverter.convertToTelemetryProto(bytes); | ||
50 | + } catch (InvalidProtocolBufferException | IllegalArgumentException e) { | ||
51 | + throw new AdaptorException(e); | ||
52 | + } | ||
53 | + } | ||
54 | + | ||
55 | + @Override | ||
56 | + public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
57 | + byte[] bytes = toBytes(inbound.payload()); | ||
58 | + try { | ||
59 | + return ProtoConverter.validatePostAttributeMsg(bytes); | ||
60 | + } catch (InvalidProtocolBufferException | IllegalArgumentException e) { | ||
61 | + throw new AdaptorException(e); | ||
62 | + } | ||
63 | + } | ||
64 | + | ||
65 | + @Override | ||
66 | + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
67 | + byte[] bytes = toBytes(inbound.payload()); | ||
68 | + try { | ||
69 | + return ProtoConverter.convertToClaimDeviceProto(ctx.getDeviceId(), bytes); | ||
70 | + } catch (InvalidProtocolBufferException e) { | ||
71 | + throw new AdaptorException(e); | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
77 | + byte[] bytes = toBytes(inbound.payload()); | ||
78 | + String topicName = inbound.variableHeader().topicName(); | ||
79 | + int requestId = getRequestId(topicName, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX); | ||
80 | + try { | ||
81 | + return ProtoConverter.convertToGetAttributeRequestMessage(bytes, requestId); | ||
82 | + } catch (InvalidProtocolBufferException e) { | ||
83 | + log.warn("Failed to decode get attributes request", e); | ||
84 | + throw new AdaptorException(e); | ||
85 | + } | ||
86 | + } | ||
87 | + | ||
88 | + @Override | ||
89 | + public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException { | ||
90 | + byte[] bytes = toBytes(mqttMsg.payload()); | ||
91 | + try { | ||
92 | + return TransportProtos.ToDeviceRpcResponseMsg.parseFrom(bytes); | ||
93 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
94 | + log.warn("Failed to decode Rpc response", e); | ||
95 | + throw new AdaptorException(e); | ||
96 | + } | ||
97 | + } | ||
98 | + | ||
99 | + @Override | ||
100 | + public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException { | ||
101 | + byte[] bytes = toBytes(mqttMsg.payload()); | ||
102 | + String topicName = mqttMsg.variableHeader().topicName(); | ||
103 | + try { | ||
104 | + int requestId = getRequestId(topicName, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC); | ||
105 | + return ProtoConverter.convertToServerRpcRequest(bytes, requestId); | ||
106 | + } catch (InvalidProtocolBufferException e) { | ||
107 | + throw new AdaptorException(e); | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | + @Override | ||
112 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | ||
113 | + if (!StringUtils.isEmpty(responseMsg.getError())) { | ||
114 | + throw new AdaptorException(responseMsg.getError()); | ||
115 | + } else { | ||
116 | + int requestId = responseMsg.getRequestId(); | ||
117 | + if (requestId >= 0) { | ||
118 | + return Optional.of(createMqttPublishMsg(ctx, | ||
119 | + MqttTopics.DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + requestId, | ||
120 | + responseMsg.toByteArray())); | ||
121 | + } | ||
122 | + return Optional.empty(); | ||
123 | + } | ||
124 | + } | ||
125 | + | ||
126 | + | ||
127 | + @Override | ||
128 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | ||
129 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_REQUESTS_TOPIC + rpcRequest.getRequestId(), rpcRequest.toByteArray())); | ||
130 | + } | ||
131 | + | ||
132 | + @Override | ||
133 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.ToServerRpcResponseMsg rpcResponse) { | ||
134 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_RPC_RESPONSE_TOPIC + rpcResponse.getRequestId(), rpcResponse.toByteArray())); | ||
135 | + } | ||
136 | + | ||
137 | + @Override | ||
138 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { | ||
139 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_ATTRIBUTES_TOPIC, notificationMsg.toByteArray())); | ||
140 | + } | ||
141 | + | ||
142 | + @Override | ||
143 | + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | ||
144 | + if (!StringUtils.isEmpty(responseMsg.getError())) { | ||
145 | + throw new AdaptorException(responseMsg.getError()); | ||
146 | + } else { | ||
147 | + TransportApiProtos.GatewayAttributeResponseMsg.Builder responseMsgBuilder = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder(); | ||
148 | + responseMsgBuilder.setDeviceName(deviceName); | ||
149 | + responseMsgBuilder.setResponseMsg(responseMsg); | ||
150 | + byte[] payloadBytes = responseMsgBuilder.build().toByteArray(); | ||
151 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, payloadBytes)); | ||
152 | + } | ||
153 | + } | ||
154 | + | ||
155 | + @Override | ||
156 | + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) { | ||
157 | + TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder builder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); | ||
158 | + builder.setDeviceName(deviceName); | ||
159 | + builder.setNotificationMsg(notificationMsg); | ||
160 | + byte[] payloadBytes = builder.build().toByteArray(); | ||
161 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, payloadBytes)); | ||
162 | + } | ||
163 | + | ||
164 | + @Override | ||
165 | + public Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { | ||
166 | + TransportApiProtos.GatewayDeviceRpcRequestMsg.Builder builder = TransportApiProtos.GatewayDeviceRpcRequestMsg.newBuilder(); | ||
167 | + builder.setDeviceName(deviceName); | ||
168 | + builder.setRpcRequestMsg(rpcRequest); | ||
169 | + byte[] payloadBytes = builder.build().toByteArray(); | ||
170 | + return Optional.of(createMqttPublishMsg(ctx, MqttTopics.GATEWAY_RPC_TOPIC, payloadBytes)); | ||
171 | + } | ||
172 | + | ||
173 | + public static byte[] toBytes(ByteBuf inbound) { | ||
174 | + byte[] bytes = new byte[inbound.readableBytes()]; | ||
175 | + int readerIndex = inbound.readerIndex(); | ||
176 | + inbound.getBytes(readerIndex, bytes); | ||
177 | + return bytes; | ||
178 | + } | ||
179 | + | ||
180 | + private MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadBytes) { | ||
181 | + MqttFixedHeader mqttFixedHeader = | ||
182 | + new MqttFixedHeader(MqttMessageType.PUBLISH, false, ctx.getQoSForTopic(topic), false, 0); | ||
183 | + MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, ctx.nextMsgId()); | ||
184 | + ByteBuf payload = ALLOCATOR.buffer(); | ||
185 | + payload.writeBytes(payloadBytes); | ||
186 | + return new MqttPublishMessage(mqttFixedHeader, header, payload); | ||
187 | + } | ||
188 | + | ||
189 | + private int getRequestId(String topicName, String topic) { | ||
190 | + return Integer.parseInt(topicName.substring(topic.length())); | ||
191 | + } | ||
192 | + | ||
193 | +} |
@@ -20,9 +20,11 @@ import lombok.Getter; | @@ -20,9 +20,11 @@ import lombok.Getter; | ||
20 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
21 | import org.thingsboard.server.common.data.DeviceProfile; | 21 | import org.thingsboard.server.common.data.DeviceProfile; |
22 | import org.thingsboard.server.common.data.DeviceTransportType; | 22 | import org.thingsboard.server.common.data.DeviceTransportType; |
23 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
23 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | 24 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
24 | import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | 25 | import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
25 | -import org.thingsboard.server.common.data.device.profile.MqttTopics; | 26 | +import org.thingsboard.server.transport.mqtt.MqttTransportContext; |
27 | +import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; | ||
26 | import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter; | 28 | import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter; |
27 | import org.thingsboard.server.transport.mqtt.util.MqttTopicFilterFactory; | 29 | import org.thingsboard.server.transport.mqtt.util.MqttTopicFilterFactory; |
28 | 30 | ||
@@ -38,14 +40,19 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | @@ -38,14 +40,19 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | ||
38 | 40 | ||
39 | @Getter | 41 | @Getter |
40 | private ChannelHandlerContext channel; | 42 | private ChannelHandlerContext channel; |
43 | + | ||
44 | + @Getter | ||
45 | + private MqttTransportContext context; | ||
46 | + | ||
41 | private final AtomicInteger msgIdSeq = new AtomicInteger(0); | 47 | private final AtomicInteger msgIdSeq = new AtomicInteger(0); |
42 | 48 | ||
43 | private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); | 49 | private volatile MqttTopicFilter telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); |
44 | private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); | 50 | private volatile MqttTopicFilter attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); |
51 | + private volatile TransportPayloadType payloadType = TransportPayloadType.JSON; | ||
45 | 52 | ||
46 | - | ||
47 | - public DeviceSessionCtx(UUID sessionId, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap) { | 53 | + public DeviceSessionCtx(UUID sessionId, ConcurrentMap<MqttTopicMatcher, Integer> mqttQoSMap, MqttTransportContext context) { |
48 | super(sessionId, mqttQoSMap); | 54 | super(sessionId, mqttQoSMap); |
55 | + this.context = context; | ||
49 | } | 56 | } |
50 | 57 | ||
51 | public void setChannel(ChannelHandlerContext channel) { | 58 | public void setChannel(ChannelHandlerContext channel) { |
@@ -56,14 +63,20 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | @@ -56,14 +63,20 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | ||
56 | return msgIdSeq.incrementAndGet(); | 63 | return msgIdSeq.incrementAndGet(); |
57 | } | 64 | } |
58 | 65 | ||
59 | - public boolean isDeviceTelemetryTopic(String topicName) { | ||
60 | - return telemetryTopicFilter.filter(topicName); | ||
61 | - } | 66 | + public boolean isDeviceTelemetryTopic(String topicName) { return telemetryTopicFilter.filter(topicName); } |
62 | 67 | ||
63 | public boolean isDeviceAttributesTopic(String topicName) { | 68 | public boolean isDeviceAttributesTopic(String topicName) { |
64 | return attributesTopicFilter.filter(topicName); | 69 | return attributesTopicFilter.filter(topicName); |
65 | } | 70 | } |
66 | 71 | ||
72 | + public MqttTransportAdaptor getPayloadAdaptor() { | ||
73 | + return payloadType.equals(TransportPayloadType.JSON) ? context.getJsonMqttAdaptor() : context.getProtoMqttAdaptor(); | ||
74 | + } | ||
75 | + | ||
76 | + public boolean isJsonPayloadType() { | ||
77 | + return payloadType.equals(TransportPayloadType.JSON); | ||
78 | + } | ||
79 | + | ||
67 | @Override | 80 | @Override |
68 | public void setDeviceProfile(DeviceProfile deviceProfile) { | 81 | public void setDeviceProfile(DeviceProfile deviceProfile) { |
69 | super.setDeviceProfile(deviceProfile); | 82 | super.setDeviceProfile(deviceProfile); |
@@ -76,11 +89,13 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | @@ -76,11 +89,13 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | ||
76 | updateTopicFilters(deviceProfile); | 89 | updateTopicFilters(deviceProfile); |
77 | } | 90 | } |
78 | 91 | ||
92 | + | ||
79 | private void updateTopicFilters(DeviceProfile deviceProfile) { | 93 | private void updateTopicFilters(DeviceProfile deviceProfile) { |
80 | DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | 94 | DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); |
81 | if (transportConfiguration.getType().equals(DeviceTransportType.MQTT) && | 95 | if (transportConfiguration.getType().equals(DeviceTransportType.MQTT) && |
82 | transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { | 96 | transportConfiguration instanceof MqttDeviceProfileTransportConfiguration) { |
83 | MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | 97 | MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration; |
98 | + payloadType = mqttConfig.getTransportPayloadType(); | ||
84 | telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic()); | 99 | telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic()); |
85 | attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic()); | 100 | attributesTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic()); |
86 | } else { | 101 | } else { |
@@ -88,4 +103,5 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | @@ -88,4 +103,5 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { | ||
88 | attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); | 103 | attributesTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); |
89 | } | 104 | } |
90 | } | 105 | } |
106 | + | ||
91 | } | 107 | } |
@@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.DeviceProfile; | @@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.DeviceProfile; | ||
20 | import org.thingsboard.server.common.transport.SessionMsgListener; | 20 | import org.thingsboard.server.common.transport.SessionMsgListener; |
21 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | 21 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
22 | import org.thingsboard.server.gen.transport.TransportProtos; | 22 | import org.thingsboard.server.gen.transport.TransportProtos; |
23 | -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | ||
24 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 23 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
25 | 24 | ||
26 | import java.util.UUID; | 25 | import java.util.UUID; |
@@ -70,7 +69,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | @@ -70,7 +69,7 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | ||
70 | @Override | 69 | @Override |
71 | public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { | 70 | public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg response) { |
72 | try { | 71 | try { |
73 | - parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), response).ifPresent(parent::writeAndFlush); | 72 | + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), response).ifPresent(parent::writeAndFlush); |
74 | } catch (Exception e) { | 73 | } catch (Exception e) { |
75 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | 74 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); |
76 | } | 75 | } |
@@ -79,27 +78,27 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | @@ -79,27 +78,27 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple | ||
79 | @Override | 78 | @Override |
80 | public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { | 79 | public void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg notification) { |
81 | try { | 80 | try { |
82 | - parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::writeAndFlush); | 81 | + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), notification).ifPresent(parent::writeAndFlush); |
83 | } catch (Exception e) { | 82 | } catch (Exception e) { |
84 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | 83 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); |
85 | } | 84 | } |
86 | } | 85 | } |
87 | 86 | ||
88 | @Override | 87 | @Override |
89 | - public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { | ||
90 | - parent.deregisterSession(getDeviceInfo().getDeviceName()); | ||
91 | - } | ||
92 | - | ||
93 | - @Override | ||
94 | public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) { | 88 | public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg request) { |
95 | try { | 89 | try { |
96 | - parent.getAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush); | 90 | + parent.getPayloadAdaptor().convertToGatewayPublish(this, getDeviceInfo().getDeviceName(), request).ifPresent(parent::writeAndFlush); |
97 | } catch (Exception e) { | 91 | } catch (Exception e) { |
98 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); | 92 | log.trace("[{}] Failed to convert device attributes response to MQTT msg", sessionId, e); |
99 | } | 93 | } |
100 | } | 94 | } |
101 | 95 | ||
102 | @Override | 96 | @Override |
97 | + public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { | ||
98 | + parent.deregisterSession(getDeviceInfo().getDeviceName()); | ||
99 | + } | ||
100 | + | ||
101 | + @Override | ||
103 | public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { | 102 | public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { |
104 | // This feature is not supported in the TB IoT Gateway yet. | 103 | // This feature is not supported in the TB IoT Gateway yet. |
105 | } | 104 | } |
@@ -25,32 +25,38 @@ import com.google.gson.JsonElement; | @@ -25,32 +25,38 @@ import com.google.gson.JsonElement; | ||
25 | import com.google.gson.JsonNull; | 25 | import com.google.gson.JsonNull; |
26 | import com.google.gson.JsonObject; | 26 | import com.google.gson.JsonObject; |
27 | import com.google.gson.JsonSyntaxException; | 27 | import com.google.gson.JsonSyntaxException; |
28 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
29 | +import com.google.protobuf.ProtocolStringList; | ||
30 | +import io.netty.buffer.ByteBuf; | ||
28 | import io.netty.channel.ChannelHandlerContext; | 31 | import io.netty.channel.ChannelHandlerContext; |
29 | import io.netty.handler.codec.mqtt.MqttMessage; | 32 | import io.netty.handler.codec.mqtt.MqttMessage; |
30 | import io.netty.handler.codec.mqtt.MqttPublishMessage; | 33 | import io.netty.handler.codec.mqtt.MqttPublishMessage; |
31 | import lombok.extern.slf4j.Slf4j; | 34 | import lombok.extern.slf4j.Slf4j; |
35 | +import org.springframework.util.CollectionUtils; | ||
32 | import org.springframework.util.StringUtils; | 36 | import org.springframework.util.StringUtils; |
33 | import org.thingsboard.server.common.data.id.DeviceId; | 37 | import org.thingsboard.server.common.data.id.DeviceId; |
34 | import org.thingsboard.server.common.transport.TransportService; | 38 | import org.thingsboard.server.common.transport.TransportService; |
35 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 39 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
36 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 40 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
37 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 41 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
42 | +import org.thingsboard.server.common.transport.adaptor.ProtoConverter; | ||
38 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; | 43 | import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; |
39 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; | 44 | import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
40 | import org.thingsboard.server.common.transport.service.DefaultTransportService; | 45 | import org.thingsboard.server.common.transport.service.DefaultTransportService; |
46 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
41 | import org.thingsboard.server.gen.transport.TransportProtos; | 47 | import org.thingsboard.server.gen.transport.TransportProtos; |
42 | -import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | ||
43 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; |
44 | -import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; | ||
45 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 49 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
46 | import org.thingsboard.server.transport.mqtt.MqttTransportContext; | 50 | import org.thingsboard.server.transport.mqtt.MqttTransportContext; |
47 | import org.thingsboard.server.transport.mqtt.MqttTransportHandler; | 51 | import org.thingsboard.server.transport.mqtt.MqttTransportHandler; |
48 | import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; | 52 | import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; |
49 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; | 53 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; |
54 | +import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor; | ||
50 | 55 | ||
51 | import javax.annotation.Nullable; | 56 | import javax.annotation.Nullable; |
52 | import java.util.Collections; | 57 | import java.util.Collections; |
53 | import java.util.HashSet; | 58 | import java.util.HashSet; |
59 | +import java.util.List; | ||
54 | import java.util.Map; | 60 | import java.util.Map; |
55 | import java.util.Set; | 61 | import java.util.Set; |
56 | import java.util.UUID; | 62 | import java.util.UUID; |
@@ -80,8 +86,8 @@ public class GatewaySessionHandler { | @@ -80,8 +86,8 @@ public class GatewaySessionHandler { | ||
80 | private final ChannelHandlerContext channel; | 86 | private final ChannelHandlerContext channel; |
81 | private final DeviceSessionCtx deviceSessionCtx; | 87 | private final DeviceSessionCtx deviceSessionCtx; |
82 | 88 | ||
83 | - public GatewaySessionHandler(MqttTransportContext context, DeviceSessionCtx deviceSessionCtx, UUID sessionId) { | ||
84 | - this.context = context; | 89 | + public GatewaySessionHandler(DeviceSessionCtx deviceSessionCtx, UUID sessionId) { |
90 | + this.context = deviceSessionCtx.getContext(); | ||
85 | this.transportService = context.getTransportService(); | 91 | this.transportService = context.getTransportService(); |
86 | this.deviceSessionCtx = deviceSessionCtx; | 92 | this.deviceSessionCtx = deviceSessionCtx; |
87 | this.gateway = deviceSessionCtx.getDeviceInfo(); | 93 | this.gateway = deviceSessionCtx.getDeviceInfo(); |
@@ -93,10 +99,108 @@ public class GatewaySessionHandler { | @@ -93,10 +99,108 @@ public class GatewaySessionHandler { | ||
93 | this.channel = deviceSessionCtx.getChannel(); | 99 | this.channel = deviceSessionCtx.getChannel(); |
94 | } | 100 | } |
95 | 101 | ||
96 | - public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { | ||
97 | - JsonElement json = getJson(msg); | ||
98 | - String deviceName = checkDeviceName(getDeviceName(json)); | ||
99 | - String deviceType = getDeviceType(json); | 102 | + public void onDeviceConnect(MqttPublishMessage mqttMsg) throws AdaptorException { |
103 | + if (isJsonPayloadType()) { | ||
104 | + onDeviceConnectJson(mqttMsg); | ||
105 | + } else { | ||
106 | + onDeviceConnectProto(mqttMsg); | ||
107 | + } | ||
108 | + } | ||
109 | + | ||
110 | + public void onDeviceDisconnect(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
111 | + if (isJsonPayloadType()) { | ||
112 | + onDeviceDisconnectJson(mqttMsg); | ||
113 | + } else { | ||
114 | + onDeviceDisconnectProto(mqttMsg); | ||
115 | + } | ||
116 | + } | ||
117 | + | ||
118 | + public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
119 | + int msgId = getMsgId(mqttMsg); | ||
120 | + ByteBuf payload = mqttMsg.payload(); | ||
121 | + if (isJsonPayloadType()) { | ||
122 | + onDeviceTelemetryJson(msgId, payload); | ||
123 | + } else { | ||
124 | + onDeviceTelemetryProto(msgId, payload); | ||
125 | + } | ||
126 | + } | ||
127 | + | ||
128 | + public void onDeviceClaim(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
129 | + int msgId = getMsgId(mqttMsg); | ||
130 | + ByteBuf payload = mqttMsg.payload(); | ||
131 | + if (isJsonPayloadType()) { | ||
132 | + onDeviceClaimJson(msgId, payload); | ||
133 | + } else { | ||
134 | + onDeviceClaimProto(msgId, payload); | ||
135 | + } | ||
136 | + } | ||
137 | + | ||
138 | + public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
139 | + int msgId = getMsgId(mqttMsg); | ||
140 | + ByteBuf payload = mqttMsg.payload(); | ||
141 | + if (isJsonPayloadType()) { | ||
142 | + onDeviceAttributesJson(msgId, payload); | ||
143 | + } else { | ||
144 | + onDeviceAttributesProto(msgId, payload); | ||
145 | + } | ||
146 | + } | ||
147 | + | ||
148 | + public void onDeviceAttributesRequest(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
149 | + if (isJsonPayloadType()) { | ||
150 | + onDeviceAttributesRequestJson(mqttMsg); | ||
151 | + } else { | ||
152 | + onDeviceAttributesRequestProto(mqttMsg); | ||
153 | + } | ||
154 | + } | ||
155 | + | ||
156 | + public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
157 | + int msgId = getMsgId(mqttMsg); | ||
158 | + ByteBuf payload = mqttMsg.payload(); | ||
159 | + if (isJsonPayloadType()) { | ||
160 | + onDeviceRpcResponseJson(msgId, payload); | ||
161 | + } else { | ||
162 | + onDeviceRpcResponseProto(msgId, payload); | ||
163 | + } | ||
164 | + } | ||
165 | + | ||
166 | + public void onGatewayDisconnect() { | ||
167 | + devices.forEach(this::deregisterSession); | ||
168 | + } | ||
169 | + | ||
170 | + public String getNodeId() { | ||
171 | + return context.getNodeId(); | ||
172 | + } | ||
173 | + | ||
174 | + public UUID getSessionId() { | ||
175 | + return sessionId; | ||
176 | + } | ||
177 | + | ||
178 | + public MqttTransportAdaptor getPayloadAdaptor() { | ||
179 | + return deviceSessionCtx.getPayloadAdaptor(); | ||
180 | + } | ||
181 | + | ||
182 | + void deregisterSession(String deviceName) { | ||
183 | + GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); | ||
184 | + if (deviceSessionCtx != null) { | ||
185 | + deregisterSession(deviceName, deviceSessionCtx); | ||
186 | + } else { | ||
187 | + log.debug("[{}] Device [{}] was already removed from the gateway session", sessionId, deviceName); | ||
188 | + } | ||
189 | + } | ||
190 | + | ||
191 | + void writeAndFlush(MqttMessage mqttMessage) { | ||
192 | + channel.writeAndFlush(mqttMessage); | ||
193 | + } | ||
194 | + | ||
195 | + int nextMsgId() { | ||
196 | + return deviceSessionCtx.nextMsgId(); | ||
197 | + } | ||
198 | + | ||
199 | + private boolean isJsonPayloadType() { | ||
200 | + return deviceSessionCtx.isJsonPayloadType(); | ||
201 | + } | ||
202 | + | ||
203 | + private void processOnConnect(MqttPublishMessage msg, String deviceName, String deviceType) { | ||
100 | log.trace("[{}] onDeviceConnect: {}", sessionId, deviceName); | 204 | log.trace("[{}] onDeviceConnect: {}", sessionId, deviceName); |
101 | Futures.addCallback(onDeviceConnect(deviceName, deviceType), new FutureCallback<GatewayDeviceSessionCtx>() { | 205 | Futures.addCallback(onDeviceConnect(deviceName, deviceType), new FutureCallback<GatewayDeviceSessionCtx>() { |
102 | @Override | 206 | @Override |
@@ -183,28 +287,50 @@ public class GatewaySessionHandler { | @@ -183,28 +287,50 @@ public class GatewaySessionHandler { | ||
183 | return future; | 287 | return future; |
184 | } | 288 | } |
185 | 289 | ||
186 | - public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException { | 290 | + private int getMsgId(MqttPublishMessage mqttMsg) { |
291 | + return mqttMsg.variableHeader().packetId(); | ||
292 | + } | ||
293 | + | ||
294 | + private void onDeviceConnectJson(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
295 | + JsonElement json = getJson(mqttMsg); | ||
296 | + String deviceName = checkDeviceName(getDeviceName(json)); | ||
297 | + String deviceType = getDeviceType(json); | ||
298 | + processOnConnect(mqttMsg, deviceName, deviceType); | ||
299 | + } | ||
300 | + | ||
301 | + private void onDeviceConnectProto(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
302 | + try { | ||
303 | + TransportApiProtos.ConnectMsg connectProto = TransportApiProtos.ConnectMsg.parseFrom(getBytes(mqttMsg.payload())); | ||
304 | + String deviceName = checkDeviceName(connectProto.getDeviceName()); | ||
305 | + String deviceType = StringUtils.isEmpty(connectProto.getDeviceType()) ? DEFAULT_DEVICE_TYPE : connectProto.getDeviceType(); | ||
306 | + processOnConnect(mqttMsg, deviceName, deviceType); | ||
307 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
308 | + throw new AdaptorException(e); | ||
309 | + } | ||
310 | + } | ||
311 | + | ||
312 | + private void onDeviceDisconnectJson(MqttPublishMessage msg) throws AdaptorException { | ||
187 | String deviceName = checkDeviceName(getDeviceName(getJson(msg))); | 313 | String deviceName = checkDeviceName(getDeviceName(getJson(msg))); |
188 | - deregisterSession(deviceName); | ||
189 | - ack(msg); | 314 | + processOnDisconnect(msg, deviceName); |
190 | } | 315 | } |
191 | 316 | ||
192 | - void deregisterSession(String deviceName) { | ||
193 | - GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); | ||
194 | - if (deviceSessionCtx != null) { | ||
195 | - deregisterSession(deviceName, deviceSessionCtx); | ||
196 | - } else { | ||
197 | - log.debug("[{}] Device [{}] was already removed from the gateway session", sessionId, deviceName); | 317 | + private void onDeviceDisconnectProto(MqttPublishMessage mqttMsg) throws AdaptorException { |
318 | + try { | ||
319 | + TransportApiProtos.DisconnectMsg connectProto = TransportApiProtos.DisconnectMsg.parseFrom(getBytes(mqttMsg.payload())); | ||
320 | + String deviceName = checkDeviceName(connectProto.getDeviceName()); | ||
321 | + processOnDisconnect(mqttMsg, deviceName); | ||
322 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
323 | + throw new AdaptorException(e); | ||
198 | } | 324 | } |
199 | } | 325 | } |
200 | 326 | ||
201 | - public void onGatewayDisconnect() { | ||
202 | - devices.forEach(this::deregisterSession); | 327 | + private void processOnDisconnect(MqttPublishMessage msg, String deviceName) { |
328 | + deregisterSession(deviceName); | ||
329 | + ack(msg); | ||
203 | } | 330 | } |
204 | 331 | ||
205 | - public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
206 | - JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | ||
207 | - int msgId = mqttMsg.variableHeader().packetId(); | 332 | + private void onDeviceTelemetryJson(int msgId, ByteBuf payload) throws AdaptorException { |
333 | + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
208 | if (json.isJsonObject()) { | 334 | if (json.isJsonObject()) { |
209 | JsonObject jsonObj = json.getAsJsonObject(); | 335 | JsonObject jsonObj = json.getAsJsonObject(); |
210 | for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { | 336 | for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { |
@@ -218,7 +344,7 @@ public class GatewaySessionHandler { | @@ -218,7 +344,7 @@ public class GatewaySessionHandler { | ||
218 | } | 344 | } |
219 | try { | 345 | try { |
220 | TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(deviceEntry.getValue().getAsJsonArray()); | 346 | TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(deviceEntry.getValue().getAsJsonArray()); |
221 | - transportService.process(deviceCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(channel, deviceName, msgId, postTelemetryMsg)); | 347 | + processPostTelemetryMsg(deviceCtx, postTelemetryMsg, deviceName, msgId); |
222 | } catch (Throwable e) { | 348 | } catch (Throwable e) { |
223 | log.warn("[{}][{}] Failed to convert telemetry: {}", gateway.getDeviceId(), deviceName, deviceEntry.getValue(), e); | 349 | log.warn("[{}][{}] Failed to convert telemetry: {}", gateway.getDeviceId(), deviceName, deviceEntry.getValue(), e); |
224 | } | 350 | } |
@@ -235,9 +361,47 @@ public class GatewaySessionHandler { | @@ -235,9 +361,47 @@ public class GatewaySessionHandler { | ||
235 | } | 361 | } |
236 | } | 362 | } |
237 | 363 | ||
238 | - public void onDeviceClaim(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
239 | - JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | ||
240 | - int msgId = mqttMsg.variableHeader().packetId(); | 364 | + private void onDeviceTelemetryProto(int msgId, ByteBuf payload) throws AdaptorException { |
365 | + try { | ||
366 | + TransportApiProtos.GatewayTelemetryMsg telemetryMsgProto = TransportApiProtos.GatewayTelemetryMsg.parseFrom(getBytes(payload)); | ||
367 | + List<TransportApiProtos.TelemetryMsg> deviceMsgList = telemetryMsgProto.getMsgList(); | ||
368 | + if (!CollectionUtils.isEmpty(deviceMsgList)) { | ||
369 | + deviceMsgList.forEach(telemetryMsg -> { | ||
370 | + String deviceName = checkDeviceName(telemetryMsg.getDeviceName()); | ||
371 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
372 | + new FutureCallback<GatewayDeviceSessionCtx>() { | ||
373 | + @Override | ||
374 | + public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
375 | + TransportProtos.PostTelemetryMsg msg = telemetryMsg.getMsg(); | ||
376 | + try { | ||
377 | + TransportProtos.PostTelemetryMsg postTelemetryMsg = ProtoConverter.validatePostTelemetryMsg(msg.toByteArray()); | ||
378 | + processPostTelemetryMsg(deviceCtx, postTelemetryMsg, deviceName, msgId); | ||
379 | + } catch (Throwable e) { | ||
380 | + log.warn("[{}][{}] Failed to convert telemetry: {}", gateway.getDeviceId(), deviceName, msg, e); | ||
381 | + } | ||
382 | + } | ||
383 | + | ||
384 | + @Override | ||
385 | + public void onFailure(Throwable t) { | ||
386 | + log.debug("[{}] Failed to process device telemetry command: {}", sessionId, deviceName, t); | ||
387 | + } | ||
388 | + }, context.getExecutor()); | ||
389 | + }); | ||
390 | + } else { | ||
391 | + log.debug("[{}] Devices telemetry messages is empty for: [{}]", sessionId, gateway.getDeviceId()); | ||
392 | + throw new IllegalArgumentException("[" + sessionId + "] Devices telemetry messages is empty for [" + gateway.getDeviceId() + "]"); | ||
393 | + } | ||
394 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
395 | + throw new AdaptorException(e); | ||
396 | + } | ||
397 | + } | ||
398 | + | ||
399 | + private void processPostTelemetryMsg(GatewayDeviceSessionCtx deviceCtx, TransportProtos.PostTelemetryMsg postTelemetryMsg, String deviceName, int msgId) { | ||
400 | + transportService.process(deviceCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(channel, deviceName, msgId, postTelemetryMsg)); | ||
401 | + } | ||
402 | + | ||
403 | + private void onDeviceClaimJson(int msgId, ByteBuf payload) throws AdaptorException { | ||
404 | + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
241 | if (json.isJsonObject()) { | 405 | if (json.isJsonObject()) { |
242 | JsonObject jsonObj = json.getAsJsonObject(); | 406 | JsonObject jsonObj = json.getAsJsonObject(); |
243 | for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { | 407 | for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { |
@@ -252,7 +416,7 @@ public class GatewaySessionHandler { | @@ -252,7 +416,7 @@ public class GatewaySessionHandler { | ||
252 | try { | 416 | try { |
253 | DeviceId deviceId = deviceCtx.getDeviceId(); | 417 | DeviceId deviceId = deviceCtx.getDeviceId(); |
254 | TransportProtos.ClaimDeviceMsg claimDeviceMsg = JsonConverter.convertToClaimDeviceProto(deviceId, deviceEntry.getValue()); | 418 | TransportProtos.ClaimDeviceMsg claimDeviceMsg = JsonConverter.convertToClaimDeviceProto(deviceId, deviceEntry.getValue()); |
255 | - transportService.process(deviceCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(channel, deviceName, msgId, claimDeviceMsg)); | 419 | + processClaimDeviceMsg(deviceCtx, claimDeviceMsg, deviceName, msgId); |
256 | } catch (Throwable e) { | 420 | } catch (Throwable e) { |
257 | log.warn("[{}][{}] Failed to convert claim message: {}", gateway.getDeviceId(), deviceName, deviceEntry.getValue(), e); | 421 | log.warn("[{}][{}] Failed to convert claim message: {}", gateway.getDeviceId(), deviceName, deviceEntry.getValue(), e); |
258 | } | 422 | } |
@@ -269,9 +433,51 @@ public class GatewaySessionHandler { | @@ -269,9 +433,51 @@ public class GatewaySessionHandler { | ||
269 | } | 433 | } |
270 | } | 434 | } |
271 | 435 | ||
272 | - public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
273 | - JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | ||
274 | - int msgId = mqttMsg.variableHeader().packetId(); | 436 | + private void onDeviceClaimProto(int msgId, ByteBuf payload) throws AdaptorException { |
437 | + try { | ||
438 | + TransportApiProtos.GatewayClaimMsg claimMsgProto = TransportApiProtos.GatewayClaimMsg.parseFrom(getBytes(payload)); | ||
439 | + List<TransportApiProtos.ClaimDeviceMsg> claimMsgList = claimMsgProto.getMsgList(); | ||
440 | + if (!CollectionUtils.isEmpty(claimMsgList)) { | ||
441 | + claimMsgList.forEach(claimDeviceMsg -> { | ||
442 | + String deviceName = checkDeviceName(claimDeviceMsg.getDeviceName()); | ||
443 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
444 | + new FutureCallback<GatewayDeviceSessionCtx>() { | ||
445 | + @Override | ||
446 | + public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
447 | + TransportApiProtos.ClaimDevice claimRequest = claimDeviceMsg.getClaimRequest(); | ||
448 | + if (claimRequest == null) { | ||
449 | + throw new IllegalArgumentException("Claim request for device: " + deviceName + " is null!"); | ||
450 | + } | ||
451 | + try { | ||
452 | + DeviceId deviceId = deviceCtx.getDeviceId(); | ||
453 | + TransportProtos.ClaimDeviceMsg claimDeviceMsg = ProtoConverter.convertToClaimDeviceProto(deviceId, claimRequest.toByteArray()); | ||
454 | + processClaimDeviceMsg(deviceCtx, claimDeviceMsg, deviceName, msgId); | ||
455 | + } catch (Throwable e) { | ||
456 | + log.warn("[{}][{}] Failed to convert claim message: {}", gateway.getDeviceId(), deviceName, claimRequest, e); | ||
457 | + } | ||
458 | + } | ||
459 | + | ||
460 | + @Override | ||
461 | + public void onFailure(Throwable t) { | ||
462 | + log.debug("[{}] Failed to process device claiming command: {}", sessionId, deviceName, t); | ||
463 | + } | ||
464 | + }, context.getExecutor()); | ||
465 | + }); | ||
466 | + } else { | ||
467 | + log.debug("[{}] Devices claim messages is empty for: [{}]", sessionId, gateway.getDeviceId()); | ||
468 | + throw new IllegalArgumentException("[" + sessionId + "] Devices claim messages is empty for [" + gateway.getDeviceId() + "]"); | ||
469 | + } | ||
470 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
471 | + throw new AdaptorException(e); | ||
472 | + } | ||
473 | + } | ||
474 | + | ||
475 | + private void processClaimDeviceMsg(GatewayDeviceSessionCtx deviceCtx, TransportProtos.ClaimDeviceMsg claimDeviceMsg, String deviceName, int msgId) { | ||
476 | + transportService.process(deviceCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(channel, deviceName, msgId, claimDeviceMsg)); | ||
477 | + } | ||
478 | + | ||
479 | + private void onDeviceAttributesJson(int msgId, ByteBuf payload) throws AdaptorException { | ||
480 | + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
275 | if (json.isJsonObject()) { | 481 | if (json.isJsonObject()) { |
276 | JsonObject jsonObj = json.getAsJsonObject(); | 482 | JsonObject jsonObj = json.getAsJsonObject(); |
277 | for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { | 483 | for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { |
@@ -284,7 +490,7 @@ public class GatewaySessionHandler { | @@ -284,7 +490,7 @@ public class GatewaySessionHandler { | ||
284 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | 490 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); |
285 | } | 491 | } |
286 | TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject()); | 492 | TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject()); |
287 | - transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId, postAttributeMsg)); | 493 | + processPostAttributesMsg(deviceCtx, postAttributeMsg, deviceName, msgId); |
288 | } | 494 | } |
289 | 495 | ||
290 | @Override | 496 | @Override |
@@ -298,34 +504,49 @@ public class GatewaySessionHandler { | @@ -298,34 +504,49 @@ public class GatewaySessionHandler { | ||
298 | } | 504 | } |
299 | } | 505 | } |
300 | 506 | ||
301 | - public void onDeviceRpcResponse(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
302 | - JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | ||
303 | - int msgId = mqttMsg.variableHeader().packetId(); | ||
304 | - if (json.isJsonObject()) { | ||
305 | - JsonObject jsonObj = json.getAsJsonObject(); | ||
306 | - String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); | ||
307 | - Futures.addCallback(checkDeviceConnected(deviceName), | ||
308 | - new FutureCallback<GatewayDeviceSessionCtx>() { | ||
309 | - @Override | ||
310 | - public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
311 | - Integer requestId = jsonObj.get("id").getAsInt(); | ||
312 | - String data = jsonObj.get("data").toString(); | ||
313 | - TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
314 | - .setRequestId(requestId).setPayload(data).build(); | ||
315 | - transportService.process(deviceCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(channel, deviceName, msgId, rpcResponseMsg)); | ||
316 | - } | 507 | + private void onDeviceAttributesProto(int msgId, ByteBuf payload) throws AdaptorException { |
508 | + try { | ||
509 | + TransportApiProtos.GatewayAttributesMsg attributesMsgProto = TransportApiProtos.GatewayAttributesMsg.parseFrom(getBytes(payload)); | ||
510 | + List<TransportApiProtos.AttributesMsg> attributesMsgList = attributesMsgProto.getMsgList(); | ||
511 | + if (!CollectionUtils.isEmpty(attributesMsgList)) { | ||
512 | + attributesMsgList.forEach(attributesMsg -> { | ||
513 | + String deviceName = checkDeviceName(attributesMsg.getDeviceName()); | ||
514 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
515 | + new FutureCallback<GatewayDeviceSessionCtx>() { | ||
516 | + @Override | ||
517 | + public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
518 | + TransportProtos.PostAttributeMsg kvListProto = attributesMsg.getMsg(); | ||
519 | + if (kvListProto == null) { | ||
520 | + throw new IllegalArgumentException("Attributes List for device: " + deviceName + " is empty!"); | ||
521 | + } | ||
522 | + try { | ||
523 | + TransportProtos.PostAttributeMsg postAttributeMsg = ProtoConverter.validatePostAttributeMsg(kvListProto.toByteArray()); | ||
524 | + processPostAttributesMsg(deviceCtx, postAttributeMsg, deviceName, msgId); | ||
525 | + } catch (Throwable e) { | ||
526 | + log.warn("[{}][{}] Failed to process device attributes command: {}", gateway.getDeviceId(), deviceName, kvListProto, e); | ||
527 | + } | ||
528 | + } | ||
317 | 529 | ||
318 | - @Override | ||
319 | - public void onFailure(Throwable t) { | ||
320 | - log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t); | ||
321 | - } | ||
322 | - }, context.getExecutor()); | ||
323 | - } else { | ||
324 | - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | 530 | + @Override |
531 | + public void onFailure(Throwable t) { | ||
532 | + log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t); | ||
533 | + } | ||
534 | + }, context.getExecutor()); | ||
535 | + }); | ||
536 | + } else { | ||
537 | + log.debug("[{}] Devices attributes keys list is empty for: [{}]", sessionId, gateway.getDeviceId()); | ||
538 | + throw new IllegalArgumentException("[" + sessionId + "] Devices attributes keys list is empty for [" + gateway.getDeviceId() + "]"); | ||
539 | + } | ||
540 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
541 | + throw new AdaptorException(e); | ||
325 | } | 542 | } |
326 | } | 543 | } |
327 | 544 | ||
328 | - public void onDeviceAttributesRequest(MqttPublishMessage msg) throws AdaptorException { | 545 | + private void processPostAttributesMsg(GatewayDeviceSessionCtx deviceCtx, TransportProtos.PostAttributeMsg postAttributeMsg, String deviceName, int msgId) { |
546 | + transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId, postAttributeMsg)); | ||
547 | + } | ||
548 | + | ||
549 | + private void onDeviceAttributesRequestJson(MqttPublishMessage msg) throws AdaptorException { | ||
329 | JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, msg.payload()); | 550 | JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, msg.payload()); |
330 | if (json.isJsonObject()) { | 551 | if (json.isJsonObject()) { |
331 | JsonObject jsonObj = json.getAsJsonObject(); | 552 | JsonObject jsonObj = json.getAsJsonObject(); |
@@ -342,27 +563,47 @@ public class GatewaySessionHandler { | @@ -342,27 +563,47 @@ public class GatewaySessionHandler { | ||
342 | keys.add(keyObj.getAsString()); | 563 | keys.add(keyObj.getAsString()); |
343 | } | 564 | } |
344 | } | 565 | } |
345 | - TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); | ||
346 | - result.setRequestId(requestId); | 566 | + TransportProtos.GetAttributeRequestMsg requestMsg = toGetAttributeRequestMsg(requestId, clientScope, keys); |
567 | + processGetAttributeRequestMessage(msg, deviceName, requestMsg); | ||
568 | + } else { | ||
569 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
570 | + } | ||
571 | + } | ||
347 | 572 | ||
348 | - if (clientScope) { | ||
349 | - result.addAllClientAttributeNames(keys); | ||
350 | - } else { | ||
351 | - result.addAllSharedAttributeNames(keys); | ||
352 | - } | ||
353 | - TransportProtos.GetAttributeRequestMsg requestMsg = result.build(); | ||
354 | - int msgId = msg.variableHeader().packetId(); | 573 | + private void onDeviceAttributesRequestProto(MqttPublishMessage mqttMsg) throws AdaptorException { |
574 | + try { | ||
575 | + TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = TransportApiProtos.GatewayAttributesRequestMsg.parseFrom(getBytes(mqttMsg.payload())); | ||
576 | + String deviceName = checkDeviceName(gatewayAttributesRequestMsg.getDeviceName()); | ||
577 | + int requestId = gatewayAttributesRequestMsg.getId(); | ||
578 | + boolean clientScope = gatewayAttributesRequestMsg.getClient(); | ||
579 | + ProtocolStringList keysList = gatewayAttributesRequestMsg.getKeysList(); | ||
580 | + Set<String> keys = new HashSet<>(keysList); | ||
581 | + TransportProtos.GetAttributeRequestMsg requestMsg = toGetAttributeRequestMsg(requestId, clientScope, keys); | ||
582 | + processGetAttributeRequestMessage(mqttMsg, deviceName, requestMsg); | ||
583 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
584 | + throw new AdaptorException(e); | ||
585 | + } | ||
586 | + } | ||
587 | + | ||
588 | + private void onDeviceRpcResponseJson(int msgId, ByteBuf payload) throws AdaptorException { | ||
589 | + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, payload); | ||
590 | + if (json.isJsonObject()) { | ||
591 | + JsonObject jsonObj = json.getAsJsonObject(); | ||
592 | + String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); | ||
355 | Futures.addCallback(checkDeviceConnected(deviceName), | 593 | Futures.addCallback(checkDeviceConnected(deviceName), |
356 | new FutureCallback<GatewayDeviceSessionCtx>() { | 594 | new FutureCallback<GatewayDeviceSessionCtx>() { |
357 | @Override | 595 | @Override |
358 | public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | 596 | public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { |
359 | - transportService.process(deviceCtx.getSessionInfo(), requestMsg, getPubAckCallback(channel, deviceName, msgId, requestMsg)); | 597 | + Integer requestId = jsonObj.get("id").getAsInt(); |
598 | + String data = jsonObj.get("data").toString(); | ||
599 | + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
600 | + .setRequestId(requestId).setPayload(data).build(); | ||
601 | + processRpcResponseMsg(deviceCtx, rpcResponseMsg, deviceName, msgId); | ||
360 | } | 602 | } |
361 | 603 | ||
362 | @Override | 604 | @Override |
363 | public void onFailure(Throwable t) { | 605 | public void onFailure(Throwable t) { |
364 | - ack(msg); | ||
365 | - log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t); | 606 | + log.debug("[{}] Failed to process device Rpc response command: {}", sessionId, deviceName, t); |
366 | } | 607 | } |
367 | }, context.getExecutor()); | 608 | }, context.getExecutor()); |
368 | } else { | 609 | } else { |
@@ -370,6 +611,64 @@ public class GatewaySessionHandler { | @@ -370,6 +611,64 @@ public class GatewaySessionHandler { | ||
370 | } | 611 | } |
371 | } | 612 | } |
372 | 613 | ||
614 | + private void onDeviceRpcResponseProto(int msgId, ByteBuf payload) throws AdaptorException { | ||
615 | + try { | ||
616 | + TransportApiProtos.GatewayRpcResponseMsg gatewayRpcResponseMsg = TransportApiProtos.GatewayRpcResponseMsg.parseFrom(getBytes(payload)); | ||
617 | + String deviceName = checkDeviceName(gatewayRpcResponseMsg.getDeviceName()); | ||
618 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
619 | + new FutureCallback<GatewayDeviceSessionCtx>() { | ||
620 | + @Override | ||
621 | + public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
622 | + Integer requestId = gatewayRpcResponseMsg.getId(); | ||
623 | + String data = gatewayRpcResponseMsg.getData(); | ||
624 | + TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | ||
625 | + .setRequestId(requestId).setPayload(data).build(); | ||
626 | + processRpcResponseMsg(deviceCtx, rpcResponseMsg, deviceName, msgId); | ||
627 | + } | ||
628 | + | ||
629 | + @Override | ||
630 | + public void onFailure(Throwable t) { | ||
631 | + log.debug("[{}] Failed to process device Rpc response command: {}", sessionId, deviceName, t); | ||
632 | + } | ||
633 | + }, context.getExecutor()); | ||
634 | + } catch (RuntimeException | InvalidProtocolBufferException e) { | ||
635 | + throw new AdaptorException(e); | ||
636 | + } | ||
637 | + } | ||
638 | + | ||
639 | + private void processRpcResponseMsg(GatewayDeviceSessionCtx deviceCtx, TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg, String deviceName, int msgId) { | ||
640 | + transportService.process(deviceCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(channel, deviceName, msgId, rpcResponseMsg)); | ||
641 | + } | ||
642 | + | ||
643 | + private void processGetAttributeRequestMessage(MqttPublishMessage mqttMsg, String deviceName, TransportProtos.GetAttributeRequestMsg requestMsg) { | ||
644 | + int msgId = getMsgId(mqttMsg); | ||
645 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
646 | + new FutureCallback<GatewayDeviceSessionCtx>() { | ||
647 | + @Override | ||
648 | + public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
649 | + transportService.process(deviceCtx.getSessionInfo(), requestMsg, getPubAckCallback(channel, deviceName, msgId, requestMsg)); | ||
650 | + } | ||
651 | + | ||
652 | + @Override | ||
653 | + public void onFailure(Throwable t) { | ||
654 | + ack(mqttMsg); | ||
655 | + log.debug("[{}] Failed to process device attributes request command: {}", sessionId, deviceName, t); | ||
656 | + } | ||
657 | + }, context.getExecutor()); | ||
658 | + } | ||
659 | + | ||
660 | + private TransportProtos.GetAttributeRequestMsg toGetAttributeRequestMsg(int requestId, boolean clientScope, Set<String> keys) { | ||
661 | + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); | ||
662 | + result.setRequestId(requestId); | ||
663 | + | ||
664 | + if (clientScope) { | ||
665 | + result.addAllClientAttributeNames(keys); | ||
666 | + } else { | ||
667 | + result.addAllSharedAttributeNames(keys); | ||
668 | + } | ||
669 | + return result.build(); | ||
670 | + } | ||
671 | + | ||
373 | private ListenableFuture<GatewayDeviceSessionCtx> checkDeviceConnected(String deviceName) { | 672 | private ListenableFuture<GatewayDeviceSessionCtx> checkDeviceConnected(String deviceName) { |
374 | GatewayDeviceSessionCtx ctx = devices.get(deviceName); | 673 | GatewayDeviceSessionCtx ctx = devices.get(deviceName); |
375 | if (ctx == null) { | 674 | if (ctx == null) { |
@@ -388,11 +687,11 @@ public class GatewaySessionHandler { | @@ -388,11 +687,11 @@ public class GatewaySessionHandler { | ||
388 | } | 687 | } |
389 | } | 688 | } |
390 | 689 | ||
391 | - private String getDeviceName(JsonElement json) throws AdaptorException { | 690 | + private String getDeviceName(JsonElement json) { |
392 | return json.getAsJsonObject().get(DEVICE_PROPERTY).getAsString(); | 691 | return json.getAsJsonObject().get(DEVICE_PROPERTY).getAsString(); |
393 | } | 692 | } |
394 | 693 | ||
395 | - private String getDeviceType(JsonElement json) throws AdaptorException { | 694 | + private String getDeviceType(JsonElement json) { |
396 | JsonElement type = json.getAsJsonObject().get("type"); | 695 | JsonElement type = json.getAsJsonObject().get("type"); |
397 | return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString(); | 696 | return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString(); |
398 | } | 697 | } |
@@ -401,18 +700,15 @@ public class GatewaySessionHandler { | @@ -401,18 +700,15 @@ public class GatewaySessionHandler { | ||
401 | return JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | 700 | return JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); |
402 | } | 701 | } |
403 | 702 | ||
404 | - private void ack(MqttPublishMessage msg) { | ||
405 | - if (msg.variableHeader().packetId() > 0) { | ||
406 | - writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().packetId())); | ||
407 | - } | ||
408 | - } | ||
409 | - | ||
410 | - void writeAndFlush(MqttMessage mqttMessage) { | ||
411 | - channel.writeAndFlush(mqttMessage); | 703 | + private byte[] getBytes(ByteBuf payload) { |
704 | + return ProtoMqttAdaptor.toBytes(payload); | ||
412 | } | 705 | } |
413 | 706 | ||
414 | - public String getNodeId() { | ||
415 | - return context.getNodeId(); | 707 | + private void ack(MqttPublishMessage msg) { |
708 | + int msgId = getMsgId(msg); | ||
709 | + if (msgId > 0) { | ||
710 | + writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msgId)); | ||
711 | + } | ||
416 | } | 712 | } |
417 | 713 | ||
418 | private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) { | 714 | private void deregisterSession(String deviceName, GatewayDeviceSessionCtx deviceSessionCtx) { |
@@ -433,25 +729,9 @@ public class GatewaySessionHandler { | @@ -433,25 +729,9 @@ public class GatewaySessionHandler { | ||
433 | 729 | ||
434 | @Override | 730 | @Override |
435 | public void onError(Throwable e) { | 731 | public void onError(Throwable e) { |
436 | - log.trace("[{}] Failed to publish msg: {}", sessionId, deviceName, msg, e); | 732 | + log.trace("[{}] Failed to publish msg: {} for device: {}", sessionId, msg, deviceName, e); |
437 | ctx.close(); | 733 | ctx.close(); |
438 | } | 734 | } |
439 | }; | 735 | }; |
440 | } | 736 | } |
441 | - | ||
442 | - public MqttTransportContext getContext() { | ||
443 | - return context; | ||
444 | - } | ||
445 | - | ||
446 | - MqttTransportAdaptor getAdaptor() { | ||
447 | - return context.getAdaptor(); | ||
448 | - } | ||
449 | - | ||
450 | - int nextMsgId() { | ||
451 | - return deviceSessionCtx.nextMsgId(); | ||
452 | - } | ||
453 | - | ||
454 | - public UUID getSessionId() { | ||
455 | - return sessionId; | ||
456 | - } | ||
457 | } | 737 | } |
@@ -16,7 +16,14 @@ | @@ -16,7 +16,14 @@ | ||
16 | package org.thingsboard.server.transport.mqtt.session; | 16 | package org.thingsboard.server.transport.mqtt.session; |
17 | 17 | ||
18 | import io.netty.handler.codec.mqtt.MqttQoS; | 18 | import io.netty.handler.codec.mqtt.MqttQoS; |
19 | +import org.thingsboard.server.common.data.DeviceProfile; | ||
20 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
21 | +import org.thingsboard.server.common.data.TransportPayloadType; | ||
22 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | ||
23 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | ||
19 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; | 24 | import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; |
25 | +import org.thingsboard.server.transport.mqtt.util.MqttTopicFilter; | ||
26 | +import org.thingsboard.server.transport.mqtt.util.MqttTopicFilterFactory; | ||
20 | 27 | ||
21 | import java.util.List; | 28 | import java.util.List; |
22 | import java.util.Map; | 29 | import java.util.Map; |
@@ -52,5 +59,4 @@ public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionCo | @@ -52,5 +59,4 @@ public abstract class MqttDeviceAwareSessionContext extends DeviceAwareSessionCo | ||
52 | return MqttQoS.AT_LEAST_ONCE; | 59 | return MqttQoS.AT_LEAST_ONCE; |
53 | } | 60 | } |
54 | } | 61 | } |
55 | - | ||
56 | } | 62 | } |
@@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; | @@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics; | ||
20 | 20 | ||
21 | import java.util.concurrent.ConcurrentHashMap; | 21 | import java.util.concurrent.ConcurrentHashMap; |
22 | import java.util.concurrent.ConcurrentMap; | 22 | import java.util.concurrent.ConcurrentMap; |
23 | -import java.util.regex.Pattern; | ||
24 | 23 | ||
25 | @Slf4j | 24 | @Slf4j |
26 | public class MqttTopicFilterFactory { | 25 | public class MqttTopicFilterFactory { |
@@ -107,6 +107,19 @@ | @@ -107,6 +107,19 @@ | ||
107 | <groupId>org.apache.commons</groupId> | 107 | <groupId>org.apache.commons</groupId> |
108 | <artifactId>commons-lang3</artifactId> | 108 | <artifactId>commons-lang3</artifactId> |
109 | </dependency> | 109 | </dependency> |
110 | + <dependency> | ||
111 | + <groupId>com.google.protobuf</groupId> | ||
112 | + <artifactId>protobuf-java</artifactId> | ||
113 | + </dependency> | ||
110 | </dependencies> | 114 | </dependencies> |
111 | 115 | ||
116 | + <build> | ||
117 | + <plugins> | ||
118 | + <plugin> | ||
119 | + <groupId>org.xolstice.maven.plugins</groupId> | ||
120 | + <artifactId>protobuf-maven-plugin</artifactId> | ||
121 | + </plugin> | ||
122 | + </plugins> | ||
123 | + </build> | ||
124 | + | ||
112 | </project> | 125 | </project> |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.transport.adaptor; | ||
17 | + | ||
18 | +import com.google.gson.JsonParser; | ||
19 | +import com.google.protobuf.InvalidProtocolBufferException; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.springframework.util.CollectionUtils; | ||
22 | +import org.springframework.util.StringUtils; | ||
23 | +import org.thingsboard.server.common.data.DataConstants; | ||
24 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
25 | +import org.thingsboard.server.gen.transport.TransportApiProtos; | ||
26 | +import org.thingsboard.server.gen.transport.TransportProtos; | ||
27 | + | ||
28 | +import java.util.ArrayList; | ||
29 | +import java.util.Arrays; | ||
30 | +import java.util.List; | ||
31 | + | ||
32 | +@Slf4j | ||
33 | +public class ProtoConverter { | ||
34 | + | ||
35 | + public static final JsonParser JSON_PARSER = new JsonParser(); | ||
36 | + | ||
37 | + public static TransportProtos.PostTelemetryMsg convertToTelemetryProto(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { | ||
38 | + TransportProtos.TsKvListProto protoPayload = TransportProtos.TsKvListProto.parseFrom(payload); | ||
39 | + TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); | ||
40 | + TransportProtos.TsKvListProto tsKvListProto = validateTsKvListProto(protoPayload); | ||
41 | + postTelemetryMsgBuilder.addTsKvList(tsKvListProto); | ||
42 | + return postTelemetryMsgBuilder.build(); | ||
43 | + } | ||
44 | + | ||
45 | + public static TransportProtos.PostTelemetryMsg validatePostTelemetryMsg(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { | ||
46 | + TransportProtos.PostTelemetryMsg msg = TransportProtos.PostTelemetryMsg.parseFrom(payload); | ||
47 | + TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); | ||
48 | + List<TransportProtos.TsKvListProto> tsKvListProtoList = msg.getTsKvListList(); | ||
49 | + if (!CollectionUtils.isEmpty(tsKvListProtoList)) { | ||
50 | + List<TransportProtos.TsKvListProto> tsKvListProtos = new ArrayList<>(); | ||
51 | + tsKvListProtoList.forEach(tsKvListProto -> { | ||
52 | + TransportProtos.TsKvListProto transportTsKvListProto = validateTsKvListProto(tsKvListProto); | ||
53 | + tsKvListProtos.add(transportTsKvListProto); | ||
54 | + }); | ||
55 | + postTelemetryMsgBuilder.addAllTsKvList(tsKvListProtos); | ||
56 | + return postTelemetryMsgBuilder.build(); | ||
57 | + } else { | ||
58 | + throw new IllegalArgumentException("TsKv list is empty!"); | ||
59 | + } | ||
60 | + } | ||
61 | + | ||
62 | + public static TransportProtos.PostAttributeMsg validatePostAttributeMsg(byte[] bytes) throws IllegalArgumentException, InvalidProtocolBufferException { | ||
63 | + TransportProtos.PostAttributeMsg proto = TransportProtos.PostAttributeMsg.parseFrom(bytes); | ||
64 | + List<TransportProtos.KeyValueProto> kvList = proto.getKvList(); | ||
65 | + if (!CollectionUtils.isEmpty(kvList)) { | ||
66 | + List<TransportProtos.KeyValueProto> keyValueProtos = validateKeyValueProtos(kvList); | ||
67 | + TransportProtos.PostAttributeMsg.Builder result = TransportProtos.PostAttributeMsg.newBuilder(); | ||
68 | + result.addAllKv(keyValueProtos); | ||
69 | + return result.build(); | ||
70 | + } else { | ||
71 | + throw new IllegalArgumentException("KeyValue list is empty!"); | ||
72 | + } | ||
73 | + } | ||
74 | + | ||
75 | + public static TransportProtos.ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, byte[] bytes) throws InvalidProtocolBufferException { | ||
76 | + TransportApiProtos.ClaimDevice proto = TransportApiProtos.ClaimDevice.parseFrom(bytes); | ||
77 | + String secretKey = proto.getSecretKey() != null ? proto.getSecretKey() : DataConstants.DEFAULT_SECRET_KEY; | ||
78 | + long durationMs = proto.getDurationMs(); | ||
79 | + return buildClaimDeviceMsg(deviceId, secretKey, durationMs); | ||
80 | + } | ||
81 | + | ||
82 | + public static TransportProtos.GetAttributeRequestMsg convertToGetAttributeRequestMessage(byte[] bytes, int requestId) throws InvalidProtocolBufferException, RuntimeException { | ||
83 | + TransportApiProtos.AttributesRequest proto = TransportApiProtos.AttributesRequest.parseFrom(bytes); | ||
84 | + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); | ||
85 | + result.setRequestId(requestId); | ||
86 | + String clientKeys = proto.getClientKeys(); | ||
87 | + String sharedKeys = proto.getSharedKeys(); | ||
88 | + if (!StringUtils.isEmpty(clientKeys)) { | ||
89 | + List<String> clientKeysList = Arrays.asList(clientKeys.split(",")); | ||
90 | + result.addAllClientAttributeNames(clientKeysList); | ||
91 | + } | ||
92 | + if (!StringUtils.isEmpty(sharedKeys)) { | ||
93 | + List<String> sharedKeysList = Arrays.asList(sharedKeys.split(",")); | ||
94 | + result.addAllSharedAttributeNames(sharedKeysList); | ||
95 | + } | ||
96 | + return result.build(); | ||
97 | + } | ||
98 | + | ||
99 | + public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(byte[] bytes, int requestId) throws InvalidProtocolBufferException { | ||
100 | + TransportApiProtos.RpcRequest proto = TransportApiProtos.RpcRequest.parseFrom(bytes); | ||
101 | + String method = proto.getMethod(); | ||
102 | + String params = proto.getParams(); | ||
103 | + return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(method).setParams(params).build(); | ||
104 | + } | ||
105 | + | ||
106 | + private static TransportProtos.ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { | ||
107 | + TransportProtos.ClaimDeviceMsg.Builder result = TransportProtos.ClaimDeviceMsg.newBuilder(); | ||
108 | + return result | ||
109 | + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) | ||
110 | + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) | ||
111 | + .setSecretKey(secretKey) | ||
112 | + .setDurationMs(durationMs) | ||
113 | + .build(); | ||
114 | + } | ||
115 | + | ||
116 | + private static TransportProtos.TsKvListProto validateTsKvListProto(TransportProtos.TsKvListProto tsKvListProto) { | ||
117 | + TransportProtos.TsKvListProto.Builder tsKvListBuilder = TransportProtos.TsKvListProto.newBuilder(); | ||
118 | + long ts = tsKvListProto.getTs(); | ||
119 | + if (ts == 0) { | ||
120 | + ts = System.currentTimeMillis(); | ||
121 | + } | ||
122 | + tsKvListBuilder.setTs(ts); | ||
123 | + List<TransportProtos.KeyValueProto> kvList = tsKvListProto.getKvList(); | ||
124 | + if (!CollectionUtils.isEmpty(kvList)) { | ||
125 | + List<TransportProtos.KeyValueProto> keyValueListProtos = validateKeyValueProtos(kvList); | ||
126 | + tsKvListBuilder.addAllKv(keyValueListProtos); | ||
127 | + return tsKvListBuilder.build(); | ||
128 | + } else { | ||
129 | + throw new IllegalArgumentException("KeyValue list is empty!"); | ||
130 | + } | ||
131 | + } | ||
132 | + | ||
133 | + | ||
134 | + private static List<TransportProtos.KeyValueProto> validateKeyValueProtos(List<TransportProtos.KeyValueProto> kvList) { | ||
135 | + kvList.forEach(keyValueProto -> { | ||
136 | + String key = keyValueProto.getKey(); | ||
137 | + if (StringUtils.isEmpty(key)) { | ||
138 | + throw new IllegalArgumentException("Invalid key value: " + key + "!"); | ||
139 | + } | ||
140 | + TransportProtos.KeyValueType type = keyValueProto.getType(); | ||
141 | + switch (type) { | ||
142 | + case BOOLEAN_V: | ||
143 | + case LONG_V: | ||
144 | + case DOUBLE_V: | ||
145 | + break; | ||
146 | + case STRING_V: | ||
147 | + if (StringUtils.isEmpty(keyValueProto.getStringV())) { | ||
148 | + throw new IllegalArgumentException("Value is empty for key: " + key + "!"); | ||
149 | + } | ||
150 | + break; | ||
151 | + case JSON_V: | ||
152 | + try { | ||
153 | + JSON_PARSER.parse(keyValueProto.getJsonV()); | ||
154 | + } catch (Exception e) { | ||
155 | + throw new IllegalArgumentException("Can't parse value: " + keyValueProto.getJsonV() + " for key: " + key + "!"); | ||
156 | + } | ||
157 | + break; | ||
158 | + case UNRECOGNIZED: | ||
159 | + throw new IllegalArgumentException("Unsupported keyValueType: " + type + "!"); | ||
160 | + } | ||
161 | + }); | ||
162 | + return kvList; | ||
163 | + } | ||
164 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +syntax = "proto3"; | ||
17 | +package transportapi; | ||
18 | + | ||
19 | +option java_package = "org.thingsboard.server.gen.transport"; | ||
20 | +option java_outer_classname = "TransportApiProtos"; | ||
21 | + | ||
22 | +import "queue.proto"; | ||
23 | + | ||
24 | +message ClaimDevice { | ||
25 | + string secretKey = 1; | ||
26 | + int64 durationMs = 2; | ||
27 | +} | ||
28 | + | ||
29 | +message AttributesRequest { | ||
30 | + string clientKeys = 1; | ||
31 | + string sharedKeys = 2; | ||
32 | +} | ||
33 | + | ||
34 | +message RpcRequest { | ||
35 | + string method = 1; | ||
36 | + string params = 2; | ||
37 | +} | ||
38 | + | ||
39 | +message DisconnectMsg { | ||
40 | + string deviceName = 1; | ||
41 | +} | ||
42 | + | ||
43 | +message ConnectMsg { | ||
44 | + string deviceName = 1; | ||
45 | + string deviceType = 2; | ||
46 | +} | ||
47 | + | ||
48 | +message TelemetryMsg { | ||
49 | + string deviceName = 1; | ||
50 | + transport.PostTelemetryMsg msg = 3; | ||
51 | +} | ||
52 | + | ||
53 | +message AttributesMsg { | ||
54 | + string deviceName = 1; | ||
55 | + transport.PostAttributeMsg msg = 2; | ||
56 | +} | ||
57 | + | ||
58 | +message ClaimDeviceMsg { | ||
59 | + string deviceName = 1; | ||
60 | + ClaimDevice claimRequest = 2; | ||
61 | +} | ||
62 | + | ||
63 | +message GatewayTelemetryMsg { | ||
64 | + repeated TelemetryMsg msg = 1; | ||
65 | +} | ||
66 | + | ||
67 | +message GatewayClaimMsg { | ||
68 | + repeated ClaimDeviceMsg msg = 1; | ||
69 | +} | ||
70 | + | ||
71 | +message GatewayAttributesMsg { | ||
72 | + repeated AttributesMsg msg = 1; | ||
73 | +} | ||
74 | + | ||
75 | +message GatewayRpcResponseMsg { | ||
76 | + string deviceName = 1; | ||
77 | + int32 id = 2; | ||
78 | + string data = 3; | ||
79 | +} | ||
80 | + | ||
81 | +message GatewayAttributeResponseMsg { | ||
82 | + string deviceName = 1; | ||
83 | + transport.GetAttributeResponseMsg responseMsg = 2; | ||
84 | +} | ||
85 | + | ||
86 | +message GatewayAttributeUpdateNotificationMsg { | ||
87 | + string deviceName = 1; | ||
88 | + transport.AttributeUpdateNotificationMsg notificationMsg = 2; | ||
89 | +} | ||
90 | + | ||
91 | +message GatewayDeviceRpcRequestMsg { | ||
92 | + string deviceName = 1; | ||
93 | + transport.ToDeviceRpcRequestMsg rpcRequestMsg = 2; | ||
94 | +} | ||
95 | + | ||
96 | +message GatewayAttributesRequestMsg { | ||
97 | + int32 id = 1; | ||
98 | + string deviceName = 2; | ||
99 | + bool client = 3; | ||
100 | + repeated string keys = 4; | ||
101 | +} |
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
@@ -177,6 +177,9 @@ public class MqttGatewayClientTest extends AbstractContainerTest { | @@ -177,6 +177,9 @@ public class MqttGatewayClientTest extends AbstractContainerTest { | ||
177 | String sharedAttributeValue = RandomStringUtils.randomAlphanumeric(8); | 177 | String sharedAttributeValue = RandomStringUtils.randomAlphanumeric(8); |
178 | sharedAttributes.addProperty("sharedAttr", sharedAttributeValue); | 178 | sharedAttributes.addProperty("sharedAttr", sharedAttributeValue); |
179 | 179 | ||
180 | + // Subscribe for attribute update event | ||
181 | + mqttClient.on("v1/gateway/attributes", listener, MqttQoS.AT_LEAST_ONCE).get(); | ||
182 | + | ||
180 | ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() | 183 | ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() |
181 | .postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE", | 184 | .postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE", |
182 | mapper.readTree(sharedAttributes.toString()), ResponseEntity.class, | 185 | mapper.readTree(sharedAttributes.toString()), ResponseEntity.class, |
@@ -45,7 +45,6 @@ transport: | @@ -45,7 +45,6 @@ transport: | ||
45 | mqtt: | 45 | mqtt: |
46 | bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" | 46 | bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" |
47 | bind_port: "${MQTT_BIND_PORT:1883}" | 47 | bind_port: "${MQTT_BIND_PORT:1883}" |
48 | - adaptor: "${MQTT_ADAPTOR_NAME:JsonMqttAdaptor}" | ||
49 | timeout: "${MQTT_TIMEOUT:10000}" | 48 | timeout: "${MQTT_TIMEOUT:10000}" |
50 | netty: | 49 | netty: |
51 | leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" | 50 | leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" |
@@ -20,6 +20,17 @@ | @@ -20,6 +20,17 @@ | ||
20 | <fieldset class="fields-group"> | 20 | <fieldset class="fields-group"> |
21 | <legend class="group-title" translate>device-profile.mqtt-device-topic-filters</legend> | 21 | <legend class="group-title" translate>device-profile.mqtt-device-topic-filters</legend> |
22 | <div fxLayoutGap="8px" fxLayout="column"> | 22 | <div fxLayoutGap="8px" fxLayout="column"> |
23 | + <mat-form-field class="mat-block"> | ||
24 | + <mat-label translate>device-profile.mqtt-device-payload-type</mat-label> | ||
25 | + <mat-select formControlName="transportPayloadType" required> | ||
26 | + <mat-option *ngFor="let type of mqttTransportPayloadTypes" [value]="type"> | ||
27 | + {{mqttTransportPayloadTypeTranslations.get(type) | translate}} | ||
28 | + </mat-option> | ||
29 | + </mat-select> | ||
30 | + <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('configuration.transportPayloadType').hasError('required')"> | ||
31 | + {{ 'device-profile.mqtt-payload-type-required' | translate }} | ||
32 | + </mat-error> | ||
33 | + </mat-form-field> | ||
23 | <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"> | 34 | <div fxLayout="row" fxLayoutGap="8px" fxLayout.xs="column"> |
24 | <mat-form-field fxFlex> | 35 | <mat-form-field fxFlex> |
25 | <mat-label translate>device-profile.telemetry-topic-filter</mat-label> | 36 | <mat-label translate>device-profile.telemetry-topic-filter</mat-label> |
@@ -28,9 +28,10 @@ import { Store } from '@ngrx/store'; | @@ -28,9 +28,10 @@ import { Store } from '@ngrx/store'; | ||
28 | import { AppState } from '@app/core/core.state'; | 28 | import { AppState } from '@app/core/core.state'; |
29 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; | 29 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
30 | import { | 30 | import { |
31 | + MqttTransportPayloadType, | ||
31 | DeviceProfileTransportConfiguration, | 32 | DeviceProfileTransportConfiguration, |
32 | DeviceTransportType, | 33 | DeviceTransportType, |
33 | - MqttDeviceProfileTransportConfiguration | 34 | + MqttDeviceProfileTransportConfiguration, mqttTransportPayloadTypeTranslationMap |
34 | } from '@shared/models/device.models'; | 35 | } from '@shared/models/device.models'; |
35 | import { isDefinedAndNotNull } from '@core/utils'; | 36 | import { isDefinedAndNotNull } from '@core/utils'; |
36 | 37 | ||
@@ -46,6 +47,11 @@ import { isDefinedAndNotNull } from '@core/utils'; | @@ -46,6 +47,11 @@ import { isDefinedAndNotNull } from '@core/utils'; | ||
46 | }) | 47 | }) |
47 | export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { | 48 | export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { |
48 | 49 | ||
50 | + mqttTransportPayloadTypes = Object.keys(MqttTransportPayloadType); | ||
51 | + | ||
52 | + mqttTransportPayloadTypeTranslations = mqttTransportPayloadTypeTranslationMap; | ||
53 | + | ||
54 | + | ||
49 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; | 55 | mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; |
50 | 56 | ||
51 | private requiredValue: boolean; | 57 | private requiredValue: boolean; |
@@ -79,7 +85,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | @@ -79,7 +85,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control | ||
79 | this.mqttDeviceProfileTransportConfigurationFormGroup = this.fb.group({ | 85 | this.mqttDeviceProfileTransportConfigurationFormGroup = this.fb.group({ |
80 | configuration: this.fb.group({ | 86 | configuration: this.fb.group({ |
81 | deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], | 87 | deviceAttributesTopic: [null, [Validators.required, this.validationMQTTTopic()]], |
82 | - deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]] | 88 | + deviceTelemetryTopic: [null, [Validators.required, this.validationMQTTTopic()]], |
89 | + transportPayloadType: [MqttTransportPayloadType.JSON, Validators.required] | ||
83 | }) | 90 | }) |
84 | }); | 91 | }); |
85 | this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { | 92 | this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { |
@@ -36,6 +36,11 @@ export enum DeviceTransportType { | @@ -36,6 +36,11 @@ export enum DeviceTransportType { | ||
36 | LWM2M = 'LWM2M' | 36 | LWM2M = 'LWM2M' |
37 | } | 37 | } |
38 | 38 | ||
39 | +export enum MqttTransportPayloadType { | ||
40 | + JSON = 'JSON', | ||
41 | + PROTOBUF = 'PROTOBUF' | ||
42 | +} | ||
43 | + | ||
39 | export interface DeviceConfigurationFormInfo { | 44 | export interface DeviceConfigurationFormInfo { |
40 | hasProfileConfiguration: boolean; | 45 | hasProfileConfiguration: boolean; |
41 | hasDeviceConfiguration: boolean; | 46 | hasDeviceConfiguration: boolean; |
@@ -67,6 +72,14 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st | @@ -67,6 +72,14 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st | ||
67 | ] | 72 | ] |
68 | ); | 73 | ); |
69 | 74 | ||
75 | +export const mqttTransportPayloadTypeTranslationMap = new Map<MqttTransportPayloadType, string>( | ||
76 | + [ | ||
77 | + [MqttTransportPayloadType.JSON, 'device-profile.mqtt-device-payload-type-json'], | ||
78 | + [MqttTransportPayloadType.PROTOBUF, 'device-profile.mqtt-device-payload-type-proto'] | ||
79 | + ] | ||
80 | +); | ||
81 | + | ||
82 | + | ||
70 | export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportType, DeviceConfigurationFormInfo>( | 83 | export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportType, DeviceConfigurationFormInfo>( |
71 | [ | 84 | [ |
72 | [ | 85 | [ |
@@ -162,7 +175,8 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT | @@ -162,7 +175,8 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT | ||
162 | case DeviceTransportType.MQTT: | 175 | case DeviceTransportType.MQTT: |
163 | const mqttTransportConfiguration: MqttDeviceProfileTransportConfiguration = { | 176 | const mqttTransportConfiguration: MqttDeviceProfileTransportConfiguration = { |
164 | deviceTelemetryTopic: 'v1/devices/me/telemetry', | 177 | deviceTelemetryTopic: 'v1/devices/me/telemetry', |
165 | - deviceAttributesTopic: 'v1/devices/me/attributes' | 178 | + deviceAttributesTopic: 'v1/devices/me/attributes', |
179 | + transportPayloadType: MqttTransportPayloadType.JSON | ||
166 | }; | 180 | }; |
167 | transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT}; | 181 | transportConfiguration = {...mqttTransportConfiguration, type: DeviceTransportType.MQTT}; |
168 | break; | 182 | break; |
@@ -798,6 +798,10 @@ | @@ -798,6 +798,10 @@ | ||
798 | "no-device-profiles-found": "No device profiles found.", | 798 | "no-device-profiles-found": "No device profiles found.", |
799 | "create-new-device-profile": "Create a new one!", | 799 | "create-new-device-profile": "Create a new one!", |
800 | "mqtt-device-topic-filters": "MQTT device topic filters", | 800 | "mqtt-device-topic-filters": "MQTT device topic filters", |
801 | + "mqtt-device-payload-type": "MQTT device payload", | ||
802 | + "mqtt-device-payload-type-json": "JSON", | ||
803 | + "mqtt-device-payload-type-proto": "Protobuf", | ||
804 | + "mqtt-payload-type-required": "Payload type is required.", | ||
801 | "support-level-wildcards": "Single <code>[+]</code> and multi-level <code>[#]</code> wildcards supported.", | 805 | "support-level-wildcards": "Single <code>[+]</code> and multi-level <code>[#]</code> wildcards supported.", |
802 | "telemetry-topic-filter": "Telemetry topic filter", | 806 | "telemetry-topic-filter": "Telemetry topic filter", |
803 | "telemetry-topic-filter-required": "Telemetry topic filter is required.", | 807 | "telemetry-topic-filter-required": "Telemetry topic filter is required.", |