Commit c526d13e453cc8acd07dcca96598abc06ae6e4d6

Authored by Volodymyr Babak
2 parents 4686b232 3f9f6efc

Merge remote-tracking branch 'upstream/develop/2.5.5' into feature/edge

Showing 100 changed files with 1302 additions and 450 deletions

Too many changes to show.

To preserve performance only 100 of 130 files are displayed.

@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>application</artifactId> 26 <artifactId>application</artifactId>
@@ -64,6 +64,7 @@ BEGIN @@ -64,6 +64,7 @@ BEGIN
64 AND tablename like 'ts_kv_' || '%' 64 AND tablename like 'ts_kv_' || '%'
65 AND tablename != 'ts_kv_latest' 65 AND tablename != 'ts_kv_latest'
66 AND tablename != 'ts_kv_dictionary' 66 AND tablename != 'ts_kv_dictionary'
  67 + AND tablename != 'ts_kv_indefinite'
67 LOOP 68 LOOP
68 IF partition != partition_by_max_ttl_date THEN 69 IF partition != partition_by_max_ttl_date THEN
69 IF partition_year IS NOT NULL THEN 70 IF partition_year IS NOT NULL THEN
@@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; @@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
28 import org.thingsboard.server.actors.ActorSystemContext; 28 import org.thingsboard.server.actors.ActorSystemContext;
29 import org.thingsboard.server.actors.TbActorCtx; 29 import org.thingsboard.server.actors.TbActorCtx;
30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
  31 +import org.thingsboard.server.common.data.DataConstants;
31 import org.thingsboard.server.common.data.Device; 32 import org.thingsboard.server.common.data.Device;
32 import org.thingsboard.server.common.data.id.DeviceId; 33 import org.thingsboard.server.common.data.id.DeviceId;
33 import org.thingsboard.server.common.data.id.TenantId; 34 import org.thingsboard.server.common.data.id.TenantId;
@@ -79,8 +80,6 @@ import java.util.UUID; @@ -79,8 +80,6 @@ import java.util.UUID;
79 import java.util.function.Consumer; 80 import java.util.function.Consumer;
80 import java.util.stream.Collectors; 81 import java.util.stream.Collectors;
81 82
82 -import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE;  
83 -import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;  
84 83
85 /** 84 /**
86 * @author Andrew Shvayka 85 * @author Andrew Shvayka
@@ -279,17 +278,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -279,17 +278,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
279 ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture; 278 ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture;
280 ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture; 279 ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture;
281 if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { 280 if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
282 - clientAttributesFuture = findAllAttributesByScope(CLIENT_SCOPE);  
283 - sharedAttributesFuture = findAllAttributesByScope(SHARED_SCOPE); 281 + clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE);
  282 + sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE);
284 } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { 283 } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
285 - clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), CLIENT_SCOPE);  
286 - sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), SHARED_SCOPE); 284 + clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
  285 + sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
287 } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { 286 } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
288 clientAttributesFuture = Futures.immediateFuture(Collections.emptyList()); 287 clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
289 - sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), SHARED_SCOPE); 288 + sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
290 } else { 289 } else {
291 sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList()); 290 sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
292 - clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), CLIENT_SCOPE); 291 + clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
293 } 292 }
294 return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)); 293 return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
295 } 294 }
@@ -316,7 +315,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -316,7 +315,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
316 AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder(); 315 AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
317 if (msg.isDeleted()) { 316 if (msg.isDeleted()) {
318 List<String> sharedKeys = msg.getDeletedKeys().stream() 317 List<String> sharedKeys = msg.getDeletedKeys().stream()
319 - .filter(key -> SHARED_SCOPE.equals(key.getScope())) 318 + .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
320 .map(AttributeKey::getAttributeKey) 319 .map(AttributeKey::getAttributeKey)
321 .collect(Collectors.toList()); 320 .collect(Collectors.toList());
322 if (!sharedKeys.isEmpty()) { 321 if (!sharedKeys.isEmpty()) {
@@ -324,7 +323,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -324,7 +323,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
324 hasNotificationData = true; 323 hasNotificationData = true;
325 } 324 }
326 } else { 325 } else {
327 - if (SHARED_SCOPE.equals(msg.getScope())) { 326 + if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
328 List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues()); 327 List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
329 if (attributes.size() > 0) { 328 if (attributes.size() > 0) {
330 List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto) 329 List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
@@ -334,7 +333,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -334,7 +333,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
334 hasNotificationData = true; 333 hasNotificationData = true;
335 } 334 }
336 } else { 335 } else {
337 - log.debug("[{}] No public server side attributes changed!", deviceId); 336 + log.debug("[{}] No public shared side attributes changed!", deviceId);
338 } 337 }
339 } 338 }
340 } 339 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.security.access.prepost.PreAuthorize; 20 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 21 import org.springframework.web.bind.annotation.PathVariable;
@@ -59,7 +60,11 @@ public class AdminController extends BaseController { @@ -59,7 +60,11 @@ public class AdminController extends BaseController {
59 public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException { 60 public AdminSettings getAdminSettings(@PathVariable("key") String key) throws ThingsboardException {
60 try { 61 try {
61 accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); 62 accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
62 - return checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key)); 63 + AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
  64 + if (adminSettings.getKey().equals("mail")) {
  65 + ((ObjectNode) adminSettings.getJsonValue()).put("password", "");
  66 + }
  67 + return adminSettings;
63 } catch (Exception e) { 68 } catch (Exception e) {
64 throw handleException(e); 69 throw handleException(e);
65 } 70 }
@@ -74,6 +79,7 @@ public class AdminController extends BaseController { @@ -74,6 +79,7 @@ public class AdminController extends BaseController {
74 adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings)); 79 adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings));
75 if (adminSettings.getKey().equals("mail")) { 80 if (adminSettings.getKey().equals("mail")) {
76 mailService.updateMailConfiguration(); 81 mailService.updateMailConfiguration();
  82 + ((ObjectNode) adminSettings.getJsonValue()).put("password", "");
77 } 83 }
78 return adminSettings; 84 return adminSettings;
79 } catch (Exception e) { 85 } catch (Exception e) {
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
18 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
19 import com.fasterxml.jackson.databind.node.ArrayNode; 20 import com.fasterxml.jackson.databind.node.ArrayNode;
20 import com.fasterxml.jackson.databind.node.ObjectNode; 21 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -624,6 +625,12 @@ public abstract class BaseController { @@ -624,6 +625,12 @@ public abstract class BaseController {
624 case ALARM_CLEAR: 625 case ALARM_CLEAR:
625 msgType = DataConstants.ALARM_CLEAR; 626 msgType = DataConstants.ALARM_CLEAR;
626 break; 627 break;
  628 + case ASSIGNED_FROM_TENANT:
  629 + msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
  630 + break;
  631 + case ASSIGNED_TO_TENANT:
  632 + msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
  633 + break;
627 case ASSIGNED_TO_EDGE: 634 case ASSIGNED_TO_EDGE:
628 msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; 635 msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE;
629 break; 636 break;
@@ -649,7 +656,17 @@ public abstract class BaseController { @@ -649,7 +656,17 @@ public abstract class BaseController {
649 String strCustomerName = extractParameter(String.class, 2, additionalInfo); 656 String strCustomerName = extractParameter(String.class, 2, additionalInfo);
650 metaData.putValue("unassignedCustomerId", strCustomerId); 657 metaData.putValue("unassignedCustomerId", strCustomerId);
651 metaData.putValue("unassignedCustomerName", strCustomerName); 658 metaData.putValue("unassignedCustomerName", strCustomerName);
652 - } if (actionType == ActionType.ASSIGNED_TO_EDGE) { 659 + } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) {
  660 + String strTenantId = extractParameter(String.class, 0, additionalInfo);
  661 + String strTenantName = extractParameter(String.class, 1, additionalInfo);
  662 + metaData.putValue("assignedFromTenantId", strTenantId);
  663 + metaData.putValue("assignedFromTenantName", strTenantName);
  664 + } else if (actionType == ActionType.ASSIGNED_TO_TENANT) {
  665 + String strTenantId = extractParameter(String.class, 0, additionalInfo);
  666 + String strTenantName = extractParameter(String.class, 1, additionalInfo);
  667 + metaData.putValue("assignedToTenantId", strTenantId);
  668 + metaData.putValue("assignedToTenantName", strTenantName);
  669 + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) {
653 String strEdgeId = extractParameter(String.class, 1, additionalInfo); 670 String strEdgeId = extractParameter(String.class, 1, additionalInfo);
654 metaData.putValue("assignedEdgeId", strEdgeId); 671 metaData.putValue("assignedEdgeId", strEdgeId);
655 } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { 672 } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
@@ -718,6 +735,15 @@ public abstract class BaseController { @@ -718,6 +735,15 @@ public abstract class BaseController {
718 return result; 735 return result;
719 } 736 }
720 737
  738 + protected <E extends HasName> String entityToStr(E entity) {
  739 + try {
  740 + return json.writeValueAsString(json.valueToTree(entity));
  741 + } catch (JsonProcessingException e) {
  742 + log.warn("[{}] Failed to convert entity to string!", entity, e);
  743 + }
  744 + return null;
  745 + }
  746 +
721 protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, CustomerId customerId, ActionType edgeEventAction) { 747 protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, CustomerId customerId, ActionType edgeEventAction) {
722 try { 748 try {
723 sendNotificationMsgToEdgeService(tenantId, edgeId, null, json.writeValueAsString(customerId), EdgeEventType.EDGE, edgeEventAction); 749 sendNotificationMsgToEdgeService(tenantId, edgeId, null, json.writeValueAsString(customerId), EdgeEventType.EDGE, edgeEventAction);
@@ -39,10 +39,11 @@ import org.thingsboard.server.common.data.DataConstants; @@ -39,10 +39,11 @@ import org.thingsboard.server.common.data.DataConstants;
39 import org.thingsboard.server.common.data.Device; 39 import org.thingsboard.server.common.data.Device;
40 import org.thingsboard.server.common.data.EntitySubtype; 40 import org.thingsboard.server.common.data.EntitySubtype;
41 import org.thingsboard.server.common.data.EntityType; 41 import org.thingsboard.server.common.data.EntityType;
  42 +import org.thingsboard.server.common.data.Tenant;
42 import org.thingsboard.server.common.data.audit.ActionType; 43 import org.thingsboard.server.common.data.audit.ActionType;
43 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 44 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
44 import org.thingsboard.server.common.data.edge.Edge; 45 import org.thingsboard.server.common.data.edge.Edge;
45 -import org.thingsboard.server.common.data.edge.EdgeEventType; 46 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
46 import org.thingsboard.server.common.data.exception.ThingsboardException; 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
47 import org.thingsboard.server.common.data.id.CustomerId; 48 import org.thingsboard.server.common.data.id.CustomerId;
48 import org.thingsboard.server.common.data.id.DeviceId; 49 import org.thingsboard.server.common.data.id.DeviceId;
@@ -52,13 +53,14 @@ import org.thingsboard.server.common.data.page.TextPageData; @@ -52,13 +53,14 @@ import org.thingsboard.server.common.data.page.TextPageData;
52 import org.thingsboard.server.common.data.page.TextPageLink; 53 import org.thingsboard.server.common.data.page.TextPageLink;
53 import org.thingsboard.server.common.data.page.TimePageData; 54 import org.thingsboard.server.common.data.page.TimePageData;
54 import org.thingsboard.server.common.data.page.TimePageLink; 55 import org.thingsboard.server.common.data.page.TimePageLink;
55 -import org.thingsboard.server.common.data.rule.RuleChain;  
56 import org.thingsboard.server.common.data.security.DeviceCredentials; 56 import org.thingsboard.server.common.data.security.DeviceCredentials;
  57 +import org.thingsboard.server.common.msg.TbMsg;
  58 +import org.thingsboard.server.common.msg.TbMsgDataType;
  59 +import org.thingsboard.server.common.msg.TbMsgMetaData;
57 import org.thingsboard.server.dao.device.claim.ClaimResponse; 60 import org.thingsboard.server.dao.device.claim.ClaimResponse;
58 import org.thingsboard.server.dao.device.claim.ClaimResult; 61 import org.thingsboard.server.dao.device.claim.ClaimResult;
59 import org.thingsboard.server.dao.exception.IncorrectParameterException; 62 import org.thingsboard.server.dao.exception.IncorrectParameterException;
60 import org.thingsboard.server.dao.model.ModelConstants; 63 import org.thingsboard.server.dao.model.ModelConstants;
61 -import org.thingsboard.server.gen.transport.TransportProtos;  
62 import org.thingsboard.server.queue.util.TbCoreComponent; 64 import org.thingsboard.server.queue.util.TbCoreComponent;
63 import org.thingsboard.server.service.security.model.SecurityUser; 65 import org.thingsboard.server.service.security.model.SecurityUser;
64 import org.thingsboard.server.service.security.permission.Operation; 66 import org.thingsboard.server.service.security.permission.Operation;
@@ -79,6 +81,7 @@ public class DeviceController extends BaseController { @@ -79,6 +81,7 @@ public class DeviceController extends BaseController {
79 81
80 private static final String DEVICE_ID = "deviceId"; 82 private static final String DEVICE_ID = "deviceId";
81 private static final String DEVICE_NAME = "deviceName"; 83 private static final String DEVICE_NAME = "deviceName";
  84 + private static final String TENANT_ID = "tenantId";
82 85
83 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 86 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
84 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET) 87 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
@@ -506,6 +509,56 @@ public class DeviceController extends BaseController { @@ -506,6 +509,56 @@ public class DeviceController extends BaseController {
506 } 509 }
507 510
508 @PreAuthorize("hasAuthority('TENANT_ADMIN')") 511 @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  512 + @RequestMapping(value = "/tenant/{tenantId}/device/{deviceId}", method = RequestMethod.POST)
  513 + @ResponseBody
  514 + public Device assignDeviceToTenant(@PathVariable(TENANT_ID) String strTenantId,
  515 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  516 + checkParameter(TENANT_ID, strTenantId);
  517 + checkParameter(DEVICE_ID, strDeviceId);
  518 + try {
  519 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  520 + Device device = checkDeviceId(deviceId, Operation.ASSIGN_TO_TENANT);
  521 +
  522 + TenantId newTenantId = new TenantId(toUUID(strTenantId));
  523 + Tenant newTenant = tenantService.findTenantById(newTenantId);
  524 + if (newTenant == null) {
  525 + throw new ThingsboardException("Could not find the specified Tenant!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  526 + }
  527 +
  528 + Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device);
  529 +
  530 + logEntityAction(getCurrentUser(), deviceId, assignedDevice,
  531 + assignedDevice.getCustomerId(),
  532 + ActionType.ASSIGNED_TO_TENANT, null, strTenantId, newTenant.getName());
  533 +
  534 + Tenant currentTenant = tenantService.findTenantById(getTenantId());
  535 + pushAssignedFromNotification(currentTenant, newTenantId, assignedDevice);
  536 +
  537 + return assignedDevice;
  538 + } catch (Exception e) {
  539 + logEntityAction(getCurrentUser(), emptyId(EntityType.DEVICE), null,
  540 + null,
  541 + ActionType.ASSIGNED_TO_TENANT, e, strTenantId);
  542 + throw handleException(e);
  543 + }
  544 + }
  545 +
  546 + private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
  547 + String data = entityToStr(assignedDevice);
  548 + if (data != null) {
  549 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
  550 + tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
  551 + }
  552 + }
  553 +
  554 + private TbMsgMetaData getMetaDataForAssignedFrom(Tenant tenant) {
  555 + TbMsgMetaData metaData = new TbMsgMetaData();
  556 + metaData.putValue("assignedFromTenantId", tenant.getId().getId().toString());
  557 + metaData.putValue("assignedFromTenantName", tenant.getName());
  558 + return metaData;
  559 + }
  560 +
  561 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
509 @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST) 562 @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST)
510 @ResponseBody 563 @ResponseBody
511 public Device assignDeviceToEdge(@PathVariable(EDGE_ID) String strEdgeId, 564 public Device assignDeviceToEdge(@PathVariable(EDGE_ID) String strEdgeId,
@@ -197,19 +197,21 @@ public class TelemetryController extends BaseController { @@ -197,19 +197,21 @@ public class TelemetryController extends BaseController {
197 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) 197 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"})
198 @ResponseBody 198 @ResponseBody
199 public DeferredResult<ResponseEntity> getTimeseries( 199 public DeferredResult<ResponseEntity> getTimeseries(
200 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, 200 + @PathVariable("entityType") String entityType,
  201 + @PathVariable("entityId") String entityIdStr,
201 @RequestParam(name = "keys") String keys, 202 @RequestParam(name = "keys") String keys,
202 @RequestParam(name = "startTs") Long startTs, 203 @RequestParam(name = "startTs") Long startTs,
203 @RequestParam(name = "endTs") Long endTs, 204 @RequestParam(name = "endTs") Long endTs,
204 @RequestParam(name = "interval", defaultValue = "0") Long interval, 205 @RequestParam(name = "interval", defaultValue = "0") Long interval,
205 @RequestParam(name = "limit", defaultValue = "100") Integer limit, 206 @RequestParam(name = "limit", defaultValue = "100") Integer limit,
206 @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, 207 @RequestParam(name = "agg", defaultValue = "NONE") String aggStr,
  208 + @RequestParam(name= "orderBy", defaultValue = "DESC") String orderBy,
207 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { 209 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
208 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, 210 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
209 (result, tenantId, entityId) -> { 211 (result, tenantId, entityId) -> {
210 // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted 212 // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
211 Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); 213 Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
212 - List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg)) 214 + List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy))
213 .collect(Collectors.toList()); 215 .collect(Collectors.toList());
214 216
215 Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); 217 Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
@@ -146,6 +146,11 @@ public class ThingsboardInstallService { @@ -146,6 +146,11 @@ public class ThingsboardInstallService {
146 databaseTsUpgradeService.upgradeDatabase("2.5.0"); 146 databaseTsUpgradeService.upgradeDatabase("2.5.0");
147 } 147 }
148 148
  149 + case "2.5.4":
  150 + log.info("Upgrading ThingsBoard from version 2.5.4 to 2.5.5 ...");
  151 + if (databaseTsUpgradeService != null) {
  152 + databaseTsUpgradeService.upgradeDatabase("2.5.4");
  153 + }
149 154
150 log.info("Updating system data..."); 155 log.info("Updating system data...");
151 156
@@ -49,6 +49,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase @@ -49,6 +49,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
49 log.info("Schema updated."); 49 log.info("Schema updated.");
50 break; 50 break;
51 case "2.5.0": 51 case "2.5.0":
  52 + case "2.5.4":
52 break; 53 break;
53 default: 54 default:
54 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); 55 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
@@ -195,6 +195,12 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe @@ -195,6 +195,12 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
195 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); 195 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
196 } 196 }
197 break; 197 break;
  198 + case "2.5.4":
  199 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  200 + log.info("Load Drop Partitions functions ...");
  201 + loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
  202 + }
  203 + break;
198 default: 204 default:
199 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); 205 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
200 } 206 }
@@ -177,6 +177,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr @@ -177,6 +177,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
177 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); 177 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
178 } 178 }
179 break; 179 break;
  180 + case "2.5.4":
  181 + break;
180 default: 182 default:
181 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); 183 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
182 } 184 }
@@ -18,7 +18,7 @@ package org.thingsboard.server.service.security.permission; @@ -18,7 +18,7 @@ package org.thingsboard.server.service.security.permission;
18 public enum Operation { 18 public enum Operation {
19 19
20 ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL, 20 ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
21 - READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES, 21 + READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES, ASSIGN_TO_TENANT,
22 ASSIGN_TO_EDGE, UNASSIGN_FROM_EDGE 22 ASSIGN_TO_EDGE, UNASSIGN_FROM_EDGE
23 23
24 } 24 }
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.state; 16 package org.thingsboard.server.service.state;
17 17
18 -import com.fasterxml.jackson.databind.ObjectMapper; 18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 import com.google.common.base.Function; 19 import com.google.common.base.Function;
20 import com.google.common.util.concurrent.FutureCallback; 20 import com.google.common.util.concurrent.FutureCallback;
21 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
@@ -45,16 +45,17 @@ import org.thingsboard.server.common.data.page.TextPageLink; @@ -45,16 +45,17 @@ import org.thingsboard.server.common.data.page.TextPageLink;
45 import org.thingsboard.server.common.msg.TbMsg; 45 import org.thingsboard.server.common.msg.TbMsg;
46 import org.thingsboard.server.common.msg.TbMsgDataType; 46 import org.thingsboard.server.common.msg.TbMsgDataType;
47 import org.thingsboard.server.common.msg.TbMsgMetaData; 47 import org.thingsboard.server.common.msg.TbMsgMetaData;
  48 +import org.thingsboard.server.common.msg.queue.ServiceType;
  49 +import org.thingsboard.server.common.msg.queue.TbCallback;
  50 +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
48 import org.thingsboard.server.dao.attributes.AttributesService; 51 import org.thingsboard.server.dao.attributes.AttributesService;
49 import org.thingsboard.server.dao.device.DeviceService; 52 import org.thingsboard.server.dao.device.DeviceService;
50 import org.thingsboard.server.dao.tenant.TenantService; 53 import org.thingsboard.server.dao.tenant.TenantService;
51 import org.thingsboard.server.dao.timeseries.TimeseriesService; 54 import org.thingsboard.server.dao.timeseries.TimeseriesService;
  55 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  56 +import org.thingsboard.server.gen.transport.TransportProtos;
52 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 57 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
53 import org.thingsboard.server.queue.discovery.PartitionService; 58 import org.thingsboard.server.queue.discovery.PartitionService;
54 -import org.thingsboard.server.common.msg.queue.ServiceType;  
55 -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;  
56 -import org.thingsboard.server.gen.transport.TransportProtos;  
57 -import org.thingsboard.server.common.msg.queue.TbCallback;  
58 import org.thingsboard.server.queue.util.TbCoreComponent; 59 import org.thingsboard.server.queue.util.TbCoreComponent;
59 import org.thingsboard.server.service.queue.TbClusterService; 60 import org.thingsboard.server.service.queue.TbClusterService;
60 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; 61 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
@@ -90,7 +91,6 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; @@ -90,7 +91,6 @@ import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE;
90 @Slf4j 91 @Slf4j
91 public class DefaultDeviceStateService implements DeviceStateService { 92 public class DefaultDeviceStateService implements DeviceStateService {
92 93
93 - private static final ObjectMapper json = new ObjectMapper();  
94 public static final String ACTIVITY_STATE = "active"; 94 public static final String ACTIVITY_STATE = "active";
95 public static final String LAST_CONNECT_TIME = "lastConnectTime"; 95 public static final String LAST_CONNECT_TIME = "lastConnectTime";
96 public static final String LAST_DISCONNECT_TIME = "lastDisconnectTime"; 96 public static final String LAST_DISCONNECT_TIME = "lastDisconnectTime";
@@ -197,15 +197,15 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -197,15 +197,15 @@ public class DefaultDeviceStateService implements DeviceStateService {
197 if (lastReportedActivity > 0 && lastReportedActivity > lastSavedActivity) { 197 if (lastReportedActivity > 0 && lastReportedActivity > lastSavedActivity) {
198 DeviceStateData stateData = getOrFetchDeviceStateData(deviceId); 198 DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
199 if (stateData != null) { 199 if (stateData != null) {
200 - DeviceState state = stateData.getState();  
201 - stateData.getState().setLastActivityTime(lastReportedActivity);  
202 - stateData.getMetaData().putValue("scope", SERVER_SCOPE);  
203 - pushRuleEngineMessage(stateData, ACTIVITY_EVENT);  
204 save(deviceId, LAST_ACTIVITY_TIME, lastReportedActivity); 200 save(deviceId, LAST_ACTIVITY_TIME, lastReportedActivity);
205 deviceLastSavedActivity.put(deviceId, lastReportedActivity); 201 deviceLastSavedActivity.put(deviceId, lastReportedActivity);
  202 + DeviceState state = stateData.getState();
  203 + state.setLastActivityTime(lastReportedActivity);
206 if (!state.isActive()) { 204 if (!state.isActive()) {
207 state.setActive(true); 205 state.setActive(true);
208 save(deviceId, ACTIVITY_STATE, state.isActive()); 206 save(deviceId, ACTIVITY_STATE, state.isActive());
  207 + stateData.getMetaData().putValue("scope", SERVER_SCOPE);
  208 + pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
209 } 209 }
210 } 210 }
211 } 211 }
@@ -503,8 +503,15 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -503,8 +503,15 @@ public class DefaultDeviceStateService implements DeviceStateService {
503 private void pushRuleEngineMessage(DeviceStateData stateData, String msgType) { 503 private void pushRuleEngineMessage(DeviceStateData stateData, String msgType) {
504 DeviceState state = stateData.getState(); 504 DeviceState state = stateData.getState();
505 try { 505 try {
506 - TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON  
507 - , json.writeValueAsString(state)); 506 + String data;
  507 + if (msgType.equals(CONNECT_EVENT)) {
  508 + ObjectNode stateNode = JacksonUtil.convertValue(state, ObjectNode.class);
  509 + stateNode.remove(ACTIVITY_STATE);
  510 + data = JacksonUtil.toString(stateNode);
  511 + } else {
  512 + data = JacksonUtil.toString(state);
  513 + }
  514 + TbMsg tbMsg = TbMsg.newMsg(msgType, stateData.getDeviceId(), stateData.getMetaData().copy(), TbMsgDataType.JSON, data);
508 clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null); 515 clusterService.pushMsgToRuleEngine(stateData.getTenantId(), stateData.getDeviceId(), tbMsg, null);
509 } catch (Exception e) { 516 } catch (Exception e) {
510 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e); 517 log.warn("[{}] Failed to push inactivity alarm: {}", stateData.getDeviceId(), state, e);
@@ -154,7 +154,7 @@ public class DefaultTransportApiService implements TransportApiService { @@ -154,7 +154,7 @@ public class DefaultTransportApiService implements TransportApiService {
154 return TransportApiResponseMsg.newBuilder() 154 return TransportApiResponseMsg.newBuilder()
155 .setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build(); 155 .setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build();
156 } catch (JsonProcessingException e) { 156 } catch (JsonProcessingException e) {
157 - log.warn("[{}] Failed to lookup device by gateway id and name", gatewayId, requestMsg.getDeviceName(), e); 157 + log.warn("[{}][{}] Failed to lookup device by gateway id and name", gatewayId, requestMsg.getDeviceName(), e);
158 throw new RuntimeException(e); 158 throw new RuntimeException(e);
159 } finally { 159 } finally {
160 deviceCreationLock.unlock(); 160 deviceCreationLock.unlock();
@@ -38,19 +38,15 @@ public abstract class AbstractCleanUpService { @@ -38,19 +38,15 @@ public abstract class AbstractCleanUpService {
38 @Value("${spring.datasource.password}") 38 @Value("${spring.datasource.password}")
39 protected String dbPassword; 39 protected String dbPassword;
40 40
41 - protected long executeQuery(Connection conn, String query) {  
42 - long removed = 0L;  
43 - try {  
44 - Statement statement = conn.createStatement(); 41 + protected long executeQuery(Connection conn, String query) throws SQLException {
  42 + try (Statement statement = conn.createStatement()) {
45 ResultSet resultSet = statement.executeQuery(query); 43 ResultSet resultSet = statement.executeQuery(query);
46 - getWarnings(statement); 44 + if (log.isDebugEnabled()) {
  45 + getWarnings(statement);
  46 + }
47 resultSet.next(); 47 resultSet.next();
48 - removed = resultSet.getLong(1);  
49 - log.debug("Successfully executed query: {}", query);  
50 - } catch (SQLException e) {  
51 - log.debug("Failed to execute query: {} due to: {}", query, e.getMessage()); 48 + return resultSet.getLong(1);
52 } 49 }
53 - return removed;  
54 } 50 }
55 51
56 protected void getWarnings(Statement statement) throws SQLException { 52 protected void getWarnings(Statement statement) throws SQLException {
@@ -65,6 +61,6 @@ public abstract class AbstractCleanUpService { @@ -65,6 +61,6 @@ public abstract class AbstractCleanUpService {
65 } 61 }
66 } 62 }
67 63
68 - protected abstract void doCleanUp(Connection connection); 64 + protected abstract void doCleanUp(Connection connection) throws SQLException;
69 65
70 } 66 }
@@ -49,7 +49,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { @@ -49,7 +49,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService {
49 } 49 }
50 50
51 @Override 51 @Override
52 - protected void doCleanUp(Connection connection) { 52 + protected void doCleanUp(Connection connection) throws SQLException {
53 long totalEdgeEventsRemoved = executeQuery(connection, "call cleanup_edge_events_by_ttl(" + ttl + ", 0);"); 53 long totalEdgeEventsRemoved = executeQuery(connection, "call cleanup_edge_events_by_ttl(" + ttl + ", 0);");
54 log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); 54 log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved);
55 } 55 }
@@ -54,7 +54,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { @@ -54,7 +54,7 @@ public class EventsCleanUpService extends AbstractCleanUpService {
54 } 54 }
55 55
56 @Override 56 @Override
57 - protected void doCleanUp(Connection connection) { 57 + protected void doCleanUp(Connection connection) throws SQLException {
58 long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);"); 58 long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);");
59 log.info("Total events removed by TTL: [{}]", totalEventsRemoved); 59 log.info("Total events removed by TTL: [{}]", totalEventsRemoved);
60 } 60 }
@@ -22,6 +22,7 @@ import org.thingsboard.server.dao.model.ModelConstants; @@ -22,6 +22,7 @@ import org.thingsboard.server.dao.model.ModelConstants;
22 import org.thingsboard.server.dao.util.PsqlTsDao; 22 import org.thingsboard.server.dao.util.PsqlTsDao;
23 23
24 import java.sql.Connection; 24 import java.sql.Connection;
  25 +import java.sql.SQLException;
25 26
26 @PsqlTsDao 27 @PsqlTsDao
27 @Service 28 @Service
@@ -32,7 +33,7 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi @@ -32,7 +33,7 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi
32 private String partitionType; 33 private String partitionType;
33 34
34 @Override 35 @Override
35 - protected void doCleanUp(Connection connection) { 36 + protected void doCleanUp(Connection connection) throws SQLException {
36 long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);"); 37 long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");
37 log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved); 38 log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);
38 long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID_STR + "'," + systemTtl + ", 0);"); 39 long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID_STR + "'," + systemTtl + ", 0);");
@@ -21,6 +21,7 @@ import org.thingsboard.server.dao.model.ModelConstants; @@ -21,6 +21,7 @@ import org.thingsboard.server.dao.model.ModelConstants;
21 import org.thingsboard.server.dao.util.TimescaleDBTsDao; 21 import org.thingsboard.server.dao.util.TimescaleDBTsDao;
22 22
23 import java.sql.Connection; 23 import java.sql.Connection;
  24 +import java.sql.SQLException;
24 25
25 @TimescaleDBTsDao 26 @TimescaleDBTsDao
26 @Service 27 @Service
@@ -28,7 +29,7 @@ import java.sql.Connection; @@ -28,7 +29,7 @@ import java.sql.Connection;
28 public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { 29 public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService {
29 30
30 @Override 31 @Override
31 - protected void doCleanUp(Connection connection) { 32 + protected void doCleanUp(Connection connection) throws SQLException {
32 long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID_STR + "'," + systemTtl + ", 0);"); 33 long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID_STR + "'," + systemTtl + ", 0);");
33 log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); 34 log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved);
34 } 35 }
@@ -30,6 +30,8 @@ @@ -30,6 +30,8 @@
30 30
31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> 31 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> 32 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
  33 +<!-- <logger name="org.thingsboard.server.queue.memory.InMemoryStorage" level="DEBUG" />-->
  34 +
33 35
34 <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" /> 36 <logger name="com.microsoft.azure.servicebus.primitives.CoreMessageReceiver" level="OFF" />
35 37
@@ -589,7 +589,7 @@ transport: @@ -589,7 +589,7 @@ transport:
589 edges: 589 edges:
590 rpc: 590 rpc:
591 enabled: "${EDGES_RPC_ENABLED:true}" 591 enabled: "${EDGES_RPC_ENABLED:true}"
592 - port: "${EDGES_RPC_PORT:60100}" 592 + port: "${EDGES_RPC_PORT:7070}"
593 ssl: 593 ssl:
594 # Enable/disable SSL support 594 # Enable/disable SSL support
595 enabled: "${EDGES_RPC_SSL_ENABLED:false}" 595 enabled: "${EDGES_RPC_SSL_ENABLED:false}"
@@ -619,6 +619,10 @@ swagger: @@ -619,6 +619,10 @@ swagger:
619 619
620 queue: 620 queue:
621 type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) 621 type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ)
  622 + in_memory:
  623 + stats:
  624 + # For debug lvl
  625 + print-interval-ms: "${TB_QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}"
622 kafka: 626 kafka:
623 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" 627 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
624 acks: "${TB_KAFKA_ACKS:all}" 628 acks: "${TB_KAFKA_ACKS:all}"
@@ -630,13 +634,21 @@ queue: @@ -630,13 +634,21 @@ queue:
630 max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" 634 max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}"
631 max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" 635 max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}"
632 fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" 636 fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}"
  637 + use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}"
  638 + confluent:
  639 + ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}"
  640 + sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
  641 + sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}"
  642 + security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
  643 + other:
633 topic-properties: 644 topic-properties:
634 - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
635 - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
636 - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
637 - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
638 - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" 645 + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  646 + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  647 + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  648 + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  649 + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
639 aws_sqs: 650 aws_sqs:
  651 + use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
640 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" 652 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
641 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" 653 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
642 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" 654 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
@@ -79,8 +79,14 @@ import java.util.Comparator; @@ -79,8 +79,14 @@ import java.util.Comparator;
79 import java.util.List; 79 import java.util.List;
80 80
81 import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; 81 import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
82 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;  
83 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; 82 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
  83 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
  84 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
  85 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
  86 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
  87 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
  88 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
  89 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
84 import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 90 import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
85 91
86 @ActiveProfiles("test") 92 @ActiveProfiles("test")
@@ -221,6 +227,7 @@ public abstract class AbstractControllerTest { @@ -221,6 +227,7 @@ public abstract class AbstractControllerTest {
221 } 227 }
222 228
223 private Tenant savedDifferentTenant; 229 private Tenant savedDifferentTenant;
  230 +
224 protected void loginDifferentTenant() throws Exception { 231 protected void loginDifferentTenant() throws Exception {
225 loginSysAdmin(); 232 loginSysAdmin();
226 Tenant tenant = new Tenant(); 233 Tenant tenant = new Tenant();
@@ -316,6 +323,10 @@ public abstract class AbstractControllerTest { @@ -316,6 +323,10 @@ public abstract class AbstractControllerTest {
316 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); 323 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
317 } 324 }
318 325
  326 + protected <T> T doGet(String urlTemplate, Class<T> responseClass, ResultMatcher resultMatcher, Object... urlVariables) throws Exception {
  327 + return readResponse(doGet(urlTemplate, urlVariables).andExpect(resultMatcher), responseClass);
  328 + }
  329 +
319 protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception { 330 protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception {
320 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); 331 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
321 } 332 }
@@ -357,9 +368,9 @@ public abstract class AbstractControllerTest { @@ -357,9 +368,9 @@ public abstract class AbstractControllerTest {
357 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType); 368 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
358 } 369 }
359 370
360 - protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,  
361 - TimePageLink pageLink,  
362 - Object... urlVariables) throws Exception { 371 + protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
  372 + TimePageLink pageLink,
  373 + Object... urlVariables) throws Exception {
363 List<Object> pageLinkVariables = new ArrayList<>(); 374 List<Object> pageLinkVariables = new ArrayList<>();
364 urlTemplate += "limit={limit}"; 375 urlTemplate += "limit={limit}";
365 pageLinkVariables.add(pageLink.getLimit()); 376 pageLinkVariables.add(pageLink.getLimit());
@@ -425,7 +436,7 @@ public abstract class AbstractControllerTest { @@ -425,7 +436,7 @@ public abstract class AbstractControllerTest {
425 return mockMvc.perform(postRequest); 436 return mockMvc.perform(postRequest);
426 } 437 }
427 438
428 - protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception { 439 + protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
429 MockHttpServletRequestBuilder postRequest = post(urlTemplate); 440 MockHttpServletRequestBuilder postRequest = post(urlTemplate);
430 setJwtToken(postRequest); 441 setJwtToken(postRequest);
431 String json = json(content); 442 String json = json(content);
@@ -15,74 +15,79 @@ @@ -15,74 +15,79 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 -import static org.hamcrest.Matchers.containsString;  
19 -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;  
20 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
21 -  
22 -import java.util.ArrayList;  
23 -import java.util.Collections;  
24 -import java.util.List;  
25 -  
26 import com.datastax.driver.core.utils.UUIDs; 18 import com.datastax.driver.core.utils.UUIDs;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
27 import org.apache.commons.lang3.RandomStringUtils; 20 import org.apache.commons.lang3.RandomStringUtils;
28 -import org.thingsboard.server.common.data.*; 21 +import org.junit.After;
  22 +import org.junit.Assert;
  23 +import org.junit.Before;
  24 +import org.junit.Test;
  25 +import org.thingsboard.server.common.data.Customer;
  26 +import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.EntitySubtype;
  28 +import org.thingsboard.server.common.data.Tenant;
  29 +import org.thingsboard.server.common.data.User;
29 import org.thingsboard.server.common.data.id.CustomerId; 30 import org.thingsboard.server.common.data.id.CustomerId;
30 import org.thingsboard.server.common.data.id.DeviceCredentialsId; 31 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
31 import org.thingsboard.server.common.data.id.DeviceId; 32 import org.thingsboard.server.common.data.id.DeviceId;
32 import org.thingsboard.server.common.data.page.TextPageData; 33 import org.thingsboard.server.common.data.page.TextPageData;
33 import org.thingsboard.server.common.data.page.TextPageLink; 34 import org.thingsboard.server.common.data.page.TextPageLink;
  35 +import org.thingsboard.server.common.data.relation.EntityRelation;
  36 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
34 import org.thingsboard.server.common.data.security.Authority; 37 import org.thingsboard.server.common.data.security.Authority;
35 import org.thingsboard.server.common.data.security.DeviceCredentials; 38 import org.thingsboard.server.common.data.security.DeviceCredentials;
36 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 39 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
37 import org.thingsboard.server.dao.model.ModelConstants; 40 import org.thingsboard.server.dao.model.ModelConstants;
38 -import org.junit.After;  
39 -import org.junit.Assert;  
40 -import org.junit.Before;  
41 -import org.junit.Test;  
42 41
43 -import com.fasterxml.jackson.core.type.TypeReference; 42 +import java.util.ArrayList;
  43 +import java.util.Collections;
  44 +import java.util.List;
  45 +
  46 +import static org.hamcrest.Matchers.containsString;
  47 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
44 49
45 public abstract class BaseDeviceControllerTest extends AbstractControllerTest { 50 public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
46 - 51 +
47 private IdComparator<Device> idComparator = new IdComparator<>(); 52 private IdComparator<Device> idComparator = new IdComparator<>();
48 - 53 +
49 private Tenant savedTenant; 54 private Tenant savedTenant;
50 private User tenantAdmin; 55 private User tenantAdmin;
51 - 56 +
52 @Before 57 @Before
53 public void beforeTest() throws Exception { 58 public void beforeTest() throws Exception {
54 loginSysAdmin(); 59 loginSysAdmin();
55 - 60 +
56 Tenant tenant = new Tenant(); 61 Tenant tenant = new Tenant();
57 tenant.setTitle("My tenant"); 62 tenant.setTitle("My tenant");
58 savedTenant = doPost("/api/tenant", tenant, Tenant.class); 63 savedTenant = doPost("/api/tenant", tenant, Tenant.class);
59 Assert.assertNotNull(savedTenant); 64 Assert.assertNotNull(savedTenant);
60 - 65 +
61 tenantAdmin = new User(); 66 tenantAdmin = new User();
62 tenantAdmin.setAuthority(Authority.TENANT_ADMIN); 67 tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
63 tenantAdmin.setTenantId(savedTenant.getId()); 68 tenantAdmin.setTenantId(savedTenant.getId());
64 tenantAdmin.setEmail("tenant2@thingsboard.org"); 69 tenantAdmin.setEmail("tenant2@thingsboard.org");
65 tenantAdmin.setFirstName("Joe"); 70 tenantAdmin.setFirstName("Joe");
66 tenantAdmin.setLastName("Downs"); 71 tenantAdmin.setLastName("Downs");
67 - 72 +
68 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); 73 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
69 } 74 }
70 - 75 +
71 @After 76 @After
72 public void afterTest() throws Exception { 77 public void afterTest() throws Exception {
73 loginSysAdmin(); 78 loginSysAdmin();
74 -  
75 - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())  
76 - .andExpect(status().isOk()); 79 +
  80 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  81 + .andExpect(status().isOk());
77 } 82 }
78 - 83 +
79 @Test 84 @Test
80 public void testSaveDevice() throws Exception { 85 public void testSaveDevice() throws Exception {
81 Device device = new Device(); 86 Device device = new Device();
82 device.setName("My device"); 87 device.setName("My device");
83 device.setType("default"); 88 device.setType("default");
84 Device savedDevice = doPost("/api/device", device, Device.class); 89 Device savedDevice = doPost("/api/device", device, Device.class);
85 - 90 +
86 Assert.assertNotNull(savedDevice); 91 Assert.assertNotNull(savedDevice);
87 Assert.assertNotNull(savedDevice.getId()); 92 Assert.assertNotNull(savedDevice.getId());
88 Assert.assertTrue(savedDevice.getCreatedTime() > 0); 93 Assert.assertTrue(savedDevice.getCreatedTime() > 0);
@@ -90,9 +95,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -90,9 +95,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
90 Assert.assertNotNull(savedDevice.getCustomerId()); 95 Assert.assertNotNull(savedDevice.getCustomerId());
91 Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId()); 96 Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId());
92 Assert.assertEquals(device.getName(), savedDevice.getName()); 97 Assert.assertEquals(device.getName(), savedDevice.getName());
93 -  
94 - DeviceCredentials deviceCredentials =  
95 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 98 +
  99 + DeviceCredentials deviceCredentials =
  100 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
96 101
97 Assert.assertNotNull(deviceCredentials); 102 Assert.assertNotNull(deviceCredentials);
98 Assert.assertNotNull(deviceCredentials.getId()); 103 Assert.assertNotNull(deviceCredentials.getId());
@@ -100,10 +105,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -100,10 +105,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
100 Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, deviceCredentials.getCredentialsType()); 105 Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, deviceCredentials.getCredentialsType());
101 Assert.assertNotNull(deviceCredentials.getCredentialsId()); 106 Assert.assertNotNull(deviceCredentials.getCredentialsId());
102 Assert.assertEquals(20, deviceCredentials.getCredentialsId().length()); 107 Assert.assertEquals(20, deviceCredentials.getCredentialsId().length());
103 - 108 +
104 savedDevice.setName("My new device"); 109 savedDevice.setName("My new device");
105 doPost("/api/device", savedDevice, Device.class); 110 doPost("/api/device", savedDevice, Device.class);
106 - 111 +
107 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); 112 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
108 Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); 113 Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
109 } 114 }
@@ -115,10 +120,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -115,10 +120,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
115 device.setType("default"); 120 device.setType("default");
116 Device savedDevice = doPost("/api/device", device, Device.class); 121 Device savedDevice = doPost("/api/device", device, Device.class);
117 loginDifferentTenant(); 122 loginDifferentTenant();
118 - doPost("/api/device", savedDevice, Device.class, status().isForbidden()); 123 + doPost("/api/device", savedDevice, Device.class, status().isNotFound());
119 deleteDifferentTenant(); 124 deleteDifferentTenant();
120 } 125 }
121 - 126 +
122 @Test 127 @Test
123 public void testFindDeviceById() throws Exception { 128 public void testFindDeviceById() throws Exception {
124 Device device = new Device(); 129 Device device = new Device();
@@ -133,26 +138,27 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -133,26 +138,27 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
133 @Test 138 @Test
134 public void testFindDeviceTypesByTenantId() throws Exception { 139 public void testFindDeviceTypesByTenantId() throws Exception {
135 List<Device> devices = new ArrayList<>(); 140 List<Device> devices = new ArrayList<>();
136 - for (int i=0;i<3;i++) { 141 + for (int i = 0; i < 3; i++) {
137 Device device = new Device(); 142 Device device = new Device();
138 - device.setName("My device B"+i); 143 + device.setName("My device B" + i);
139 device.setType("typeB"); 144 device.setType("typeB");
140 devices.add(doPost("/api/device", device, Device.class)); 145 devices.add(doPost("/api/device", device, Device.class));
141 } 146 }
142 - for (int i=0;i<7;i++) { 147 + for (int i = 0; i < 7; i++) {
143 Device device = new Device(); 148 Device device = new Device();
144 - device.setName("My device C"+i); 149 + device.setName("My device C" + i);
145 device.setType("typeC"); 150 device.setType("typeC");
146 devices.add(doPost("/api/device", device, Device.class)); 151 devices.add(doPost("/api/device", device, Device.class));
147 } 152 }
148 - for (int i=0;i<9;i++) { 153 + for (int i = 0; i < 9; i++) {
149 Device device = new Device(); 154 Device device = new Device();
150 - device.setName("My device A"+i); 155 + device.setName("My device A" + i);
151 device.setType("typeA"); 156 device.setType("typeA");
152 devices.add(doPost("/api/device", device, Device.class)); 157 devices.add(doPost("/api/device", device, Device.class));
153 } 158 }
154 List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types", 159 List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types",
155 - new TypeReference<List<EntitySubtype>>(){}); 160 + new TypeReference<List<EntitySubtype>>() {
  161 + });
156 162
157 Assert.assertNotNull(deviceTypes); 163 Assert.assertNotNull(deviceTypes);
158 Assert.assertEquals(3, deviceTypes.size()); 164 Assert.assertEquals(3, deviceTypes.size());
@@ -160,19 +166,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -160,19 +166,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
160 Assert.assertEquals("typeB", deviceTypes.get(1).getType()); 166 Assert.assertEquals("typeB", deviceTypes.get(1).getType());
161 Assert.assertEquals("typeC", deviceTypes.get(2).getType()); 167 Assert.assertEquals("typeC", deviceTypes.get(2).getType());
162 } 168 }
163 - 169 +
164 @Test 170 @Test
165 public void testDeleteDevice() throws Exception { 171 public void testDeleteDevice() throws Exception {
166 Device device = new Device(); 172 Device device = new Device();
167 device.setName("My device"); 173 device.setName("My device");
168 device.setType("default"); 174 device.setType("default");
169 Device savedDevice = doPost("/api/device", device, Device.class); 175 Device savedDevice = doPost("/api/device", device, Device.class);
170 -  
171 - doDelete("/api/device/"+savedDevice.getId().getId().toString())  
172 - .andExpect(status().isOk());  
173 176
174 - doGet("/api/device/"+savedDevice.getId().getId().toString())  
175 - .andExpect(status().isNotFound()); 177 + doDelete("/api/device/" + savedDevice.getId().getId().toString())
  178 + .andExpect(status().isOk());
  179 +
  180 + doGet("/api/device/" + savedDevice.getId().getId().toString())
  181 + .andExpect(status().isNotFound());
176 } 182 }
177 183
178 @Test 184 @Test
@@ -189,52 +195,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -189,52 +195,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
189 Device device = new Device(); 195 Device device = new Device();
190 device.setType("default"); 196 device.setType("default");
191 doPost("/api/device", device) 197 doPost("/api/device", device)
192 - .andExpect(status().isBadRequest())  
193 - .andExpect(statusReason(containsString("Device name should be specified"))); 198 + .andExpect(status().isBadRequest())
  199 + .andExpect(statusReason(containsString("Device name should be specified")));
194 } 200 }
195 - 201 +
196 @Test 202 @Test
197 public void testAssignUnassignDeviceToCustomer() throws Exception { 203 public void testAssignUnassignDeviceToCustomer() throws Exception {
198 Device device = new Device(); 204 Device device = new Device();
199 device.setName("My device"); 205 device.setName("My device");
200 device.setType("default"); 206 device.setType("default");
201 Device savedDevice = doPost("/api/device", device, Device.class); 207 Device savedDevice = doPost("/api/device", device, Device.class);
202 - 208 +
203 Customer customer = new Customer(); 209 Customer customer = new Customer();
204 customer.setTitle("My customer"); 210 customer.setTitle("My customer");
205 Customer savedCustomer = doPost("/api/customer", customer, Customer.class); 211 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
206 -  
207 - Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString() 212 +
  213 + Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
208 + "/device/" + savedDevice.getId().getId().toString(), Device.class); 214 + "/device/" + savedDevice.getId().getId().toString(), Device.class);
209 Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId()); 215 Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId());
210 - 216 +
211 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); 217 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
212 Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId()); 218 Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId());
213 219
214 - Device unassignedDevice = 220 + Device unassignedDevice =
215 doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class); 221 doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class);
216 Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId()); 222 Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId());
217 - 223 +
218 foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); 224 foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
219 Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId()); 225 Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId());
220 } 226 }
221 - 227 +
222 @Test 228 @Test
223 public void testAssignDeviceToNonExistentCustomer() throws Exception { 229 public void testAssignDeviceToNonExistentCustomer() throws Exception {
224 Device device = new Device(); 230 Device device = new Device();
225 device.setName("My device"); 231 device.setName("My device");
226 device.setType("default"); 232 device.setType("default");
227 Device savedDevice = doPost("/api/device", device, Device.class); 233 Device savedDevice = doPost("/api/device", device, Device.class);
228 - 234 +
229 doPost("/api/customer/" + UUIDs.timeBased().toString() 235 doPost("/api/customer/" + UUIDs.timeBased().toString()
230 + "/device/" + savedDevice.getId().getId().toString()) 236 + "/device/" + savedDevice.getId().getId().toString())
231 - .andExpect(status().isNotFound()); 237 + .andExpect(status().isNotFound());
232 } 238 }
233 - 239 +
234 @Test 240 @Test
235 public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception { 241 public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception {
236 loginSysAdmin(); 242 loginSysAdmin();
237 - 243 +
238 Tenant tenant2 = new Tenant(); 244 Tenant tenant2 = new Tenant();
239 tenant2.setTitle("Different tenant"); 245 tenant2.setTitle("Different tenant");
240 Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class); 246 Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
@@ -246,103 +252,103 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -246,103 +252,103 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
246 tenantAdmin2.setEmail("tenant3@thingsboard.org"); 252 tenantAdmin2.setEmail("tenant3@thingsboard.org");
247 tenantAdmin2.setFirstName("Joe"); 253 tenantAdmin2.setFirstName("Joe");
248 tenantAdmin2.setLastName("Downs"); 254 tenantAdmin2.setLastName("Downs");
249 - 255 +
250 tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); 256 tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
251 - 257 +
252 Customer customer = new Customer(); 258 Customer customer = new Customer();
253 customer.setTitle("Different customer"); 259 customer.setTitle("Different customer");
254 Customer savedCustomer = doPost("/api/customer", customer, Customer.class); 260 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
255 261
256 login(tenantAdmin.getEmail(), "testPassword1"); 262 login(tenantAdmin.getEmail(), "testPassword1");
257 - 263 +
258 Device device = new Device(); 264 Device device = new Device();
259 device.setName("My device"); 265 device.setName("My device");
260 device.setType("default"); 266 device.setType("default");
261 Device savedDevice = doPost("/api/device", device, Device.class); 267 Device savedDevice = doPost("/api/device", device, Device.class);
262 - 268 +
263 doPost("/api/customer/" + savedCustomer.getId().getId().toString() 269 doPost("/api/customer/" + savedCustomer.getId().getId().toString()
264 + "/device/" + savedDevice.getId().getId().toString()) 270 + "/device/" + savedDevice.getId().getId().toString())
265 - .andExpect(status().isForbidden());  
266 - 271 + .andExpect(status().isForbidden());
  272 +
267 loginSysAdmin(); 273 loginSysAdmin();
268 -  
269 - doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())  
270 - .andExpect(status().isOk()); 274 +
  275 + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
  276 + .andExpect(status().isOk());
271 } 277 }
272 - 278 +
273 @Test 279 @Test
274 public void testFindDeviceCredentialsByDeviceId() throws Exception { 280 public void testFindDeviceCredentialsByDeviceId() throws Exception {
275 Device device = new Device(); 281 Device device = new Device();
276 device.setName("My device"); 282 device.setName("My device");
277 device.setType("default"); 283 device.setType("default");
278 Device savedDevice = doPost("/api/device", device, Device.class); 284 Device savedDevice = doPost("/api/device", device, Device.class);
279 - DeviceCredentials deviceCredentials =  
280 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 285 + DeviceCredentials deviceCredentials =
  286 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
281 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); 287 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
282 } 288 }
283 - 289 +
284 @Test 290 @Test
285 public void testSaveDeviceCredentials() throws Exception { 291 public void testSaveDeviceCredentials() throws Exception {
286 Device device = new Device(); 292 Device device = new Device();
287 device.setName("My device"); 293 device.setName("My device");
288 device.setType("default"); 294 device.setType("default");
289 Device savedDevice = doPost("/api/device", device, Device.class); 295 Device savedDevice = doPost("/api/device", device, Device.class);
290 - DeviceCredentials deviceCredentials =  
291 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 296 + DeviceCredentials deviceCredentials =
  297 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
292 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); 298 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
293 deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); 299 deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
294 deviceCredentials.setCredentialsId("access_token"); 300 deviceCredentials.setCredentialsId("access_token");
295 doPost("/api/device/credentials", deviceCredentials) 301 doPost("/api/device/credentials", deviceCredentials)
296 - .andExpect(status().isOk());  
297 -  
298 - DeviceCredentials foundDeviceCredentials = 302 + .andExpect(status().isOk());
  303 +
  304 + DeviceCredentials foundDeviceCredentials =
299 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 305 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
300 - 306 +
301 Assert.assertEquals(deviceCredentials, foundDeviceCredentials); 307 Assert.assertEquals(deviceCredentials, foundDeviceCredentials);
302 } 308 }
303 - 309 +
304 @Test 310 @Test
305 public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception { 311 public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception {
306 DeviceCredentials deviceCredentials = new DeviceCredentials(); 312 DeviceCredentials deviceCredentials = new DeviceCredentials();
307 doPost("/api/device/credentials", deviceCredentials) 313 doPost("/api/device/credentials", deviceCredentials)
308 - .andExpect(status().isBadRequest()); 314 + .andExpect(status().isBadRequest());
309 } 315 }
310 - 316 +
311 @Test 317 @Test
312 public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception { 318 public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
313 Device device = new Device(); 319 Device device = new Device();
314 device.setName("My device"); 320 device.setName("My device");
315 device.setType("default"); 321 device.setType("default");
316 Device savedDevice = doPost("/api/device", device, Device.class); 322 Device savedDevice = doPost("/api/device", device, Device.class);
317 - DeviceCredentials deviceCredentials = 323 + DeviceCredentials deviceCredentials =
318 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 324 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
319 deviceCredentials.setCredentialsType(null); 325 deviceCredentials.setCredentialsType(null);
320 doPost("/api/device/credentials", deviceCredentials) 326 doPost("/api/device/credentials", deviceCredentials)
321 - .andExpect(status().isBadRequest())  
322 - .andExpect(statusReason(containsString("Device credentials type should be specified"))); 327 + .andExpect(status().isBadRequest())
  328 + .andExpect(statusReason(containsString("Device credentials type should be specified")));
323 } 329 }
324 - 330 +
325 @Test 331 @Test
326 public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception { 332 public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
327 Device device = new Device(); 333 Device device = new Device();
328 device.setName("My device"); 334 device.setName("My device");
329 device.setType("default"); 335 device.setType("default");
330 Device savedDevice = doPost("/api/device", device, Device.class); 336 Device savedDevice = doPost("/api/device", device, Device.class);
331 - DeviceCredentials deviceCredentials = 337 + DeviceCredentials deviceCredentials =
332 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 338 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
333 deviceCredentials.setCredentialsId(null); 339 deviceCredentials.setCredentialsId(null);
334 doPost("/api/device/credentials", deviceCredentials) 340 doPost("/api/device/credentials", deviceCredentials)
335 - .andExpect(status().isBadRequest())  
336 - .andExpect(statusReason(containsString("Device credentials id should be specified"))); 341 + .andExpect(status().isBadRequest())
  342 + .andExpect(statusReason(containsString("Device credentials id should be specified")));
337 } 343 }
338 - 344 +
339 @Test 345 @Test
340 public void testSaveNonExistentDeviceCredentials() throws Exception { 346 public void testSaveNonExistentDeviceCredentials() throws Exception {
341 Device device = new Device(); 347 Device device = new Device();
342 device.setName("My device"); 348 device.setName("My device");
343 device.setType("default"); 349 device.setType("default");
344 Device savedDevice = doPost("/api/device", device, Device.class); 350 Device savedDevice = doPost("/api/device", device, Device.class);
345 - DeviceCredentials deviceCredentials = 351 + DeviceCredentials deviceCredentials =
346 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 352 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
347 DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(UUIDs.timeBased())); 353 DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(UUIDs.timeBased()));
348 newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime()); 354 newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
@@ -350,29 +356,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -350,29 +356,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
350 newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType()); 356 newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType());
351 newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId()); 357 newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId());
352 doPost("/api/device/credentials", newDeviceCredentials) 358 doPost("/api/device/credentials", newDeviceCredentials)
353 - .andExpect(status().isBadRequest())  
354 - .andExpect(statusReason(containsString("Unable to update non-existent device credentials"))); 359 + .andExpect(status().isBadRequest())
  360 + .andExpect(statusReason(containsString("Unable to update non-existent device credentials")));
355 } 361 }
356 - 362 +
357 @Test 363 @Test
358 public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception { 364 public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
359 Device device = new Device(); 365 Device device = new Device();
360 device.setName("My device"); 366 device.setName("My device");
361 device.setType("default"); 367 device.setType("default");
362 Device savedDevice = doPost("/api/device", device, Device.class); 368 Device savedDevice = doPost("/api/device", device, Device.class);
363 - DeviceCredentials deviceCredentials = 369 + DeviceCredentials deviceCredentials =
364 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 370 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
365 deviceCredentials.setDeviceId(new DeviceId(UUIDs.timeBased())); 371 deviceCredentials.setDeviceId(new DeviceId(UUIDs.timeBased()));
366 doPost("/api/device/credentials", deviceCredentials) 372 doPost("/api/device/credentials", deviceCredentials)
367 - .andExpect(status().isNotFound()); 373 + .andExpect(status().isNotFound());
368 } 374 }
369 375
370 @Test 376 @Test
371 public void testFindTenantDevices() throws Exception { 377 public void testFindTenantDevices() throws Exception {
372 List<Device> devices = new ArrayList<>(); 378 List<Device> devices = new ArrayList<>();
373 - for (int i=0;i<178;i++) { 379 + for (int i = 0; i < 178; i++) {
374 Device device = new Device(); 380 Device device = new Device();
375 - device.setName("Device"+i); 381 + device.setName("Device" + i);
376 device.setType("default"); 382 device.setType("default");
377 devices.add(doPost("/api/device", device, Device.class)); 383 devices.add(doPost("/api/device", device, Device.class));
378 } 384 }
@@ -380,28 +386,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -380,28 +386,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
380 TextPageLink pageLink = new TextPageLink(23); 386 TextPageLink pageLink = new TextPageLink(23);
381 TextPageData<Device> pageData = null; 387 TextPageData<Device> pageData = null;
382 do { 388 do {
383 - pageData = doGetTypedWithPageLink("/api/tenant/devices?",  
384 - new TypeReference<TextPageData<Device>>(){}, pageLink); 389 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  390 + new TypeReference<TextPageData<Device>>() {
  391 + }, pageLink);
385 loadedDevices.addAll(pageData.getData()); 392 loadedDevices.addAll(pageData.getData());
386 if (pageData.hasNext()) { 393 if (pageData.hasNext()) {
387 pageLink = pageData.getNextPageLink(); 394 pageLink = pageData.getNextPageLink();
388 } 395 }
389 } while (pageData.hasNext()); 396 } while (pageData.hasNext());
390 - 397 +
391 Collections.sort(devices, idComparator); 398 Collections.sort(devices, idComparator);
392 Collections.sort(loadedDevices, idComparator); 399 Collections.sort(loadedDevices, idComparator);
393 - 400 +
394 Assert.assertEquals(devices, loadedDevices); 401 Assert.assertEquals(devices, loadedDevices);
395 } 402 }
396 - 403 +
397 @Test 404 @Test
398 public void testFindTenantDevicesByName() throws Exception { 405 public void testFindTenantDevicesByName() throws Exception {
399 String title1 = "Device title 1"; 406 String title1 = "Device title 1";
400 List<Device> devicesTitle1 = new ArrayList<>(); 407 List<Device> devicesTitle1 = new ArrayList<>();
401 - for (int i=0;i<143;i++) { 408 + for (int i = 0; i < 143; i++) {
402 Device device = new Device(); 409 Device device = new Device();
403 String suffix = RandomStringUtils.randomAlphanumeric(15); 410 String suffix = RandomStringUtils.randomAlphanumeric(15);
404 - String name = title1+suffix; 411 + String name = title1 + suffix;
405 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 412 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
406 device.setName(name); 413 device.setName(name);
407 device.setType("default"); 414 device.setType("default");
@@ -409,38 +416,40 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -409,38 +416,40 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
409 } 416 }
410 String title2 = "Device title 2"; 417 String title2 = "Device title 2";
411 List<Device> devicesTitle2 = new ArrayList<>(); 418 List<Device> devicesTitle2 = new ArrayList<>();
412 - for (int i=0;i<75;i++) { 419 + for (int i = 0; i < 75; i++) {
413 Device device = new Device(); 420 Device device = new Device();
414 String suffix = RandomStringUtils.randomAlphanumeric(15); 421 String suffix = RandomStringUtils.randomAlphanumeric(15);
415 - String name = title2+suffix; 422 + String name = title2 + suffix;
416 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 423 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
417 device.setName(name); 424 device.setName(name);
418 device.setType("default"); 425 device.setType("default");
419 devicesTitle2.add(doPost("/api/device", device, Device.class)); 426 devicesTitle2.add(doPost("/api/device", device, Device.class));
420 } 427 }
421 - 428 +
422 List<Device> loadedDevicesTitle1 = new ArrayList<>(); 429 List<Device> loadedDevicesTitle1 = new ArrayList<>();
423 TextPageLink pageLink = new TextPageLink(15, title1); 430 TextPageLink pageLink = new TextPageLink(15, title1);
424 TextPageData<Device> pageData = null; 431 TextPageData<Device> pageData = null;
425 do { 432 do {
426 - pageData = doGetTypedWithPageLink("/api/tenant/devices?",  
427 - new TypeReference<TextPageData<Device>>(){}, pageLink); 433 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  434 + new TypeReference<TextPageData<Device>>() {
  435 + }, pageLink);
428 loadedDevicesTitle1.addAll(pageData.getData()); 436 loadedDevicesTitle1.addAll(pageData.getData());
429 if (pageData.hasNext()) { 437 if (pageData.hasNext()) {
430 pageLink = pageData.getNextPageLink(); 438 pageLink = pageData.getNextPageLink();
431 } 439 }
432 } while (pageData.hasNext()); 440 } while (pageData.hasNext());
433 - 441 +
434 Collections.sort(devicesTitle1, idComparator); 442 Collections.sort(devicesTitle1, idComparator);
435 Collections.sort(loadedDevicesTitle1, idComparator); 443 Collections.sort(loadedDevicesTitle1, idComparator);
436 - 444 +
437 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1); 445 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
438 - 446 +
439 List<Device> loadedDevicesTitle2 = new ArrayList<>(); 447 List<Device> loadedDevicesTitle2 = new ArrayList<>();
440 pageLink = new TextPageLink(4, title2); 448 pageLink = new TextPageLink(4, title2);
441 do { 449 do {
442 - pageData = doGetTypedWithPageLink("/api/tenant/devices?",  
443 - new TypeReference<TextPageData<Device>>(){}, pageLink); 450 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  451 + new TypeReference<TextPageData<Device>>() {
  452 + }, pageLink);
444 loadedDevicesTitle2.addAll(pageData.getData()); 453 loadedDevicesTitle2.addAll(pageData.getData());
445 if (pageData.hasNext()) { 454 if (pageData.hasNext()) {
446 pageLink = pageData.getNextPageLink(); 455 pageLink = pageData.getNextPageLink();
@@ -449,28 +458,30 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -449,28 +458,30 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
449 458
450 Collections.sort(devicesTitle2, idComparator); 459 Collections.sort(devicesTitle2, idComparator);
451 Collections.sort(loadedDevicesTitle2, idComparator); 460 Collections.sort(loadedDevicesTitle2, idComparator);
452 - 461 +
453 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2); 462 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
454 - 463 +
455 for (Device device : loadedDevicesTitle1) { 464 for (Device device : loadedDevicesTitle1) {
456 - doDelete("/api/device/"+device.getId().getId().toString())  
457 - .andExpect(status().isOk()); 465 + doDelete("/api/device/" + device.getId().getId().toString())
  466 + .andExpect(status().isOk());
458 } 467 }
459 - 468 +
460 pageLink = new TextPageLink(4, title1); 469 pageLink = new TextPageLink(4, title1);
461 - pageData = doGetTypedWithPageLink("/api/tenant/devices?",  
462 - new TypeReference<TextPageData<Device>>(){}, pageLink); 470 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  471 + new TypeReference<TextPageData<Device>>() {
  472 + }, pageLink);
463 Assert.assertFalse(pageData.hasNext()); 473 Assert.assertFalse(pageData.hasNext());
464 Assert.assertEquals(0, pageData.getData().size()); 474 Assert.assertEquals(0, pageData.getData().size());
465 - 475 +
466 for (Device device : loadedDevicesTitle2) { 476 for (Device device : loadedDevicesTitle2) {
467 - doDelete("/api/device/"+device.getId().getId().toString())  
468 - .andExpect(status().isOk()); 477 + doDelete("/api/device/" + device.getId().getId().toString())
  478 + .andExpect(status().isOk());
469 } 479 }
470 - 480 +
471 pageLink = new TextPageLink(4, title2); 481 pageLink = new TextPageLink(4, title2);
472 - pageData = doGetTypedWithPageLink("/api/tenant/devices?",  
473 - new TypeReference<TextPageData<Device>>(){}, pageLink); 482 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
  483 + new TypeReference<TextPageData<Device>>() {
  484 + }, pageLink);
474 Assert.assertFalse(pageData.hasNext()); 485 Assert.assertFalse(pageData.hasNext());
475 Assert.assertEquals(0, pageData.getData().size()); 486 Assert.assertEquals(0, pageData.getData().size());
476 } 487 }
@@ -480,10 +491,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -480,10 +491,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
480 String title1 = "Device title 1"; 491 String title1 = "Device title 1";
481 String type1 = "typeA"; 492 String type1 = "typeA";
482 List<Device> devicesType1 = new ArrayList<>(); 493 List<Device> devicesType1 = new ArrayList<>();
483 - for (int i=0;i<143;i++) { 494 + for (int i = 0; i < 143; i++) {
484 Device device = new Device(); 495 Device device = new Device();
485 String suffix = RandomStringUtils.randomAlphanumeric(15); 496 String suffix = RandomStringUtils.randomAlphanumeric(15);
486 - String name = title1+suffix; 497 + String name = title1 + suffix;
487 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 498 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
488 device.setName(name); 499 device.setName(name);
489 device.setType(type1); 500 device.setType(type1);
@@ -492,10 +503,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -492,10 +503,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
492 String title2 = "Device title 2"; 503 String title2 = "Device title 2";
493 String type2 = "typeB"; 504 String type2 = "typeB";
494 List<Device> devicesType2 = new ArrayList<>(); 505 List<Device> devicesType2 = new ArrayList<>();
495 - for (int i=0;i<75;i++) { 506 + for (int i = 0; i < 75; i++) {
496 Device device = new Device(); 507 Device device = new Device();
497 String suffix = RandomStringUtils.randomAlphanumeric(15); 508 String suffix = RandomStringUtils.randomAlphanumeric(15);
498 - String name = title2+suffix; 509 + String name = title2 + suffix;
499 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 510 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
500 device.setName(name); 511 device.setName(name);
501 device.setType(type2); 512 device.setType(type2);
@@ -507,7 +518,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -507,7 +518,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
507 TextPageData<Device> pageData = null; 518 TextPageData<Device> pageData = null;
508 do { 519 do {
509 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", 520 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
510 - new TypeReference<TextPageData<Device>>(){}, pageLink, type1); 521 + new TypeReference<TextPageData<Device>>() {
  522 + }, pageLink, type1);
511 loadedDevicesType1.addAll(pageData.getData()); 523 loadedDevicesType1.addAll(pageData.getData());
512 if (pageData.hasNext()) { 524 if (pageData.hasNext()) {
513 pageLink = pageData.getNextPageLink(); 525 pageLink = pageData.getNextPageLink();
@@ -523,7 +535,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -523,7 +535,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
523 pageLink = new TextPageLink(4); 535 pageLink = new TextPageLink(4);
524 do { 536 do {
525 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", 537 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
526 - new TypeReference<TextPageData<Device>>(){}, pageLink, type2); 538 + new TypeReference<TextPageData<Device>>() {
  539 + }, pageLink, type2);
527 loadedDevicesType2.addAll(pageData.getData()); 540 loadedDevicesType2.addAll(pageData.getData());
528 if (pageData.hasNext()) { 541 if (pageData.hasNext()) {
529 pageLink = pageData.getNextPageLink(); 542 pageLink = pageData.getNextPageLink();
@@ -536,63 +549,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -536,63 +549,66 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
536 Assert.assertEquals(devicesType2, loadedDevicesType2); 549 Assert.assertEquals(devicesType2, loadedDevicesType2);
537 550
538 for (Device device : loadedDevicesType1) { 551 for (Device device : loadedDevicesType1) {
539 - doDelete("/api/device/"+device.getId().getId().toString()) 552 + doDelete("/api/device/" + device.getId().getId().toString())
540 .andExpect(status().isOk()); 553 .andExpect(status().isOk());
541 } 554 }
542 555
543 pageLink = new TextPageLink(4); 556 pageLink = new TextPageLink(4);
544 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", 557 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
545 - new TypeReference<TextPageData<Device>>(){}, pageLink, type1); 558 + new TypeReference<TextPageData<Device>>() {
  559 + }, pageLink, type1);
546 Assert.assertFalse(pageData.hasNext()); 560 Assert.assertFalse(pageData.hasNext());
547 Assert.assertEquals(0, pageData.getData().size()); 561 Assert.assertEquals(0, pageData.getData().size());
548 562
549 for (Device device : loadedDevicesType2) { 563 for (Device device : loadedDevicesType2) {
550 - doDelete("/api/device/"+device.getId().getId().toString()) 564 + doDelete("/api/device/" + device.getId().getId().toString())
551 .andExpect(status().isOk()); 565 .andExpect(status().isOk());
552 } 566 }
553 567
554 pageLink = new TextPageLink(4); 568 pageLink = new TextPageLink(4);
555 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", 569 pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
556 - new TypeReference<TextPageData<Device>>(){}, pageLink, type2); 570 + new TypeReference<TextPageData<Device>>() {
  571 + }, pageLink, type2);
557 Assert.assertFalse(pageData.hasNext()); 572 Assert.assertFalse(pageData.hasNext());
558 Assert.assertEquals(0, pageData.getData().size()); 573 Assert.assertEquals(0, pageData.getData().size());
559 } 574 }
560 - 575 +
561 @Test 576 @Test
562 public void testFindCustomerDevices() throws Exception { 577 public void testFindCustomerDevices() throws Exception {
563 Customer customer = new Customer(); 578 Customer customer = new Customer();
564 customer.setTitle("Test customer"); 579 customer.setTitle("Test customer");
565 customer = doPost("/api/customer", customer, Customer.class); 580 customer = doPost("/api/customer", customer, Customer.class);
566 CustomerId customerId = customer.getId(); 581 CustomerId customerId = customer.getId();
567 - 582 +
568 List<Device> devices = new ArrayList<>(); 583 List<Device> devices = new ArrayList<>();
569 - for (int i=0;i<128;i++) { 584 + for (int i = 0; i < 128; i++) {
570 Device device = new Device(); 585 Device device = new Device();
571 - device.setName("Device"+i); 586 + device.setName("Device" + i);
572 device.setType("default"); 587 device.setType("default");
573 device = doPost("/api/device", device, Device.class); 588 device = doPost("/api/device", device, Device.class);
574 - devices.add(doPost("/api/customer/" + customerId.getId().toString()  
575 - + "/device/" + device.getId().getId().toString(), Device.class)); 589 + devices.add(doPost("/api/customer/" + customerId.getId().toString()
  590 + + "/device/" + device.getId().getId().toString(), Device.class));
576 } 591 }
577 - 592 +
578 List<Device> loadedDevices = new ArrayList<>(); 593 List<Device> loadedDevices = new ArrayList<>();
579 TextPageLink pageLink = new TextPageLink(23); 594 TextPageLink pageLink = new TextPageLink(23);
580 TextPageData<Device> pageData = null; 595 TextPageData<Device> pageData = null;
581 do { 596 do {
582 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",  
583 - new TypeReference<TextPageData<Device>>(){}, pageLink); 597 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  598 + new TypeReference<TextPageData<Device>>() {
  599 + }, pageLink);
584 loadedDevices.addAll(pageData.getData()); 600 loadedDevices.addAll(pageData.getData());
585 if (pageData.hasNext()) { 601 if (pageData.hasNext()) {
586 pageLink = pageData.getNextPageLink(); 602 pageLink = pageData.getNextPageLink();
587 } 603 }
588 } while (pageData.hasNext()); 604 } while (pageData.hasNext());
589 - 605 +
590 Collections.sort(devices, idComparator); 606 Collections.sort(devices, idComparator);
591 Collections.sort(loadedDevices, idComparator); 607 Collections.sort(loadedDevices, idComparator);
592 - 608 +
593 Assert.assertEquals(devices, loadedDevices); 609 Assert.assertEquals(devices, loadedDevices);
594 } 610 }
595 - 611 +
596 @Test 612 @Test
597 public void testFindCustomerDevicesByName() throws Exception { 613 public void testFindCustomerDevicesByName() throws Exception {
598 Customer customer = new Customer(); 614 Customer customer = new Customer();
@@ -602,53 +618,55 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -602,53 +618,55 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
602 618
603 String title1 = "Device title 1"; 619 String title1 = "Device title 1";
604 List<Device> devicesTitle1 = new ArrayList<>(); 620 List<Device> devicesTitle1 = new ArrayList<>();
605 - for (int i=0;i<125;i++) { 621 + for (int i = 0; i < 125; i++) {
606 Device device = new Device(); 622 Device device = new Device();
607 String suffix = RandomStringUtils.randomAlphanumeric(15); 623 String suffix = RandomStringUtils.randomAlphanumeric(15);
608 - String name = title1+suffix; 624 + String name = title1 + suffix;
609 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 625 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
610 device.setName(name); 626 device.setName(name);
611 device.setType("default"); 627 device.setType("default");
612 device = doPost("/api/device", device, Device.class); 628 device = doPost("/api/device", device, Device.class);
613 - devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString() 629 + devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
614 + "/device/" + device.getId().getId().toString(), Device.class)); 630 + "/device/" + device.getId().getId().toString(), Device.class));
615 } 631 }
616 String title2 = "Device title 2"; 632 String title2 = "Device title 2";
617 List<Device> devicesTitle2 = new ArrayList<>(); 633 List<Device> devicesTitle2 = new ArrayList<>();
618 - for (int i=0;i<143;i++) { 634 + for (int i = 0; i < 143; i++) {
619 Device device = new Device(); 635 Device device = new Device();
620 String suffix = RandomStringUtils.randomAlphanumeric(15); 636 String suffix = RandomStringUtils.randomAlphanumeric(15);
621 - String name = title2+suffix; 637 + String name = title2 + suffix;
622 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 638 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
623 device.setName(name); 639 device.setName(name);
624 device.setType("default"); 640 device.setType("default");
625 device = doPost("/api/device", device, Device.class); 641 device = doPost("/api/device", device, Device.class);
626 - devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString() 642 + devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
627 + "/device/" + device.getId().getId().toString(), Device.class)); 643 + "/device/" + device.getId().getId().toString(), Device.class));
628 } 644 }
629 - 645 +
630 List<Device> loadedDevicesTitle1 = new ArrayList<>(); 646 List<Device> loadedDevicesTitle1 = new ArrayList<>();
631 TextPageLink pageLink = new TextPageLink(15, title1); 647 TextPageLink pageLink = new TextPageLink(15, title1);
632 TextPageData<Device> pageData = null; 648 TextPageData<Device> pageData = null;
633 do { 649 do {
634 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",  
635 - new TypeReference<TextPageData<Device>>(){}, pageLink); 650 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  651 + new TypeReference<TextPageData<Device>>() {
  652 + }, pageLink);
636 loadedDevicesTitle1.addAll(pageData.getData()); 653 loadedDevicesTitle1.addAll(pageData.getData());
637 if (pageData.hasNext()) { 654 if (pageData.hasNext()) {
638 pageLink = pageData.getNextPageLink(); 655 pageLink = pageData.getNextPageLink();
639 } 656 }
640 } while (pageData.hasNext()); 657 } while (pageData.hasNext());
641 - 658 +
642 Collections.sort(devicesTitle1, idComparator); 659 Collections.sort(devicesTitle1, idComparator);
643 Collections.sort(loadedDevicesTitle1, idComparator); 660 Collections.sort(loadedDevicesTitle1, idComparator);
644 - 661 +
645 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1); 662 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
646 - 663 +
647 List<Device> loadedDevicesTitle2 = new ArrayList<>(); 664 List<Device> loadedDevicesTitle2 = new ArrayList<>();
648 pageLink = new TextPageLink(4, title2); 665 pageLink = new TextPageLink(4, title2);
649 do { 666 do {
650 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",  
651 - new TypeReference<TextPageData<Device>>(){}, pageLink); 667 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  668 + new TypeReference<TextPageData<Device>>() {
  669 + }, pageLink);
652 loadedDevicesTitle2.addAll(pageData.getData()); 670 loadedDevicesTitle2.addAll(pageData.getData());
653 if (pageData.hasNext()) { 671 if (pageData.hasNext()) {
654 pageLink = pageData.getNextPageLink(); 672 pageLink = pageData.getNextPageLink();
@@ -657,28 +675,30 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -657,28 +675,30 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
657 675
658 Collections.sort(devicesTitle2, idComparator); 676 Collections.sort(devicesTitle2, idComparator);
659 Collections.sort(loadedDevicesTitle2, idComparator); 677 Collections.sort(loadedDevicesTitle2, idComparator);
660 - 678 +
661 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2); 679 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
662 - 680 +
663 for (Device device : loadedDevicesTitle1) { 681 for (Device device : loadedDevicesTitle1) {
664 doDelete("/api/customer/device/" + device.getId().getId().toString()) 682 doDelete("/api/customer/device/" + device.getId().getId().toString())
665 - .andExpect(status().isOk()); 683 + .andExpect(status().isOk());
666 } 684 }
667 - 685 +
668 pageLink = new TextPageLink(4, title1); 686 pageLink = new TextPageLink(4, title1);
669 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",  
670 - new TypeReference<TextPageData<Device>>(){}, pageLink); 687 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  688 + new TypeReference<TextPageData<Device>>() {
  689 + }, pageLink);
671 Assert.assertFalse(pageData.hasNext()); 690 Assert.assertFalse(pageData.hasNext());
672 Assert.assertEquals(0, pageData.getData().size()); 691 Assert.assertEquals(0, pageData.getData().size());
673 - 692 +
674 for (Device device : loadedDevicesTitle2) { 693 for (Device device : loadedDevicesTitle2) {
675 doDelete("/api/customer/device/" + device.getId().getId().toString()) 694 doDelete("/api/customer/device/" + device.getId().getId().toString())
676 - .andExpect(status().isOk()); 695 + .andExpect(status().isOk());
677 } 696 }
678 - 697 +
679 pageLink = new TextPageLink(4, title2); 698 pageLink = new TextPageLink(4, title2);
680 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",  
681 - new TypeReference<TextPageData<Device>>(){}, pageLink); 699 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
  700 + new TypeReference<TextPageData<Device>>() {
  701 + }, pageLink);
682 Assert.assertFalse(pageData.hasNext()); 702 Assert.assertFalse(pageData.hasNext());
683 Assert.assertEquals(0, pageData.getData().size()); 703 Assert.assertEquals(0, pageData.getData().size());
684 } 704 }
@@ -693,10 +713,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -693,10 +713,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
693 String title1 = "Device title 1"; 713 String title1 = "Device title 1";
694 String type1 = "typeC"; 714 String type1 = "typeC";
695 List<Device> devicesType1 = new ArrayList<>(); 715 List<Device> devicesType1 = new ArrayList<>();
696 - for (int i=0;i<125;i++) { 716 + for (int i = 0; i < 125; i++) {
697 Device device = new Device(); 717 Device device = new Device();
698 String suffix = RandomStringUtils.randomAlphanumeric(15); 718 String suffix = RandomStringUtils.randomAlphanumeric(15);
699 - String name = title1+suffix; 719 + String name = title1 + suffix;
700 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 720 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
701 device.setName(name); 721 device.setName(name);
702 device.setType(type1); 722 device.setType(type1);
@@ -707,10 +727,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -707,10 +727,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
707 String title2 = "Device title 2"; 727 String title2 = "Device title 2";
708 String type2 = "typeD"; 728 String type2 = "typeD";
709 List<Device> devicesType2 = new ArrayList<>(); 729 List<Device> devicesType2 = new ArrayList<>();
710 - for (int i=0;i<143;i++) { 730 + for (int i = 0; i < 143; i++) {
711 Device device = new Device(); 731 Device device = new Device();
712 String suffix = RandomStringUtils.randomAlphanumeric(15); 732 String suffix = RandomStringUtils.randomAlphanumeric(15);
713 - String name = title2+suffix; 733 + String name = title2 + suffix;
714 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 734 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
715 device.setName(name); 735 device.setName(name);
716 device.setType(type2); 736 device.setType(type2);
@@ -724,7 +744,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -724,7 +744,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
724 TextPageData<Device> pageData = null; 744 TextPageData<Device> pageData = null;
725 do { 745 do {
726 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", 746 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
727 - new TypeReference<TextPageData<Device>>(){}, pageLink, type1); 747 + new TypeReference<TextPageData<Device>>() {
  748 + }, pageLink, type1);
728 loadedDevicesType1.addAll(pageData.getData()); 749 loadedDevicesType1.addAll(pageData.getData());
729 if (pageData.hasNext()) { 750 if (pageData.hasNext()) {
730 pageLink = pageData.getNextPageLink(); 751 pageLink = pageData.getNextPageLink();
@@ -740,7 +761,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -740,7 +761,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
740 pageLink = new TextPageLink(4); 761 pageLink = new TextPageLink(4);
741 do { 762 do {
742 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", 763 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
743 - new TypeReference<TextPageData<Device>>(){}, pageLink, type2); 764 + new TypeReference<TextPageData<Device>>() {
  765 + }, pageLink, type2);
744 loadedDevicesType2.addAll(pageData.getData()); 766 loadedDevicesType2.addAll(pageData.getData());
745 if (pageData.hasNext()) { 767 if (pageData.hasNext()) {
746 pageLink = pageData.getNextPageLink(); 768 pageLink = pageData.getNextPageLink();
@@ -759,7 +781,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -759,7 +781,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
759 781
760 pageLink = new TextPageLink(4); 782 pageLink = new TextPageLink(4);
761 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", 783 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
762 - new TypeReference<TextPageData<Device>>(){}, pageLink, type1); 784 + new TypeReference<TextPageData<Device>>() {
  785 + }, pageLink, type1);
763 Assert.assertFalse(pageData.hasNext()); 786 Assert.assertFalse(pageData.hasNext());
764 Assert.assertEquals(0, pageData.getData().size()); 787 Assert.assertEquals(0, pageData.getData().size());
765 788
@@ -770,9 +793,60 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -770,9 +793,60 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
770 793
771 pageLink = new TextPageLink(4); 794 pageLink = new TextPageLink(4);
772 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", 795 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
773 - new TypeReference<TextPageData<Device>>(){}, pageLink, type2); 796 + new TypeReference<TextPageData<Device>>() {
  797 + }, pageLink, type2);
774 Assert.assertFalse(pageData.hasNext()); 798 Assert.assertFalse(pageData.hasNext());
775 Assert.assertEquals(0, pageData.getData().size()); 799 Assert.assertEquals(0, pageData.getData().size());
776 } 800 }
777 801
  802 + @Test
  803 + public void testAssignDeviceToTenant() throws Exception {
  804 + Device device = new Device();
  805 + device.setName("My device");
  806 + device.setType("default");
  807 + Device savedDevice = doPost("/api/device", device, Device.class);
  808 +
  809 + Device anotherDevice = new Device();
  810 + anotherDevice.setName("My device1");
  811 + anotherDevice.setType("default");
  812 + Device savedAnotherDevice = doPost("/api/device", anotherDevice, Device.class);
  813 +
  814 + EntityRelation relation = new EntityRelation();
  815 + relation.setFrom(savedDevice.getId());
  816 + relation.setTo(savedAnotherDevice.getId());
  817 + relation.setTypeGroup(RelationTypeGroup.COMMON);
  818 + relation.setType("Contains");
  819 + doPost("/api/relation", relation).andExpect(status().isOk());
  820 +
  821 + loginSysAdmin();
  822 + Tenant tenant = new Tenant();
  823 + tenant.setTitle("Different tenant");
  824 + Tenant savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
  825 + Assert.assertNotNull(savedDifferentTenant);
  826 +
  827 + User user = new User();
  828 + user.setAuthority(Authority.TENANT_ADMIN);
  829 + user.setTenantId(savedDifferentTenant.getId());
  830 + user.setEmail("tenant9@thingsboard.org");
  831 + user.setFirstName("Sam");
  832 + user.setLastName("Downs");
  833 +
  834 + createUserAndLogin(user, "testPassword1");
  835 +
  836 + login("tenant2@thingsboard.org", "testPassword1");
  837 + Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class);
  838 +
  839 + doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class, status().isNotFound());
  840 +
  841 + login("tenant9@thingsboard.org", "testPassword1");
  842 +
  843 + Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class);
  844 + Assert.assertNotNull(foundDevice1);
  845 +
  846 + doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound());
  847 +
  848 + loginSysAdmin();
  849 + doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
  850 + .andExpect(status().isOk());
  851 + }
778 } 852 }
@@ -93,7 +93,7 @@ public class BaseEdgeEventControllerTest extends AbstractControllerTest { @@ -93,7 +93,7 @@ public class BaseEdgeEventControllerTest extends AbstractControllerTest {
93 93
94 doPost("/api/relation", relation); 94 doPost("/api/relation", relation);
95 95
96 - Thread.sleep(1000); 96 + Thread.sleep(2000);
97 97
98 List<EdgeEvent> edgeEvents = doGetTypedWithTimePageLink("/api/edge/" + edge.getId().toString() + "/events?", 98 List<EdgeEvent> edgeEvents = doGetTypedWithTimePageLink("/api/edge/" + edge.getId().toString() + "/events?",
99 new TypeReference<TimePageData<EdgeEvent>>() { 99 new TypeReference<TimePageData<EdgeEvent>>() {
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -16,14 +16,12 @@ @@ -16,14 +16,12 @@
16 package org.thingsboard.server.dao.audit; 16 package org.thingsboard.server.dao.audit;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 -import org.thingsboard.server.common.data.BaseData;  
20 import org.thingsboard.server.common.data.HasName; 19 import org.thingsboard.server.common.data.HasName;
21 import org.thingsboard.server.common.data.audit.ActionType; 20 import org.thingsboard.server.common.data.audit.ActionType;
22 import org.thingsboard.server.common.data.audit.AuditLog; 21 import org.thingsboard.server.common.data.audit.AuditLog;
23 import org.thingsboard.server.common.data.id.CustomerId; 22 import org.thingsboard.server.common.data.id.CustomerId;
24 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
25 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
26 -import org.thingsboard.server.common.data.id.UUIDBased;  
27 import org.thingsboard.server.common.data.id.UserId; 25 import org.thingsboard.server.common.data.id.UserId;
28 import org.thingsboard.server.common.data.page.TimePageData; 26 import org.thingsboard.server.common.data.page.TimePageData;
29 import org.thingsboard.server.common.data.page.TimePageLink; 27 import org.thingsboard.server.common.data.page.TimePageLink;
@@ -49,5 +47,4 @@ public interface AuditLogService { @@ -49,5 +47,4 @@ public interface AuditLogService {
49 E entity, 47 E entity,
50 ActionType actionType, 48 ActionType actionType,
51 Exception e, Object... additionalInfo); 49 Exception e, Object... additionalInfo);
52 -  
53 } 50 }
@@ -68,6 +68,8 @@ public interface DeviceService { @@ -68,6 +68,8 @@ public interface DeviceService {
68 68
69 ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId); 69 ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
70 70
  71 + Device assignDeviceToTenant(TenantId tenantId, Device device);
  72 +
71 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); 73 Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
72 74
73 Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId); 75 Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
@@ -41,4 +41,6 @@ public interface EventService { @@ -41,4 +41,6 @@ public interface EventService {
41 41
42 List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit); 42 List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
43 43
  44 + void removeEvents(TenantId tenantId, EntityId entityId);
  45 +
44 } 46 }
@@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery; @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
24 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 24 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
25 25
26 import java.util.List; 26 import java.util.List;
27 -import java.util.concurrent.ExecutionException;  
28 27
29 /** 28 /**
30 * Created by ashvayka on 27.04.17. 29 * Created by ashvayka on 27.04.17.
@@ -77,6 +76,8 @@ public interface RelationService { @@ -77,6 +76,8 @@ public interface RelationService {
77 76
78 ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query); 77 ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query);
79 78
  79 + void removeRelations(TenantId tenantId, EntityId entityId);
  80 +
80 // TODO: This method may be useful for some validations in the future 81 // TODO: This method may be useful for some validations in the future
81 // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to); 82 // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
82 83
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -57,6 +57,8 @@ public class DataConstants { @@ -57,6 +57,8 @@ public class DataConstants {
57 public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; 57 public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED";
58 public static final String ALARM_ACK = "ALARM_ACK"; 58 public static final String ALARM_ACK = "ALARM_ACK";
59 public static final String ALARM_CLEAR = "ALARM_CLEAR"; 59 public static final String ALARM_CLEAR = "ALARM_CLEAR";
  60 + public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT";
  61 + public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT";
60 public static final String ENTITY_ASSIGNED_TO_EDGE = "ENTITY_ASSIGNED_TO_EDGE"; 62 public static final String ENTITY_ASSIGNED_TO_EDGE = "ENTITY_ASSIGNED_TO_EDGE";
61 public static final String ENTITY_UNASSIGNED_FROM_EDGE = "ENTITY_UNASSIGNED_FROM_EDGE"; 63 public static final String ENTITY_UNASSIGNED_FROM_EDGE = "ENTITY_UNASSIGNED_FROM_EDGE";
62 64
@@ -42,6 +42,8 @@ public enum ActionType { @@ -42,6 +42,8 @@ public enum ActionType {
42 LOGIN(false), 42 LOGIN(false),
43 LOGOUT(false), 43 LOGOUT(false),
44 LOCKOUT(false), 44 LOCKOUT(false),
  45 + ASSIGNED_FROM_TENANT(false),
  46 + ASSIGNED_TO_TENANT(false),
45 ASSIGNED_TO_EDGE(false), // log edge name 47 ASSIGNED_TO_EDGE(false), // log edge name
46 UNASSIGNED_FROM_EDGE(false), // log edge name 48 UNASSIGNED_FROM_EDGE(false), // log edge name
47 CREDENTIALS_REQUEST(false), // request credentials from edge 49 CREDENTIALS_REQUEST(false), // request credentials from edge
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
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.msg.kv;  
17 -  
18 -import java.io.Serializable;  
19 -import java.util.List;  
20 -  
21 -import org.thingsboard.server.common.data.kv.AttributeKey;  
22 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
23 -  
24 -public interface AttributesKVMsg extends Serializable {  
25 -  
26 - List<AttributeKvEntry> getClientAttributes();  
27 - List<AttributeKvEntry> getSharedAttributes();  
28 - List<AttributeKey> getDeletedAttributes();  
29 -}  
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.msg.kv;  
17 -  
18 -import lombok.AccessLevel;  
19 -import lombok.Data;  
20 -import lombok.RequiredArgsConstructor;  
21 -import org.thingsboard.server.common.data.kv.AttributeKey;  
22 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
23 -  
24 -import java.util.Collections;  
25 -import java.util.List;  
26 -  
27 -@Data  
28 -@RequiredArgsConstructor(access = AccessLevel.PRIVATE)  
29 -public class BasicAttributeKVMsg implements AttributesKVMsg {  
30 -  
31 - private static final long serialVersionUID = 1L;  
32 -  
33 - private final List<AttributeKvEntry> clientAttributes;  
34 - private final List<AttributeKvEntry> sharedAttributes;  
35 - private final List<AttributeKey> deletedAttributes;  
36 -  
37 - public static BasicAttributeKVMsg fromClient(List<AttributeKvEntry> attributes) {  
38 - return new BasicAttributeKVMsg(attributes, Collections.emptyList(), Collections.emptyList());  
39 - }  
40 -  
41 - public static BasicAttributeKVMsg fromShared(List<AttributeKvEntry> attributes) {  
42 - return new BasicAttributeKVMsg(Collections.emptyList(), attributes, Collections.emptyList());  
43 - }  
44 -  
45 - public static BasicAttributeKVMsg from(List<AttributeKvEntry> client, List<AttributeKvEntry> shared) {  
46 - return new BasicAttributeKVMsg(client, shared, Collections.emptyList());  
47 - }  
48 -  
49 - public static AttributesKVMsg fromDeleted(List<AttributeKey> shared) {  
50 - return new BasicAttributeKVMsg(Collections.emptyList(), Collections.emptyList(), shared);  
51 - }  
52 -}  
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>common</artifactId> 26 <artifactId>common</artifactId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -37,6 +37,7 @@ public class TbKafkaAdmin implements TbQueueAdmin { @@ -37,6 +37,7 @@ public class TbKafkaAdmin implements TbQueueAdmin {
37 private final AdminClient client; 37 private final AdminClient client;
38 private final Map<String, String> topicConfigs; 38 private final Map<String, String> topicConfigs;
39 private final Set<String> topics = ConcurrentHashMap.newKeySet(); 39 private final Set<String> topics = ConcurrentHashMap.newKeySet();
  40 + private final int numPartitions;
40 41
41 private final short replicationFactor; 42 private final short replicationFactor;
42 43
@@ -50,6 +51,13 @@ public class TbKafkaAdmin implements TbQueueAdmin { @@ -50,6 +51,13 @@ public class TbKafkaAdmin implements TbQueueAdmin {
50 log.error("Failed to get all topics.", e); 51 log.error("Failed to get all topics.", e);
51 } 52 }
52 53
  54 + String numPartitionsStr = topicConfigs.get("partitions");
  55 + if (numPartitionsStr != null) {
  56 + numPartitions = Integer.parseInt(numPartitionsStr);
  57 + topicConfigs.remove("partitions");
  58 + } else {
  59 + numPartitions = 1;
  60 + }
53 replicationFactor = settings.getReplicationFactor(); 61 replicationFactor = settings.getReplicationFactor();
54 } 62 }
55 63
@@ -59,7 +67,7 @@ public class TbKafkaAdmin implements TbQueueAdmin { @@ -59,7 +67,7 @@ public class TbKafkaAdmin implements TbQueueAdmin {
59 return; 67 return;
60 } 68 }
61 try { 69 try {
62 - NewTopic newTopic = new NewTopic(topic, 1, replicationFactor).configs(topicConfigs); 70 + NewTopic newTopic = new NewTopic(topic, numPartitions, replicationFactor).configs(topicConfigs);
63 createTopic(newTopic).values().get(topic).get(); 71 createTopic(newTopic).values().get(topic).get();
64 topics.add(topic); 72 topics.add(topic);
65 } catch (ExecutionException ee) { 73 } catch (ExecutionException ee) {
@@ -16,10 +16,13 @@ @@ -16,10 +16,13 @@
16 package org.thingsboard.server.queue.kafka; 16 package org.thingsboard.server.queue.kafka;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
  19 +import lombok.Setter;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
  21 +import org.apache.kafka.clients.CommonClientConfigs;
20 import org.apache.kafka.clients.producer.ProducerConfig; 22 import org.apache.kafka.clients.producer.ProducerConfig;
21 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 24 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  25 +import org.springframework.boot.context.properties.ConfigurationProperties;
23 import org.springframework.stereotype.Component; 26 import org.springframework.stereotype.Component;
24 27
25 import java.util.List; 28 import java.util.List;
@@ -30,6 +33,7 @@ import java.util.Properties; @@ -30,6 +33,7 @@ import java.util.Properties;
30 */ 33 */
31 @Slf4j 34 @Slf4j
32 @ConditionalOnExpression("'${queue.type:null}'=='kafka'") 35 @ConditionalOnExpression("'${queue.type:null}'=='kafka'")
  36 +@ConfigurationProperties(prefix = "queue.kafka")
33 @Component 37 @Component
34 public class TbKafkaSettings { 38 public class TbKafkaSettings {
35 39
@@ -65,20 +69,44 @@ public class TbKafkaSettings { @@ -65,20 +69,44 @@ public class TbKafkaSettings {
65 69
66 @Value("${queue.kafka.fetch_max_bytes:134217728}") 70 @Value("${queue.kafka.fetch_max_bytes:134217728}")
67 @Getter 71 @Getter
68 - private int fetchMaxBytes; 72 + private int fetchMaxBytes;
69 73
70 - @Value("${kafka.other:#{null}}") 74 + @Value("${queue.kafka.use_confluent_cloud:false}")
  75 + private boolean useConfluent;
  76 +
  77 + @Value("${queue.kafka.confluent.ssl.algorithm}")
  78 + private String sslAlgorithm;
  79 +
  80 + @Value("${queue.kafka.confluent.sasl.mechanism}")
  81 + private String saslMechanism;
  82 +
  83 + @Value("${queue.kafka.confluent.sasl.config}")
  84 + private String saslConfig;
  85 +
  86 + @Value("${queue.kafka.confluent.security.protocol}")
  87 + private String securityProtocol;
  88 +
  89 + @Setter
71 private List<TbKafkaProperty> other; 90 private List<TbKafkaProperty> other;
72 91
73 public Properties toProps() { 92 public Properties toProps() {
74 Properties props = new Properties(); 93 Properties props = new Properties();
75 props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); 94 props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
76 - props.put(ProducerConfig.ACKS_CONFIG, acks);  
77 props.put(ProducerConfig.RETRIES_CONFIG, retries); 95 props.put(ProducerConfig.RETRIES_CONFIG, retries);
78 - props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);  
79 - props.put(ProducerConfig.LINGER_MS_CONFIG, lingerMs);  
80 - props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);  
81 - if(other != null){ 96 +
  97 + if (useConfluent) {
  98 + props.put("ssl.endpoint.identification.algorithm", sslAlgorithm);
  99 + props.put("sasl.mechanism", saslMechanism);
  100 + props.put("sasl.jaas.config", saslConfig);
  101 + props.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol);
  102 + } else {
  103 + props.put(ProducerConfig.ACKS_CONFIG, acks);
  104 + props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
  105 + props.put(ProducerConfig.LINGER_MS_CONFIG, lingerMs);
  106 + props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
  107 + }
  108 +
  109 + if (other != null) {
82 other.forEach(kv -> props.put(kv.getKey(), kv.getValue())); 110 other.forEach(kv -> props.put(kv.getKey(), kv.getValue()));
83 } 111 }
84 return props; 112 return props;
@@ -24,7 +24,6 @@ import java.util.List; @@ -24,7 +24,6 @@ import java.util.List;
24 import java.util.concurrent.BlockingQueue; 24 import java.util.concurrent.BlockingQueue;
25 import java.util.concurrent.ConcurrentHashMap; 25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.LinkedBlockingQueue; 26 import java.util.concurrent.LinkedBlockingQueue;
27 -import java.util.concurrent.TimeUnit;  
28 27
29 @Slf4j 28 @Slf4j
30 public final class InMemoryStorage { 29 public final class InMemoryStorage {
@@ -35,6 +34,14 @@ public final class InMemoryStorage { @@ -35,6 +34,14 @@ public final class InMemoryStorage {
35 storage = new ConcurrentHashMap<>(); 34 storage = new ConcurrentHashMap<>();
36 } 35 }
37 36
  37 + public void printStats() {
  38 + storage.forEach((topic, queue) -> {
  39 + if (queue.size() > 0) {
  40 + log.debug("[{}] Queue Size [{}]", topic, queue.size());
  41 + }
  42 + });
  43 + }
  44 +
38 public static InMemoryStorage getInstance() { 45 public static InMemoryStorage getInstance() {
39 if (instance == null) { 46 if (instance == null) {
40 synchronized (InMemoryStorage.class) { 47 synchronized (InMemoryStorage.class) {
@@ -17,6 +17,7 @@ package org.thingsboard.server.queue.provider; @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.provider;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  20 +import org.springframework.scheduling.annotation.Scheduled;
20 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
21 import org.thingsboard.server.common.msg.queue.ServiceType; 22 import org.thingsboard.server.common.msg.queue.ServiceType;
22 import org.thingsboard.server.gen.js.JsInvokeProtos; 23 import org.thingsboard.server.gen.js.JsInvokeProtos;
@@ -28,6 +29,7 @@ import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; @@ -28,6 +29,7 @@ import org.thingsboard.server.queue.common.TbProtoJsQueueMsg;
28 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 29 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
29 import org.thingsboard.server.queue.discovery.PartitionService; 30 import org.thingsboard.server.queue.discovery.PartitionService;
30 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 31 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
  32 +import org.thingsboard.server.queue.memory.InMemoryStorage;
31 import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer; 33 import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer;
32 import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer; 34 import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer;
33 import org.thingsboard.server.queue.settings.TbQueueCoreSettings; 35 import org.thingsboard.server.queue.settings.TbQueueCoreSettings;
@@ -47,6 +49,7 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @@ -47,6 +49,7 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
47 private final TbQueueRuleEngineSettings ruleEngineSettings; 49 private final TbQueueRuleEngineSettings ruleEngineSettings;
48 private final TbQueueTransportApiSettings transportApiSettings; 50 private final TbQueueTransportApiSettings transportApiSettings;
49 private final TbQueueTransportNotificationSettings transportNotificationSettings; 51 private final TbQueueTransportNotificationSettings transportNotificationSettings;
  52 + private final InMemoryStorage storage;
50 53
51 public InMemoryMonolithQueueFactory(PartitionService partitionService, TbQueueCoreSettings coreSettings, 54 public InMemoryMonolithQueueFactory(PartitionService partitionService, TbQueueCoreSettings coreSettings,
52 TbQueueRuleEngineSettings ruleEngineSettings, 55 TbQueueRuleEngineSettings ruleEngineSettings,
@@ -59,6 +62,7 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @@ -59,6 +62,7 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
59 this.ruleEngineSettings = ruleEngineSettings; 62 this.ruleEngineSettings = ruleEngineSettings;
60 this.transportApiSettings = transportApiSettings; 63 this.transportApiSettings = transportApiSettings;
61 this.transportNotificationSettings = transportNotificationSettings; 64 this.transportNotificationSettings = transportNotificationSettings;
  65 + this.storage = InMemoryStorage.getInstance();
62 } 66 }
63 67
64 @Override 68 @Override
@@ -120,4 +124,9 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @@ -120,4 +124,9 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
120 public TbQueueRequestTemplate<TbProtoJsQueueMsg<JsInvokeProtos.RemoteJsRequest>, TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> createRemoteJsRequestTemplate() { 124 public TbQueueRequestTemplate<TbProtoJsQueueMsg<JsInvokeProtos.RemoteJsRequest>, TbProtoQueueMsg<JsInvokeProtos.RemoteJsResponse>> createRemoteJsRequestTemplate() {
121 return null; 125 return null;
122 } 126 }
  127 +
  128 + @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}")
  129 + private void printInMemoryStats() {
  130 + storage.printStats();
  131 + }
123 } 132 }
@@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
16 package org.thingsboard.server.queue.sqs; 16 package org.thingsboard.server.queue.sqs;
17 17
18 import com.amazonaws.auth.AWSCredentials; 18 import com.amazonaws.auth.AWSCredentials;
  19 +import com.amazonaws.auth.AWSCredentialsProvider;
19 import com.amazonaws.auth.AWSStaticCredentialsProvider; 20 import com.amazonaws.auth.AWSStaticCredentialsProvider;
20 import com.amazonaws.auth.BasicAWSCredentials; 21 import com.amazonaws.auth.BasicAWSCredentials;
  22 +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
21 import com.amazonaws.services.sqs.AmazonSQS; 23 import com.amazonaws.services.sqs.AmazonSQS;
22 import com.amazonaws.services.sqs.AmazonSQSClientBuilder; 24 import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
23 import com.amazonaws.services.sqs.model.CreateQueueRequest; 25 import com.amazonaws.services.sqs.model.CreateQueueRequest;
@@ -37,9 +39,16 @@ public class TbAwsSqsAdmin implements TbQueueAdmin { @@ -37,9 +39,16 @@ public class TbAwsSqsAdmin implements TbQueueAdmin {
37 public TbAwsSqsAdmin(TbAwsSqsSettings sqsSettings, Map<String, String> attributes) { 39 public TbAwsSqsAdmin(TbAwsSqsSettings sqsSettings, Map<String, String> attributes) {
38 this.attributes = attributes; 40 this.attributes = attributes;
39 41
40 - AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey()); 42 + AWSCredentialsProvider credentialsProvider;
  43 + if (sqsSettings.getUseDefaultCredentialProviderChain()) {
  44 + credentialsProvider = new DefaultAWSCredentialsProviderChain();
  45 + } else {
  46 + AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());
  47 + credentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
  48 + }
  49 +
41 sqsClient = AmazonSQSClientBuilder.standard() 50 sqsClient = AmazonSQSClientBuilder.standard()
42 - .withCredentials(new AWSStaticCredentialsProvider(awsCredentials)) 51 + .withCredentials(credentialsProvider)
43 .withRegion(sqsSettings.getRegion()) 52 .withRegion(sqsSettings.getRegion())
44 .build(); 53 .build();
45 54
@@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
16 package org.thingsboard.server.queue.sqs; 16 package org.thingsboard.server.queue.sqs;
17 17
18 import com.amazonaws.auth.AWSCredentials; 18 import com.amazonaws.auth.AWSCredentials;
  19 +import com.amazonaws.auth.AWSCredentialsProvider;
19 import com.amazonaws.auth.AWSStaticCredentialsProvider; 20 import com.amazonaws.auth.AWSStaticCredentialsProvider;
20 import com.amazonaws.auth.BasicAWSCredentials; 21 import com.amazonaws.auth.BasicAWSCredentials;
  22 +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
21 import com.amazonaws.services.sqs.AmazonSQS; 23 import com.amazonaws.services.sqs.AmazonSQS;
22 import com.amazonaws.services.sqs.AmazonSQSClientBuilder; 24 import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
23 import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry; 25 import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry;
@@ -67,13 +69,19 @@ public class TbAwsSqsConsumerTemplate<T extends TbQueueMsg> extends AbstractPara @@ -67,13 +69,19 @@ public class TbAwsSqsConsumerTemplate<T extends TbQueueMsg> extends AbstractPara
67 this.decoder = decoder; 69 this.decoder = decoder;
68 this.sqsSettings = sqsSettings; 70 this.sqsSettings = sqsSettings;
69 71
70 - AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());  
71 - AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials); 72 + AWSCredentialsProvider credentialsProvider;
  73 + if (sqsSettings.getUseDefaultCredentialProviderChain()) {
  74 + credentialsProvider = new DefaultAWSCredentialsProviderChain();
  75 + } else {
  76 + AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());
  77 + credentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
  78 + }
72 79
73 - this.sqsClient = AmazonSQSClientBuilder.standard()  
74 - .withCredentials(credProvider) 80 + sqsClient = AmazonSQSClientBuilder.standard()
  81 + .withCredentials(credentialsProvider)
75 .withRegion(sqsSettings.getRegion()) 82 .withRegion(sqsSettings.getRegion())
76 .build(); 83 .build();
  84 +
77 } 85 }
78 86
79 @Override 87 @Override
@@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
16 package org.thingsboard.server.queue.sqs; 16 package org.thingsboard.server.queue.sqs;
17 17
18 import com.amazonaws.auth.AWSCredentials; 18 import com.amazonaws.auth.AWSCredentials;
  19 +import com.amazonaws.auth.AWSCredentialsProvider;
19 import com.amazonaws.auth.AWSStaticCredentialsProvider; 20 import com.amazonaws.auth.AWSStaticCredentialsProvider;
20 import com.amazonaws.auth.BasicAWSCredentials; 21 import com.amazonaws.auth.BasicAWSCredentials;
  22 +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
21 import com.amazonaws.services.sqs.AmazonSQS; 23 import com.amazonaws.services.sqs.AmazonSQS;
22 import com.amazonaws.services.sqs.AmazonSQSClientBuilder; 24 import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
23 import com.amazonaws.services.sqs.model.SendMessageRequest; 25 import com.amazonaws.services.sqs.model.SendMessageRequest;
@@ -54,14 +56,18 @@ public class TbAwsSqsProducerTemplate<T extends TbQueueMsg> implements TbQueuePr @@ -54,14 +56,18 @@ public class TbAwsSqsProducerTemplate<T extends TbQueueMsg> implements TbQueuePr
54 this.admin = admin; 56 this.admin = admin;
55 this.defaultTopic = defaultTopic; 57 this.defaultTopic = defaultTopic;
56 58
57 - AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());  
58 - AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials); 59 + AWSCredentialsProvider credentialsProvider;
  60 + if (sqsSettings.getUseDefaultCredentialProviderChain()) {
  61 + credentialsProvider = new DefaultAWSCredentialsProviderChain();
  62 + } else {
  63 + AWSCredentials awsCredentials = new BasicAWSCredentials(sqsSettings.getAccessKeyId(), sqsSettings.getSecretAccessKey());
  64 + credentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);
  65 + }
59 66
60 - this.sqsClient = AmazonSQSClientBuilder.standard()  
61 - .withCredentials(credProvider) 67 + sqsClient = AmazonSQSClientBuilder.standard()
  68 + .withCredentials(credentialsProvider)
62 .withRegion(sqsSettings.getRegion()) 69 .withRegion(sqsSettings.getRegion())
63 .build(); 70 .build();
64 -  
65 producerExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); 71 producerExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
66 } 72 }
67 73
@@ -27,6 +27,9 @@ import org.springframework.stereotype.Component; @@ -27,6 +27,9 @@ import org.springframework.stereotype.Component;
27 @Data 27 @Data
28 public class TbAwsSqsSettings { 28 public class TbAwsSqsSettings {
29 29
  30 + @Value("${queue.aws_sqs.use_default_credential_provider_chain}")
  31 + private Boolean useDefaultCredentialProviderChain;
  32 +
30 @Value("${queue.aws_sqs.access_key_id}") 33 @Value("${queue.aws_sqs.access_key_id}")
31 private String accessKeyId; 34 private String accessKeyId;
32 35
@@ -127,7 +127,6 @@ message GetAttributeResponseMsg { @@ -127,7 +127,6 @@ message GetAttributeResponseMsg {
127 int32 requestId = 1; 127 int32 requestId = 1;
128 repeated TsKvProto clientAttributeList = 2; 128 repeated TsKvProto clientAttributeList = 2;
129 repeated TsKvProto sharedAttributeList = 3; 129 repeated TsKvProto sharedAttributeList = 3;
130 - repeated string deletedAttributeKeys = 4;  
131 string error = 5; 130 string error = 5;
132 } 131 }
133 132
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 <modelVersion>4.0.0</modelVersion> 22 <modelVersion>4.0.0</modelVersion>
23 <parent> 23 <parent>
24 <groupId>org.thingsboard</groupId> 24 <groupId>org.thingsboard</groupId>
25 - <version>2.5.3-SNAPSHOT</version> 25 + <version>2.5.5-SNAPSHOT</version>
26 <artifactId>common</artifactId> 26 <artifactId>common</artifactId>
27 </parent> 27 </parent>
28 <groupId>org.thingsboard.common</groupId> 28 <groupId>org.thingsboard.common</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.common</groupId> 22 <groupId>org.thingsboard.common</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common.transport</groupId> 26 <groupId>org.thingsboard.common.transport</groupId>
@@ -125,7 +125,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { @@ -125,7 +125,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
125 125
126 @Override 126 @Override
127 public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException { 127 public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
128 - if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0 && msg.getDeletedAttributeKeysCount() == 0) { 128 + if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) {
129 return new Response(CoAP.ResponseCode.NOT_FOUND); 129 return new Response(CoAP.ResponseCode.NOT_FOUND);
130 } else { 130 } else {
131 Response response = new Response(CoAP.ResponseCode.CONTENT); 131 Response response = new Response(CoAP.ResponseCode.CONTENT);
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.common</groupId> 22 <groupId>org.thingsboard.common</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common.transport</groupId> 26 <groupId>org.thingsboard.common.transport</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.common</groupId> 22 <groupId>org.thingsboard.common</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common.transport</groupId> 26 <groupId>org.thingsboard.common.transport</groupId>
@@ -53,7 +53,7 @@ import java.util.concurrent.TimeUnit; @@ -53,7 +53,7 @@ import java.util.concurrent.TimeUnit;
53 */ 53 */
54 @Slf4j 54 @Slf4j
55 @Component("MqttSslHandlerProvider") 55 @Component("MqttSslHandlerProvider")
56 -@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.http.enabled}'=='true')") 56 +@ConditionalOnExpression("'${transport.type:null}'=='null' || ('${transport.type}'=='local' && '${transport.mqtt.enabled}'=='true')")
57 @ConditionalOnProperty(prefix = "transport.mqtt.ssl", value = "enabled", havingValue = "true", matchIfMissing = false) 57 @ConditionalOnProperty(prefix = "transport.mqtt.ssl", value = "enabled", havingValue = "true", matchIfMissing = false)
58 public class MqttSslHandlerProvider { 58 public class MqttSslHandlerProvider {
59 59
@@ -207,6 +207,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { @@ -207,6 +207,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor {
207 try { 207 try {
208 return new JsonParser().parse(payload); 208 return new JsonParser().parse(payload);
209 } catch (JsonSyntaxException ex) { 209 } catch (JsonSyntaxException ex) {
  210 + log.warn("Payload is in incorrect format: {}", payload);
210 throw new AdaptorException(ex); 211 throw new AdaptorException(ex);
211 } 212 }
212 } 213 }
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.common</groupId> 22 <groupId>org.thingsboard.common</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common.transport</groupId> 26 <groupId>org.thingsboard.common.transport</groupId>
@@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry; @@ -35,7 +35,6 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry;
35 import org.thingsboard.server.common.data.kv.KvEntry; 35 import org.thingsboard.server.common.data.kv.KvEntry;
36 import org.thingsboard.server.common.data.kv.LongDataEntry; 36 import org.thingsboard.server.common.data.kv.LongDataEntry;
37 import org.thingsboard.server.common.data.kv.StringDataEntry; 37 import org.thingsboard.server.common.data.kv.StringDataEntry;
38 -import org.thingsboard.server.common.msg.kv.AttributesKVMsg;  
39 import org.thingsboard.server.gen.transport.TransportProtos; 38 import org.thingsboard.server.gen.transport.TransportProtos;
40 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 39 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
41 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; 40 import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
@@ -273,11 +272,6 @@ public class JsonConverter { @@ -273,11 +272,6 @@ public class JsonConverter {
273 payload.getSharedAttributeListList().forEach(addToObjectFromProto(attrObject)); 272 payload.getSharedAttributeListList().forEach(addToObjectFromProto(attrObject));
274 result.add("shared", attrObject); 273 result.add("shared", attrObject);
275 } 274 }
276 - if (payload.getDeletedAttributeKeysCount() > 0) {  
277 - JsonArray attrObject = new JsonArray();  
278 - payload.getDeletedAttributeKeysList().forEach(attrObject::add);  
279 - result.add("deleted", attrObject);  
280 - }  
281 return result; 275 return result;
282 } 276 }
283 277
@@ -294,31 +288,6 @@ public class JsonConverter { @@ -294,31 +288,6 @@ public class JsonConverter {
294 return result; 288 return result;
295 } 289 }
296 290
297 - public static JsonObject toJson(AttributesKVMsg payload, boolean asMap) {  
298 - JsonObject result = new JsonObject();  
299 - if (asMap) {  
300 - if (!payload.getClientAttributes().isEmpty()) {  
301 - JsonObject attrObject = new JsonObject();  
302 - payload.getClientAttributes().forEach(addToObject(attrObject));  
303 - result.add("client", attrObject);  
304 - }  
305 - if (!payload.getSharedAttributes().isEmpty()) {  
306 - JsonObject attrObject = new JsonObject();  
307 - payload.getSharedAttributes().forEach(addToObject(attrObject));  
308 - result.add("shared", attrObject);  
309 - }  
310 - } else {  
311 - payload.getClientAttributes().forEach(addToObject(result));  
312 - payload.getSharedAttributes().forEach(addToObject(result));  
313 - }  
314 - if (!payload.getDeletedAttributes().isEmpty()) {  
315 - JsonArray attrObject = new JsonArray();  
316 - payload.getDeletedAttributes().forEach(addToObject(attrObject));  
317 - result.add("deleted", attrObject);  
318 - }  
319 - return result;  
320 - }  
321 -  
322 public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) { 291 public static JsonObject getJsonObjectForGateway(String deviceName, TransportProtos.GetAttributeResponseMsg responseMsg) {
323 JsonObject result = new JsonObject(); 292 JsonObject result = new JsonObject();
324 result.addProperty("id", responseMsg.getRequestId()); 293 result.addProperty("id", responseMsg.getRequestId());
@@ -374,10 +343,6 @@ public class JsonConverter { @@ -374,10 +343,6 @@ public class JsonConverter {
374 } 343 }
375 } 344 }
376 345
377 - private static Consumer<AttributeKey> addToObject(JsonArray result) {  
378 - return key -> result.add(key.getAttributeKey());  
379 - }  
380 -  
381 private static Consumer<TsKvProto> addToObjectFromProto(JsonObject result) { 346 private static Consumer<TsKvProto> addToObjectFromProto(JsonObject result) {
382 return de -> { 347 return de -> {
383 switch (de.getKv().getType()) { 348 switch (de.getKv().getType()) {
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>dao</artifactId> 26 <artifactId>dao</artifactId>
@@ -166,7 +166,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -166,7 +166,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
166 try { 166 try {
167 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get(); 167 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
168 if (entityViews != null && !entityViews.isEmpty()) { 168 if (entityViews != null && !entityViews.isEmpty()) {
169 - throw new DataValidationException("Can't delete asset that is assigned to entity views!"); 169 + throw new DataValidationException("Can't delete asset that has entity views!");
170 } 170 }
171 } catch (ExecutionException | InterruptedException e) { 171 } catch (ExecutionException | InterruptedException e) {
172 log.error("Exception while finding entity views for assetId [{}]", assetId, e); 172 log.error("Exception while finding entity views for assetId [{}]", assetId, e);
@@ -22,11 +22,12 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -22,11 +22,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.EntityId; 22 import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.UserId; 23 import org.thingsboard.server.common.data.id.UserId;
24 import org.thingsboard.server.common.data.page.TimePageLink; 24 import org.thingsboard.server.common.data.page.TimePageLink;
  25 +import org.thingsboard.server.dao.Dao;
25 26
26 import java.util.List; 27 import java.util.List;
27 import java.util.UUID; 28 import java.util.UUID;
28 29
29 -public interface AuditLogDao { 30 +public interface AuditLogDao extends Dao<AuditLog> {
30 31
31 ListenableFuture<Void> saveByTenantId(AuditLog auditLog); 32 ListenableFuture<Void> saveByTenantId(AuditLog auditLog);
32 33
@@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
28 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 28 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
29 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
30 import org.springframework.util.StringUtils; 30 import org.springframework.util.StringUtils;
31 -import org.thingsboard.server.common.data.BaseData;  
32 import org.thingsboard.server.common.data.EntityType; 31 import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.HasName; 32 import org.thingsboard.server.common.data.HasName;
34 import org.thingsboard.server.common.data.audit.ActionStatus; 33 import org.thingsboard.server.common.data.audit.ActionStatus;
@@ -38,7 +37,6 @@ import org.thingsboard.server.common.data.id.AuditLogId; @@ -38,7 +37,6 @@ import org.thingsboard.server.common.data.id.AuditLogId;
38 import org.thingsboard.server.common.data.id.CustomerId; 37 import org.thingsboard.server.common.data.id.CustomerId;
39 import org.thingsboard.server.common.data.id.EntityId; 38 import org.thingsboard.server.common.data.id.EntityId;
40 import org.thingsboard.server.common.data.id.TenantId; 39 import org.thingsboard.server.common.data.id.TenantId;
41 -import org.thingsboard.server.common.data.id.UUIDBased;  
42 import org.thingsboard.server.common.data.id.UserId; 40 import org.thingsboard.server.common.data.id.UserId;
43 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 41 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
44 import org.thingsboard.server.common.data.page.TimePageData; 42 import org.thingsboard.server.common.data.page.TimePageData;
@@ -117,8 +115,8 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -117,8 +115,8 @@ public class AuditLogServiceImpl implements AuditLogService {
117 115
118 @Override 116 @Override
119 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> 117 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>>
120 - logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity,  
121 - ActionType actionType, Exception e, Object... additionalInfo) { 118 + logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity,
  119 + ActionType actionType, Exception e, Object... additionalInfo) {
122 if (canLog(entityId.getEntityType(), actionType)) { 120 if (canLog(entityId.getEntityType(), actionType)) {
123 JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo); 121 JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo);
124 ActionStatus actionStatus = ActionStatus.SUCCESS; 122 ActionStatus actionStatus = ActionStatus.SUCCESS;
@@ -129,7 +127,8 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -129,7 +127,8 @@ public class AuditLogServiceImpl implements AuditLogService {
129 } else { 127 } else {
130 try { 128 try {
131 entityName = entityService.fetchEntityNameAsync(tenantId, entityId).get(); 129 entityName = entityService.fetchEntityNameAsync(tenantId, entityId).get();
132 - } catch (Exception ex) {} 130 + } catch (Exception ex) {
  131 + }
133 } 132 }
134 if (e != null) { 133 if (e != null) {
135 actionStatus = ActionStatus.FAILURE; 134 actionStatus = ActionStatus.FAILURE;
@@ -158,15 +157,16 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -158,15 +157,16 @@ public class AuditLogServiceImpl implements AuditLogService {
158 } 157 }
159 158
160 private <E extends HasName, I extends EntityId> JsonNode constructActionData(I entityId, E entity, 159 private <E extends HasName, I extends EntityId> JsonNode constructActionData(I entityId, E entity,
161 - ActionType actionType,  
162 - Object... additionalInfo) { 160 + ActionType actionType,
  161 + Object... additionalInfo) {
163 ObjectNode actionData = objectMapper.createObjectNode(); 162 ObjectNode actionData = objectMapper.createObjectNode();
164 - switch(actionType) { 163 + switch (actionType) {
165 case ADDED: 164 case ADDED:
166 case UPDATED: 165 case UPDATED:
167 case ALARM_ACK: 166 case ALARM_ACK:
168 case ALARM_CLEAR: 167 case ALARM_CLEAR:
169 case RELATIONS_DELETED: 168 case RELATIONS_DELETED:
  169 + case ASSIGNED_TO_TENANT:
170 if (entity != null) { 170 if (entity != null) {
171 ObjectNode entityNode = objectMapper.valueToTree(entity); 171 ObjectNode entityNode = objectMapper.valueToTree(entity);
172 if (entityId.getEntityType() == EntityType.DASHBOARD) { 172 if (entityId.getEntityType() == EntityType.DASHBOARD) {
@@ -208,7 +208,7 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -208,7 +208,7 @@ public class AuditLogServiceImpl implements AuditLogService {
208 scope = extractParameter(String.class, 0, additionalInfo); 208 scope = extractParameter(String.class, 0, additionalInfo);
209 actionData.put("scope", scope); 209 actionData.put("scope", scope);
210 List<String> keys = extractParameter(List.class, 1, additionalInfo); 210 List<String> keys = extractParameter(List.class, 1, additionalInfo);
211 - ArrayNode attrsArrayNode = actionData.putArray("attributes"); 211 + ArrayNode attrsArrayNode = actionData.putArray("attributes");
212 if (keys != null) { 212 if (keys != null) {
213 keys.forEach(attrsArrayNode::add); 213 keys.forEach(attrsArrayNode::add);
214 } 214 }
@@ -18,14 +18,12 @@ package org.thingsboard.server.dao.audit; @@ -18,14 +18,12 @@ package org.thingsboard.server.dao.audit;
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
20 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
21 -import org.thingsboard.server.common.data.BaseData;  
22 import org.thingsboard.server.common.data.HasName; 21 import org.thingsboard.server.common.data.HasName;
23 import org.thingsboard.server.common.data.audit.ActionType; 22 import org.thingsboard.server.common.data.audit.ActionType;
24 import org.thingsboard.server.common.data.audit.AuditLog; 23 import org.thingsboard.server.common.data.audit.AuditLog;
25 import org.thingsboard.server.common.data.id.CustomerId; 24 import org.thingsboard.server.common.data.id.CustomerId;
26 import org.thingsboard.server.common.data.id.EntityId; 25 import org.thingsboard.server.common.data.id.EntityId;
27 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
28 -import org.thingsboard.server.common.data.id.UUIDBased;  
29 import org.thingsboard.server.common.data.id.UserId; 27 import org.thingsboard.server.common.data.id.UserId;
30 import org.thingsboard.server.common.data.page.TimePageData; 28 import org.thingsboard.server.common.data.page.TimePageData;
31 import org.thingsboard.server.common.data.page.TimePageLink; 29 import org.thingsboard.server.common.data.page.TimePageLink;
@@ -60,5 +58,4 @@ public class DummyAuditLogServiceImpl implements AuditLogService { @@ -60,5 +58,4 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
60 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { 58 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) {
61 return null; 59 return null;
62 } 60 }
63 -  
64 } 61 }
@@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
38 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 38 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
39 import org.thingsboard.server.dao.DaoUtil; 39 import org.thingsboard.server.dao.DaoUtil;
40 import org.thingsboard.server.dao.model.EntitySubtypeEntity; 40 import org.thingsboard.server.dao.model.EntitySubtypeEntity;
  41 +import org.thingsboard.server.dao.model.ModelConstants;
41 import org.thingsboard.server.dao.model.nosql.DeviceEntity; 42 import org.thingsboard.server.dao.model.nosql.DeviceEntity;
42 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; 43 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
43 import org.thingsboard.server.dao.relation.RelationDao; 44 import org.thingsboard.server.dao.relation.RelationDao;
@@ -199,6 +200,21 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt @@ -199,6 +200,21 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
199 } 200 }
200 201
201 @Override 202 @Override
  203 + public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
  204 + Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.TENANT_ID_PROPERTY, tenantId.getId())).and(eq(ModelConstants.ID_PROPERTY, id));
  205 + log.trace("Execute query {}", query);
  206 + DeviceEntity entity = findOneByStatement(tenantId, query);
  207 + return DaoUtil.getData(entity);
  208 + }
  209 +
  210 + @Override
  211 + public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
  212 + Select.Where query = select().from(getColumnFamilyName()).where(eq(ModelConstants.TENANT_ID_PROPERTY, tenantId.getId())).and(eq(ModelConstants.ID_PROPERTY, id));
  213 + log.trace("Execute query {}", query);
  214 + return findOneByStatementAsync(tenantId, query);
  215 + }
  216 +
  217 + @Override
202 public ListenableFuture<List<Device>> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { 218 public ListenableFuture<List<Device>> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
203 log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); 219 log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
204 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DEVICE, pageLink); 220 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DEVICE, pageLink);
@@ -118,6 +118,23 @@ public interface DeviceDao extends Dao<Device> { @@ -118,6 +118,23 @@ public interface DeviceDao extends Dao<Device> {
118 ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId); 118 ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
119 119
120 /** 120 /**
  121 + * Find devices by tenantId and device id.
  122 + * @param tenantId the tenant Id
  123 + * @param id the device Id
  124 + * @return the device object
  125 + */
  126 + Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id);
  127 +
  128 + /**
  129 + * Find devices by tenantId and device id.
  130 + * @param tenantId tenantId the tenantId
  131 + * @param id the deviceId
  132 + * @return the device object
  133 + */
  134 + ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
  135 +
  136 +
  137 + /**
121 * Find devices by tenantId, edgeId and page link. 138 * Find devices by tenantId, edgeId and page link.
122 * 139 *
123 * @param tenantId the tenantId 140 * @param tenantId the tenantId
@@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager; @@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager;
28 import org.springframework.cache.annotation.CacheEvict; 28 import org.springframework.cache.annotation.CacheEvict;
29 import org.springframework.cache.annotation.Cacheable; 29 import org.springframework.cache.annotation.Cacheable;
30 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
  31 +import org.springframework.transaction.annotation.Transactional;
  32 +import org.springframework.util.CollectionUtils;
31 import org.springframework.util.StringUtils; 33 import org.springframework.util.StringUtils;
32 import org.thingsboard.server.common.data.Customer; 34 import org.thingsboard.server.common.data.Customer;
33 import org.thingsboard.server.common.data.Device; 35 import org.thingsboard.server.common.data.Device;
@@ -54,6 +56,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; @@ -54,6 +56,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
54 import org.thingsboard.server.dao.customer.CustomerDao; 56 import org.thingsboard.server.dao.customer.CustomerDao;
55 import org.thingsboard.server.dao.edge.EdgeService; 57 import org.thingsboard.server.dao.edge.EdgeService;
56 import org.thingsboard.server.dao.entity.AbstractEntityService; 58 import org.thingsboard.server.dao.entity.AbstractEntityService;
  59 +import org.thingsboard.server.dao.event.EventService;
57 import org.thingsboard.server.dao.exception.DataValidationException; 60 import org.thingsboard.server.dao.exception.DataValidationException;
58 import org.thingsboard.server.dao.service.DataValidator; 61 import org.thingsboard.server.dao.service.DataValidator;
59 import org.thingsboard.server.dao.service.PaginatedRemover; 62 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -104,18 +107,29 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -104,18 +107,29 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
104 @Autowired 107 @Autowired
105 private CacheManager cacheManager; 108 private CacheManager cacheManager;
106 109
  110 + @Autowired
  111 + private EventService eventService;
  112 +
107 @Override 113 @Override
108 public Device findDeviceById(TenantId tenantId, DeviceId deviceId) { 114 public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
109 log.trace("Executing findDeviceById [{}]", deviceId); 115 log.trace("Executing findDeviceById [{}]", deviceId);
110 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); 116 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
111 - return deviceDao.findById(tenantId, deviceId.getId()); 117 + if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
  118 + return deviceDao.findById(tenantId, deviceId.getId());
  119 + } else {
  120 + return deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId());
  121 + }
112 } 122 }
113 123
114 @Override 124 @Override
115 public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) { 125 public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
116 log.trace("Executing findDeviceById [{}]", deviceId); 126 log.trace("Executing findDeviceById [{}]", deviceId);
117 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); 127 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
118 - return deviceDao.findByIdAsync(tenantId, deviceId.getId()); 128 + if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
  129 + return deviceDao.findByIdAsync(tenantId, deviceId.getId());
  130 + } else {
  131 + return deviceDao.findDeviceByTenantIdAndIdAsync(tenantId, deviceId.getId());
  132 + }
119 } 133 }
120 134
121 @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}") 135 @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}")
@@ -190,7 +204,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -190,7 +204,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
190 try { 204 try {
191 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get(); 205 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
192 if (entityViews != null && !entityViews.isEmpty()) { 206 if (entityViews != null && !entityViews.isEmpty()) {
193 - throw new DataValidationException("Can't delete device that is assigned to entity views!"); 207 + throw new DataValidationException("Can't delete device that has entity views!");
194 } 208 }
195 } catch (ExecutionException | InterruptedException e) { 209 } catch (ExecutionException | InterruptedException e) {
196 log.error("Exception while finding entity views for deviceId [{}]", deviceId, e); 210 log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
@@ -378,6 +392,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -378,6 +392,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
378 }, MoreExecutors.directExecutor()); 392 }, MoreExecutors.directExecutor());
379 } 393 }
380 394
  395 + @Transactional
  396 + @CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
  397 + @Override
  398 + public Device assignDeviceToTenant(TenantId tenantId, Device device) {
  399 + log.trace("Executing assignDeviceToTenant [{}][{}]", tenantId, device);
  400 +
  401 + try {
  402 + List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), device.getId()).get();
  403 + if (!CollectionUtils.isEmpty(entityViews)) {
  404 + throw new DataValidationException("Can't assign device that has entity views to another tenant!");
  405 + }
  406 + } catch (ExecutionException | InterruptedException e) {
  407 + log.error("Exception while finding entity views for deviceId [{}]", device.getId(), e);
  408 + throw new RuntimeException("Exception while finding entity views for deviceId [" + device.getId() + "]", e);
  409 + }
  410 +
  411 + eventService.removeEvents(device.getTenantId(), device.getId());
  412 +
  413 + relationService.removeRelations(device.getTenantId(), device.getId());
  414 +
  415 + device.setTenantId(tenantId);
  416 + device.setCustomerId(null);
  417 + return doSaveDevice(device, null);
  418 + }
  419 +
381 private DataValidator<Device> deviceValidator = 420 private DataValidator<Device> deviceValidator =
382 new DataValidator<Device>() { 421 new DataValidator<Device>() {
383 422
@@ -94,6 +94,21 @@ public class BaseEventService implements EventService { @@ -94,6 +94,21 @@ public class BaseEventService implements EventService {
94 return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit); 94 return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
95 } 95 }
96 96
  97 + @Override
  98 + public void removeEvents(TenantId tenantId, EntityId entityId) {
  99 + TimePageData<Event> eventPageData;
  100 + TimePageLink eventPageLink = new TimePageLink(1000);
  101 + do {
  102 + eventPageData = findEvents(tenantId, entityId, eventPageLink);
  103 + for (Event event : eventPageData.getData()) {
  104 + eventDao.removeById(tenantId, event.getUuidId());
  105 + }
  106 + if (eventPageData.hasNext()) {
  107 + eventPageLink = eventPageData.getNextPageLink();
  108 + }
  109 + } while (eventPageData.hasNext());
  110 + }
  111 +
97 private DataValidator<Event> eventValidator = 112 private DataValidator<Event> eventValidator =
98 new DataValidator<Event>() { 113 new DataValidator<Event>() {
99 @Override 114 @Override
@@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService { @@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService {
506 }, MoreExecutors.directExecutor()); 506 }, MoreExecutors.directExecutor());
507 } 507 }
508 508
  509 + @Override
  510 + public void removeRelations(TenantId tenantId, EntityId entityId) {
  511 + Cache cache = cacheManager.getCache(RELATIONS_CACHE);
  512 +
  513 + List<EntityRelation> relations = new ArrayList<>();
  514 + for (RelationTypeGroup relationTypeGroup : RelationTypeGroup.values()) {
  515 + relations.addAll(findByFrom(tenantId, entityId, relationTypeGroup));
  516 + relations.addAll(findByTo(tenantId, entityId, relationTypeGroup));
  517 + }
  518 +
  519 + for (EntityRelation relation : relations) {
  520 + cacheEviction(relation, cache);
  521 + deleteRelation(tenantId, relation);
  522 + }
  523 + }
  524 +
509 protected void validate(EntityRelation relation) { 525 protected void validate(EntityRelation relation) {
510 if (relation == null) { 526 if (relation == null) {
511 throw new DataValidationException("Relation type should be specified!"); 527 throw new DataValidationException("Relation type should be specified!");
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.dao.settings; 16 package org.thingsboard.server.dao.settings;
17 17
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils; 20 import org.apache.commons.lang3.StringUtils;
20 import org.springframework.beans.factory.annotation.Autowired; 21 import org.springframework.beans.factory.annotation.Autowired;
@@ -52,6 +53,13 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { @@ -52,6 +53,13 @@ public class AdminSettingsServiceImpl implements AdminSettingsService {
52 public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) { 53 public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) {
53 log.trace("Executing saveAdminSettings [{}]", adminSettings); 54 log.trace("Executing saveAdminSettings [{}]", adminSettings);
54 adminSettingsValidator.validate(adminSettings, data -> tenantId); 55 adminSettingsValidator.validate(adminSettings, data -> tenantId);
  56 + if (adminSettings.getKey().equals("mail") && "".equals(adminSettings.getJsonValue().get("password").asText())) {
  57 + AdminSettings mailSettings = findAdminSettingsByKey(tenantId, "mail");
  58 + if (mailSettings != null) {
  59 + ((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText());
  60 + }
  61 + }
  62 +
55 return adminSettingsDao.save(tenantId, adminSettings); 63 return adminSettingsDao.save(tenantId, adminSettings);
56 } 64 }
57 65
@@ -85,4 +85,7 @@ public interface DeviceRepository extends CrudRepository<DeviceEntity, String> { @@ -85,4 +85,7 @@ public interface DeviceRepository extends CrudRepository<DeviceEntity, String> {
85 List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(String tenantId, String customerId, List<String> deviceIds); 85 List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(String tenantId, String customerId, List<String> deviceIds);
86 86
87 List<DeviceEntity> findDevicesByTenantIdAndIdIn(String tenantId, List<String> deviceIds); 87 List<DeviceEntity> findDevicesByTenantIdAndIdIn(String tenantId, List<String> deviceIds);
  88 +
  89 + DeviceEntity findByTenantIdAndId(String tenantId, String id);
  90 +
88 } 91 }
@@ -151,6 +151,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> @@ -151,6 +151,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
151 return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(fromTimeUUID(tenantId)))); 151 return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(fromTimeUUID(tenantId))));
152 } 152 }
153 153
  154 + @Override
  155 + public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
  156 + return DaoUtil.getData(deviceRepository.findByTenantIdAndId(fromTimeUUID(tenantId.getId()), fromTimeUUID(id)));
  157 + }
  158 +
  159 + @Override
  160 + public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
  161 + return service.submit(() -> DaoUtil.getData(deviceRepository.findByTenantIdAndId(fromTimeUUID(tenantId.getId()), fromTimeUUID(id))));
  162 + }
  163 +
154 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) { 164 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
155 List<EntitySubtype> list = Collections.emptyList(); 165 List<EntitySubtype> list = Collections.emptyList();
156 if (types != null && !types.isEmpty()) { 166 if (types != null && !types.isEmpty()) {
@@ -46,6 +46,7 @@ public class JpaHsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa @@ -46,6 +46,7 @@ public class JpaHsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa
46 entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); 46 entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
47 entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); 47 entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
48 entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); 48 entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  49 + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
49 log.trace("Saving entity: {}", entity); 50 log.trace("Saving entity: {}", entity);
50 return tsQueue.add(entity); 51 return tsQueue.add(entity);
51 } 52 }
@@ -41,8 +41,8 @@ public class HsqlLatestInsertTsRepository extends AbstractInsertRepository imple @@ -41,8 +41,8 @@ public class HsqlLatestInsertTsRepository extends AbstractInsertRepository imple
41 "ON (ts_kv_latest.entity_id=T.entity_id " + 41 "ON (ts_kv_latest.entity_id=T.entity_id " +
42 "AND ts_kv_latest.key=T.key) " + 42 "AND ts_kv_latest.key=T.key) " +
43 "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v, ts_kv_latest.json_v = T.json_v " + 43 "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v, ts_kv_latest.json_v = T.json_v " +
44 - "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " +  
45 - "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; 44 + "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " +
  45 + "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v, T.json_v);";
46 46
47 @Override 47 @Override
48 public void saveOrUpdate(List<TsKvLatestEntity> entities) { 48 public void saveOrUpdate(List<TsKvLatestEntity> entities) {
@@ -114,7 +114,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @@ -114,7 +114,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
114 public User saveUser(User user) { 114 public User saveUser(User user) {
115 log.trace("Executing saveUser [{}]", user); 115 log.trace("Executing saveUser [{}]", user);
116 userValidator.validate(user, User::getTenantId); 116 userValidator.validate(user, User::getTenantId);
117 - if (user.getId() == null && !userLoginCaseSensitive) { 117 + if (!userLoginCaseSensitive) {
118 user.setEmail(user.getEmail().toLowerCase()); 118 user.setEmail(user.getEmail().toLowerCase());
119 } 119 }
120 User savedUser = userDao.save(user.getTenantId(), user); 120 User savedUser = userDao.save(user.getTenantId(), user);
@@ -28,6 +28,15 @@ public class JacksonUtil { @@ -28,6 +28,15 @@ public class JacksonUtil {
28 28
29 public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); 29 public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
30 30
  31 + public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
  32 + try {
  33 + return OBJECT_MAPPER.convertValue(fromValue, toValueType);
  34 + } catch (IllegalArgumentException e) {
  35 + throw new IllegalArgumentException("The given object value: "
  36 + + fromValue + " cannot be converted to " + toValueType);
  37 + }
  38 + }
  39 +
31 public static <T> T fromString(String string, Class<T> clazz) { 40 public static <T> T fromString(String string, Class<T> clazz) {
32 try { 41 try {
33 return OBJECT_MAPPER.readValue(string, clazz); 42 return OBJECT_MAPPER.readValue(string, clazz);
@@ -60,4 +69,4 @@ public class JacksonUtil { @@ -60,4 +69,4 @@ public class JacksonUtil {
60 public static <T> T clone(T value) { 69 public static <T> T clone(T value) {
61 return fromString(toString(value), (Class<T>) value.getClass()); 70 return fromString(toString(value), (Class<T>) value.getClass());
62 } 71 }
63 -}  
  72 +}
@@ -105,6 +105,7 @@ BEGIN @@ -105,6 +105,7 @@ BEGIN
105 AND tablename like 'ts_kv_' || '%' 105 AND tablename like 'ts_kv_' || '%'
106 AND tablename != 'ts_kv_latest' 106 AND tablename != 'ts_kv_latest'
107 AND tablename != 'ts_kv_dictionary' 107 AND tablename != 'ts_kv_dictionary'
  108 + AND tablename != 'ts_kv_indefinite'
108 LOOP 109 LOOP
109 IF partition != partition_by_max_ttl_date THEN 110 IF partition != partition_by_max_ttl_date THEN
110 IF partition_year IS NOT NULL THEN 111 IF partition_year IS NOT NULL THEN
@@ -13,6 +13,7 @@ DROP TABLE IF EXISTS relation; @@ -13,6 +13,7 @@ DROP TABLE IF EXISTS relation;
13 DROP TABLE IF EXISTS tb_user; 13 DROP TABLE IF EXISTS tb_user;
14 DROP TABLE IF EXISTS tenant; 14 DROP TABLE IF EXISTS tenant;
15 DROP TABLE IF EXISTS ts_kv; 15 DROP TABLE IF EXISTS ts_kv;
  16 +DROP TABLE IF EXISTS ts_kv_dictionary;
16 DROP TABLE IF EXISTS ts_kv_latest; 17 DROP TABLE IF EXISTS ts_kv_latest;
17 DROP TABLE IF EXISTS user_credentials; 18 DROP TABLE IF EXISTS user_credentials;
18 DROP TABLE IF EXISTS widget_type; 19 DROP TABLE IF EXISTS widget_type;
@@ -39,6 +39,9 @@ function additionalComposeQueueArgs() { @@ -39,6 +39,9 @@ function additionalComposeQueueArgs() {
39 kafka) 39 kafka)
40 ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.kafka.yml" 40 ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.kafka.yml"
41 ;; 41 ;;
  42 + confluent)
  43 + ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.confluent.yml"
  44 + ;;
42 aws-sqs) 45 aws-sqs)
43 ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.aws-sqs.yml" 46 ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.aws-sqs.yml"
44 ;; 47 ;;
@@ -52,7 +55,7 @@ function additionalComposeQueueArgs() { @@ -52,7 +55,7 @@ function additionalComposeQueueArgs() {
52 ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.service-bus.yml" 55 ADDITIONAL_COMPOSE_QUEUE_ARGS="-f docker-compose.service-bus.yml"
53 ;; 56 ;;
54 *) 57 *)
55 - echo "Unknown Queue service value specified: '${TB_QUEUE_TYPE}'. Should be either kafka or aws-sqs or pubsub or rabbitmq or service-bus." >&2 58 + echo "Unknown Queue service value specified: '${TB_QUEUE_TYPE}'. Should be either kafka or confluent or aws-sqs or pubsub or rabbitmq or service-bus." >&2
56 exit 1 59 exit 1
57 esac 60 esac
58 echo $ADDITIONAL_COMPOSE_QUEUE_ARGS 61 echo $ADDITIONAL_COMPOSE_QUEUE_ARGS
  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 +
  17 +version: '2.2'
  18 +
  19 +services:
  20 + tb-js-executor:
  21 + env_file:
  22 + - queue-confluent.env
  23 + tb-core1:
  24 + env_file:
  25 + - queue-confluent.env
  26 + depends_on:
  27 + - redis
  28 + tb-core2:
  29 + env_file:
  30 + - queue-confluent.env
  31 + depends_on:
  32 + - redis
  33 + tb-rule-engine1:
  34 + env_file:
  35 + - queue-confluent.env
  36 + depends_on:
  37 + - redis
  38 + tb-rule-engine2:
  39 + env_file:
  40 + - queue-confluent.env
  41 + depends_on:
  42 + - redis
  43 + tb-mqtt-transport1:
  44 + env_file:
  45 + - queue-confluent.env
  46 + tb-mqtt-transport2:
  47 + env_file:
  48 + - queue-confluent.env
  49 + tb-http-transport1:
  50 + env_file:
  51 + - queue-confluent.env
  52 + tb-http-transport2:
  53 + env_file:
  54 + - queue-confluent.env
  55 + tb-coap-transport:
  56 + env_file:
  57 + - queue-confluent.env
  1 +TB_QUEUE_TYPE=kafka
  2 +
  3 +TB_KAFKA_SERVERS=confluent.cloud:9092
  4 +TB_QUEUE_KAFKA_REPLICATION_FACTOR=3
  5 +
  6 +TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD=true
  7 +TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM=https
  8 +TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM=PLAIN
  9 +TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG=org.apache.kafka.common.security.plain.PlainLoginModule required username="CLUSTER_API_KEY" password="CLUSTER_API_SECRET";
  10 +TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL=SASL_SSL
  11 +TB_QUEUE_KAFKA_CONFLUENT_USERNAME=CLUSTER_API_KEY
  12 +TB_QUEUE_KAFKA_CONFLUENT_PASSWORD=CLUSTER_API_SECRET
  13 +
  14 +TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000
  15 +TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000
  16 +TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000
  17 +TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:52428800;retention.bytes:1048576000
  18 +TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:52428800;retention.bytes:104857600
1 TB_QUEUE_TYPE=kafka 1 TB_QUEUE_TYPE=kafka
2 TB_KAFKA_SERVERS=kafka:9092 2 TB_KAFKA_SERVERS=kafka:9092
  3 +TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 21
22 <parent> 22 <parent>
23 <groupId>org.thingsboard</groupId> 23 <groupId>org.thingsboard</groupId>
24 - <version>2.5.3-SNAPSHOT</version> 24 + <version>2.5.5-SNAPSHOT</version>
25 <artifactId>msa</artifactId> 25 <artifactId>msa</artifactId>
26 </parent> 26 </parent>
27 <groupId>org.thingsboard.msa</groupId> 27 <groupId>org.thingsboard.msa</groupId>
@@ -15,11 +15,15 @@ @@ -15,11 +15,15 @@
15 */ 15 */
16 package org.thingsboard.server.msa; 16 package org.thingsboard.server.msa;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.databind.JsonMappingException;
  20 +import com.fasterxml.jackson.databind.JsonNode;
18 import com.fasterxml.jackson.databind.ObjectMapper; 21 import com.fasterxml.jackson.databind.ObjectMapper;
19 import com.google.common.collect.ImmutableMap; 22 import com.google.common.collect.ImmutableMap;
20 import com.google.gson.JsonArray; 23 import com.google.gson.JsonArray;
21 import com.google.gson.JsonObject; 24 import com.google.gson.JsonObject;
22 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
  26 +import org.apache.cassandra.cql3.Json;
23 import org.apache.commons.lang3.RandomStringUtils; 27 import org.apache.commons.lang3.RandomStringUtils;
24 import org.apache.http.config.Registry; 28 import org.apache.http.config.Registry;
25 import org.apache.http.config.RegistryBuilder; 29 import org.apache.http.config.RegistryBuilder;
@@ -32,6 +36,7 @@ import org.apache.http.impl.client.HttpClients; @@ -32,6 +36,7 @@ import org.apache.http.impl.client.HttpClients;
32 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 36 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
33 import org.apache.http.ssl.SSLContextBuilder; 37 import org.apache.http.ssl.SSLContextBuilder;
34 import org.apache.http.ssl.SSLContexts; 38 import org.apache.http.ssl.SSLContexts;
  39 +import org.json.simple.JSONObject;
35 import org.junit.BeforeClass; 40 import org.junit.BeforeClass;
36 import org.junit.Rule; 41 import org.junit.Rule;
37 import org.junit.rules.TestRule; 42 import org.junit.rules.TestRule;
@@ -42,6 +47,7 @@ import org.thingsboard.rest.client.RestClient; @@ -42,6 +47,7 @@ import org.thingsboard.rest.client.RestClient;
42 import org.thingsboard.server.common.data.Device; 47 import org.thingsboard.server.common.data.Device;
43 import org.thingsboard.server.common.data.EntityType; 48 import org.thingsboard.server.common.data.EntityType;
44 import org.thingsboard.server.common.data.id.DeviceId; 49 import org.thingsboard.server.common.data.id.DeviceId;
  50 +import org.thingsboard.server.common.data.security.DeviceCredentials;
45 import org.thingsboard.server.msa.mapper.WsTelemetryResponse; 51 import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
46 52
47 53
@@ -52,6 +58,7 @@ import java.net.URI; @@ -52,6 +58,7 @@ import java.net.URI;
52 import java.security.cert.X509Certificate; 58 import java.security.cert.X509Certificate;
53 import java.util.List; 59 import java.util.List;
54 import java.util.Map; 60 import java.util.Map;
  61 +import java.util.Optional;
55 import java.util.Random; 62 import java.util.Random;
56 63
57 @Slf4j 64 @Slf4j
@@ -95,6 +102,17 @@ public abstract class AbstractContainerTest { @@ -95,6 +102,17 @@ public abstract class AbstractContainerTest {
95 } 102 }
96 }; 103 };
97 104
  105 + protected Device createGatewayDevice() throws JsonProcessingException {
  106 + String isGateway = "{\"gateway\":true}";
  107 + ObjectMapper objectMapper = new ObjectMapper();
  108 + JsonNode additionalInfo = objectMapper.readTree(isGateway);
  109 + Device gatewayDeviceTemplate = new Device();
  110 + gatewayDeviceTemplate.setName("mqtt_gateway");
  111 + gatewayDeviceTemplate.setType("gateway");
  112 + gatewayDeviceTemplate.setAdditionalInfo(additionalInfo);
  113 + return restClient.saveDevice(gatewayDeviceTemplate);
  114 + }
  115 +
98 protected Device createDevice(String name) { 116 protected Device createDevice(String name) {
99 return restClient.createDevice(name + RandomStringUtils.randomAlphanumeric(7), "DEFAULT"); 117 return restClient.createDevice(name + RandomStringUtils.randomAlphanumeric(7), "DEFAULT");
100 } 118 }
@@ -140,6 +158,27 @@ public abstract class AbstractContainerTest { @@ -140,6 +158,27 @@ public abstract class AbstractContainerTest {
140 return expectedValue.equals(list.get(1)); 158 return expectedValue.equals(list.get(1));
141 } 159 }
142 160
  161 + protected JsonObject createGatewayConnectPayload(String deviceName){
  162 + JsonObject payload = new JsonObject();
  163 + payload.addProperty("device", deviceName);
  164 + return payload;
  165 + }
  166 +
  167 + protected JsonObject createGatewayPayload(String deviceName, long ts){
  168 + JsonObject payload = new JsonObject();
  169 + payload.add(deviceName, createGatewayTelemetryArray(ts));
  170 + return payload;
  171 + }
  172 +
  173 + protected JsonArray createGatewayTelemetryArray(long ts){
  174 + JsonArray telemetryArray = new JsonArray();
  175 + if (ts > 0)
  176 + telemetryArray.add(createPayload(ts));
  177 + else
  178 + telemetryArray.add(createPayload());
  179 + return telemetryArray;
  180 + }
  181 +
143 protected JsonObject createPayload(long ts) { 182 protected JsonObject createPayload(long ts) {
144 JsonObject values = createPayload(); 183 JsonObject values = createPayload();
145 JsonObject payload = new JsonObject(); 184 JsonObject payload = new JsonObject();
  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.msa.connectivity;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import com.google.common.collect.Sets;
  21 +import com.google.common.util.concurrent.ListenableFuture;
  22 +import com.google.common.util.concurrent.ListeningExecutorService;
  23 +import com.google.common.util.concurrent.MoreExecutors;
  24 +import com.google.gson.JsonElement;
  25 +import com.google.gson.JsonObject;
  26 +import com.google.gson.JsonParser;
  27 +import io.netty.buffer.ByteBuf;
  28 +import io.netty.buffer.Unpooled;
  29 +import io.netty.handler.codec.mqtt.MqttQoS;
  30 +import lombok.Data;
  31 +import lombok.extern.slf4j.Slf4j;
  32 +import org.apache.commons.lang3.RandomStringUtils;
  33 +import org.checkerframework.checker.units.qual.A;
  34 +import org.junit.After;
  35 +import org.junit.Assert;
  36 +import org.junit.Before;
  37 +import org.junit.Test;
  38 +import org.springframework.core.ParameterizedTypeReference;
  39 +import org.springframework.http.HttpMethod;
  40 +import org.springframework.http.ResponseEntity;
  41 +import org.thingsboard.mqtt.MqttClient;
  42 +import org.thingsboard.mqtt.MqttClientConfig;
  43 +import org.thingsboard.mqtt.MqttHandler;
  44 +import org.thingsboard.server.common.data.Device;
  45 +import org.thingsboard.server.common.data.id.DeviceId;
  46 +import org.thingsboard.server.common.data.id.EntityId;
  47 +import org.thingsboard.server.common.data.id.RuleChainId;
  48 +import org.thingsboard.server.common.data.page.TextPageData;
  49 +import org.thingsboard.server.common.data.relation.EntityRelation;
  50 +import org.thingsboard.server.common.data.relation.EntityRelationInfo;
  51 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  52 +import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
  53 +import org.thingsboard.server.common.data.rule.RuleChain;
  54 +import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  55 +import org.thingsboard.server.common.data.rule.RuleNode;
  56 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  57 +import org.thingsboard.server.msa.AbstractContainerTest;
  58 +import org.thingsboard.server.msa.WsClient;
  59 +import org.thingsboard.server.msa.mapper.AttributesResponse;
  60 +import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
  61 +
  62 +import java.io.IOException;
  63 +import java.nio.charset.StandardCharsets;
  64 +import java.util.*;
  65 +import java.util.concurrent.*;
  66 +
  67 +@Slf4j
  68 +public class MqttGatewayClientTest extends AbstractContainerTest {
  69 + Device gatewayDevice;
  70 + MqttClient mqttClient;
  71 + Device createdDevice;
  72 + MqttMessageListener listener;
  73 +
  74 + @Before
  75 + public void createGateway() throws Exception {
  76 + restClient.login("tenant@thingsboard.org", "tenant");
  77 + this.gatewayDevice = createGatewayDevice();
  78 + Optional<DeviceCredentials> gatewayDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(gatewayDevice.getId());
  79 + Assert.assertTrue(gatewayDeviceCredentials.isPresent());
  80 + this.listener = new MqttMessageListener();
  81 + this.mqttClient = getMqttClient(gatewayDeviceCredentials.get(), listener);
  82 + this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice);
  83 + }
  84 +
  85 + @After
  86 + public void removeGateway() throws Exception {
  87 + restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.gatewayDevice.getId());
  88 + restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.createdDevice.getId());
  89 + this.listener = null;
  90 + this.mqttClient = null;
  91 + this.createdDevice = null;
  92 + }
  93 +
  94 + @Test
  95 + public void telemetryUpload() throws Exception {
  96 + WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
  97 + mqttClient.publish("v1/gateway/telemetry", Unpooled.wrappedBuffer(createGatewayPayload(createdDevice.getName(), -1).toString().getBytes())).get();
  98 + WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
  99 + log.info("Received telemetry: {}", actualLatestTelemetry);
  100 + wsClient.closeBlocking();
  101 +
  102 + Assert.assertEquals(4, actualLatestTelemetry.getData().size());
  103 + Assert.assertEquals(Sets.newHashSet("booleanKey", "stringKey", "doubleKey", "longKey"),
  104 + actualLatestTelemetry.getLatestValues().keySet());
  105 +
  106 + Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", Boolean.TRUE.toString()));
  107 + Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", "value1"));
  108 + Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", Double.toString(42.0)));
  109 + Assert.assertTrue(verify(actualLatestTelemetry, "longKey", Long.toString(73)));
  110 + }
  111 +
  112 + @Test
  113 + public void telemetryUploadWithTs() throws Exception {
  114 + long ts = 1451649600512L;
  115 +
  116 + restClient.login("tenant@thingsboard.org", "tenant");
  117 + WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
  118 + mqttClient.publish("v1/gateway/telemetry", Unpooled.wrappedBuffer(createGatewayPayload(createdDevice.getName(), ts).toString().getBytes())).get();
  119 + WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
  120 + log.info("Received telemetry: {}", actualLatestTelemetry);
  121 + wsClient.closeBlocking();
  122 +
  123 + Assert.assertEquals(4, actualLatestTelemetry.getData().size());
  124 + Assert.assertEquals(getExpectedLatestValues(ts), actualLatestTelemetry.getLatestValues());
  125 +
  126 + Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", ts, Boolean.TRUE.toString()));
  127 + Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", ts, "value1"));
  128 + Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", ts, Double.toString(42.0)));
  129 + Assert.assertTrue(verify(actualLatestTelemetry, "longKey", ts, Long.toString(73)));
  130 + }
  131 +
  132 + @Test
  133 + public void publishAttributeUpdateToServer() throws Exception {
  134 + Optional<DeviceCredentials> createdDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(createdDevice.getId());
  135 + Assert.assertTrue(createdDeviceCredentials.isPresent());
  136 + WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS);
  137 + JsonObject clientAttributes = new JsonObject();
  138 + clientAttributes.addProperty("attr1", "value1");
  139 + clientAttributes.addProperty("attr2", true);
  140 + clientAttributes.addProperty("attr3", 42.0);
  141 + clientAttributes.addProperty("attr4", 73);
  142 + JsonObject gatewayClientAttributes = new JsonObject();
  143 + gatewayClientAttributes.add(createdDevice.getName(), clientAttributes);
  144 + mqttClient.publish("v1/gateway/attributes", Unpooled.wrappedBuffer(gatewayClientAttributes.toString().getBytes())).get();
  145 + WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
  146 + log.info("Received attributes: {}", actualLatestTelemetry);
  147 + wsClient.closeBlocking();
  148 +
  149 + Assert.assertEquals(4, actualLatestTelemetry.getData().size());
  150 + Assert.assertEquals(Sets.newHashSet("attr1", "attr2", "attr3", "attr4"),
  151 + actualLatestTelemetry.getLatestValues().keySet());
  152 +
  153 + Assert.assertTrue(verify(actualLatestTelemetry, "attr1", "value1"));
  154 + Assert.assertTrue(verify(actualLatestTelemetry, "attr2", Boolean.TRUE.toString()));
  155 + Assert.assertTrue(verify(actualLatestTelemetry, "attr3", Double.toString(42.0)));
  156 + Assert.assertTrue(verify(actualLatestTelemetry, "attr4", Long.toString(73)));
  157 + }
  158 +
  159 + @Test
  160 + public void requestAttributeValuesFromServer() throws Exception {
  161 + WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS);
  162 + // Add a new client attribute
  163 + JsonObject clientAttributes = new JsonObject();
  164 + String clientAttributeValue = RandomStringUtils.randomAlphanumeric(8);
  165 + clientAttributes.addProperty("clientAttr", clientAttributeValue);
  166 +
  167 + JsonObject gatewayClientAttributes = new JsonObject();
  168 + gatewayClientAttributes.add(createdDevice.getName(), clientAttributes);
  169 + mqttClient.publish("v1/gateway/attributes", Unpooled.wrappedBuffer(gatewayClientAttributes.toString().getBytes())).get();
  170 +
  171 + WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
  172 + log.info("Received ws telemetry: {}", actualLatestTelemetry);
  173 + wsClient.closeBlocking();
  174 +
  175 + Assert.assertEquals(1, actualLatestTelemetry.getData().size());
  176 + Assert.assertEquals(Sets.newHashSet("clientAttr"),
  177 + actualLatestTelemetry.getLatestValues().keySet());
  178 +
  179 + Assert.assertTrue(verify(actualLatestTelemetry, "clientAttr", clientAttributeValue));
  180 +
  181 + // Add a new shared attribute
  182 + JsonObject sharedAttributes = new JsonObject();
  183 + String sharedAttributeValue = RandomStringUtils.randomAlphanumeric(8);
  184 + sharedAttributes.addProperty("sharedAttr", sharedAttributeValue);
  185 +
  186 + ResponseEntity sharedAttributesResponse = restClient.getRestTemplate()
  187 + .postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
  188 + mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
  189 + createdDevice.getId());
  190 + Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
  191 + MqttEvent sharedAttributeEvent = listener.getEvents().poll(10, TimeUnit.SECONDS);
  192 +
  193 + // Catch attribute update event
  194 + Assert.assertNotNull(sharedAttributeEvent);
  195 + Assert.assertEquals("v1/gateway/attributes", sharedAttributeEvent.getTopic());
  196 +
  197 + // Subscribe to attributes response
  198 + mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
  199 +
  200 + // Wait until subscription is processed
  201 + TimeUnit.SECONDS.sleep(3);
  202 +
  203 + checkAttribute(true, clientAttributeValue);
  204 + checkAttribute(false, sharedAttributeValue);
  205 + }
  206 +
  207 + @Test
  208 + public void subscribeToAttributeUpdatesFromServer() throws Exception {
  209 + mqttClient.on("v1/gateway/attributes", listener, MqttQoS.AT_LEAST_ONCE).get();
  210 + // Wait until subscription is processed
  211 + TimeUnit.SECONDS.sleep(3);
  212 + String sharedAttributeName = "sharedAttr";
  213 + // Add a new shared attribute
  214 +
  215 + JsonObject sharedAttributes = new JsonObject();
  216 + String sharedAttributeValue = RandomStringUtils.randomAlphanumeric(8);
  217 + sharedAttributes.addProperty(sharedAttributeName, sharedAttributeValue);
  218 +
  219 + JsonObject gatewaySharedAttributeValue = new JsonObject();
  220 + gatewaySharedAttributeValue.addProperty("device", createdDevice.getName());
  221 + gatewaySharedAttributeValue.add("data", sharedAttributes);
  222 +
  223 + ResponseEntity sharedAttributesResponse = restClient.getRestTemplate()
  224 + .postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
  225 + mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
  226 + createdDevice.getId());
  227 + Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
  228 +
  229 + MqttEvent event = listener.getEvents().poll(10, TimeUnit.SECONDS);
  230 + Assert.assertEquals(sharedAttributeValue,
  231 + mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText());
  232 +
  233 + // Update the shared attribute value
  234 + JsonObject updatedSharedAttributes = new JsonObject();
  235 + String updatedSharedAttributeValue = RandomStringUtils.randomAlphanumeric(8);
  236 + updatedSharedAttributes.addProperty(sharedAttributeName, updatedSharedAttributeValue);
  237 +
  238 + JsonObject gatewayUpdatedSharedAttributeValue = new JsonObject();
  239 + gatewayUpdatedSharedAttributeValue.addProperty("device", createdDevice.getName());
  240 + gatewayUpdatedSharedAttributeValue.add("data", updatedSharedAttributes);
  241 +
  242 + ResponseEntity updatedSharedAttributesResponse = restClient.getRestTemplate()
  243 + .postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
  244 + mapper.readTree(updatedSharedAttributes.toString()), ResponseEntity.class,
  245 + createdDevice.getId());
  246 + Assert.assertTrue(updatedSharedAttributesResponse.getStatusCode().is2xxSuccessful());
  247 +
  248 + event = listener.getEvents().poll(10, TimeUnit.SECONDS);
  249 + Assert.assertEquals(updatedSharedAttributeValue,
  250 + mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText());
  251 + }
  252 +
  253 + @Test
  254 + public void serverSideRpc() throws Exception {
  255 + String gatewayRpcTopic = "v1/gateway/rpc";
  256 + mqttClient.on(gatewayRpcTopic, listener, MqttQoS.AT_LEAST_ONCE).get();
  257 +
  258 + // Wait until subscription is processed
  259 + TimeUnit.SECONDS.sleep(3);
  260 +
  261 + // Send an RPC from the server
  262 + JsonObject serverRpcPayload = new JsonObject();
  263 + serverRpcPayload.addProperty("method", "getValue");
  264 + serverRpcPayload.addProperty("params", true);
  265 + ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
  266 + ListenableFuture<ResponseEntity> future = service.submit(() -> {
  267 + try {
  268 + return restClient.getRestTemplate()
  269 + .postForEntity(HTTPS_URL + "/api/plugins/rpc/twoway/{deviceId}",
  270 + mapper.readTree(serverRpcPayload.toString()), String.class,
  271 + createdDevice.getId());
  272 + } catch (IOException e) {
  273 + return ResponseEntity.badRequest().build();
  274 + }
  275 + });
  276 +
  277 + // Wait for RPC call from the server and send the response
  278 + MqttEvent requestFromServer = listener.getEvents().poll(10, TimeUnit.SECONDS);
  279 +
  280 + Assert.assertNotNull(requestFromServer);
  281 + Assert.assertNotNull(requestFromServer.getMessage());
  282 +
  283 + JsonObject requestFromServerJson = new JsonParser().parse(requestFromServer.getMessage()).getAsJsonObject();
  284 +
  285 + Assert.assertEquals(createdDevice.getName(), requestFromServerJson.get("device").getAsString());
  286 +
  287 + JsonObject requestFromServerData = requestFromServerJson.get("data").getAsJsonObject();
  288 +
  289 + Assert.assertEquals("getValue", requestFromServerData.get("method").getAsString());
  290 + Assert.assertTrue(requestFromServerData.get("params").getAsBoolean());
  291 +
  292 + int requestId = requestFromServerData.get("id").getAsInt();
  293 +
  294 + JsonObject clientResponse = new JsonObject();
  295 + clientResponse.addProperty("response", "someResponse");
  296 + JsonObject gatewayResponse = new JsonObject();
  297 + gatewayResponse.addProperty("device", createdDevice.getName());
  298 + gatewayResponse.addProperty("id", requestId);
  299 + gatewayResponse.add("data", clientResponse);
  300 + // Send a response to the server's RPC request
  301 +
  302 + mqttClient.publish(gatewayRpcTopic, Unpooled.wrappedBuffer(gatewayResponse.toString().getBytes())).get();
  303 + ResponseEntity serverResponse = future.get(5, TimeUnit.SECONDS);
  304 + Assert.assertTrue(serverResponse.getStatusCode().is2xxSuccessful());
  305 + Assert.assertEquals(clientResponse.toString(), serverResponse.getBody());
  306 + }
  307 +
  308 + private void checkAttribute(boolean client, String expectedValue) throws Exception{
  309 + JsonObject gatewayAttributesRequest = new JsonObject();
  310 + int messageId = new Random().nextInt(100);
  311 + gatewayAttributesRequest.addProperty("id", messageId);
  312 + gatewayAttributesRequest.addProperty("device", createdDevice.getName());
  313 + gatewayAttributesRequest.addProperty("client", client);
  314 + String attributeName;
  315 + if (client)
  316 + attributeName = "clientAttr";
  317 + else
  318 + attributeName = "sharedAttr";
  319 + gatewayAttributesRequest.addProperty("key", attributeName);
  320 + log.info(gatewayAttributesRequest.toString());
  321 + mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(gatewayAttributesRequest.toString().getBytes())).get();
  322 + MqttEvent clientAttributeEvent = listener.getEvents().poll(10, TimeUnit.SECONDS);
  323 + Assert.assertNotNull(clientAttributeEvent);
  324 + JsonObject responseMessage = new JsonParser().parse(Objects.requireNonNull(clientAttributeEvent).getMessage()).getAsJsonObject();
  325 +
  326 + Assert.assertEquals(messageId, responseMessage.get("id").getAsInt());
  327 + Assert.assertEquals(createdDevice.getName(), responseMessage.get("device").getAsString());
  328 + Assert.assertEquals(3, responseMessage.entrySet().size());
  329 + Assert.assertEquals(expectedValue, responseMessage.get("value").getAsString());
  330 + }
  331 +
  332 + private Device createDeviceThroughGateway(MqttClient mqttClient, Device gatewayDevice) throws Exception {
  333 + String deviceName = "mqtt_device";
  334 + mqttClient.publish("v1/gateway/connect", Unpooled.wrappedBuffer(createGatewayConnectPayload(deviceName).toString().getBytes())).get();
  335 +
  336 + TimeUnit.SECONDS.sleep(3);
  337 + List<EntityRelation> relations = restClient.findByFrom(gatewayDevice.getId(), RelationTypeGroup.COMMON);
  338 +
  339 + Assert.assertEquals(1, relations.size());
  340 +
  341 + EntityId createdEntityId = relations.get(0).getTo();
  342 + DeviceId createdDeviceId = new DeviceId(createdEntityId.getId());
  343 + Optional<Device> createdDevice = restClient.getDeviceById(createdDeviceId);
  344 +
  345 + Assert.assertTrue(createdDevice.isPresent());
  346 +
  347 + return createdDevice.get();
  348 + }
  349 +
  350 + private MqttClient getMqttClient(DeviceCredentials deviceCredentials, MqttMessageListener listener) throws InterruptedException, ExecutionException {
  351 + MqttClientConfig clientConfig = new MqttClientConfig();
  352 + clientConfig.setClientId("MQTT client from test");
  353 + clientConfig.setUsername(deviceCredentials.getCredentialsId());
  354 + MqttClient mqttClient = MqttClient.create(clientConfig, listener);
  355 + mqttClient.connect("localhost", 1883).get();
  356 + return mqttClient;
  357 + }
  358 +
  359 + @Data
  360 + private class MqttMessageListener implements MqttHandler {
  361 + private final BlockingQueue<MqttEvent> events;
  362 +
  363 + private MqttMessageListener() {
  364 + events = new ArrayBlockingQueue<>(100);
  365 + }
  366 +
  367 + @Override
  368 + public void onMessage(String topic, ByteBuf message) {
  369 + log.info("MQTT message [{}], topic [{}]", message.toString(StandardCharsets.UTF_8), topic);
  370 + events.add(new MqttEvent(topic, message.toString(StandardCharsets.UTF_8)));
  371 + }
  372 +
  373 + }
  374 +
  375 + @Data
  376 + private class MqttEvent {
  377 + private final String topic;
  378 + private final String message;
  379 + }
  380 +
  381 +
  382 +}
@@ -26,6 +26,12 @@ kafka: @@ -26,6 +26,12 @@ kafka:
26 servers: "TB_KAFKA_SERVERS" 26 servers: "TB_KAFKA_SERVERS"
27 replication_factor: "TB_QUEUE_KAFKA_REPLICATION_FACTOR" 27 replication_factor: "TB_QUEUE_KAFKA_REPLICATION_FACTOR"
28 topic_properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES" 28 topic_properties: "TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES"
  29 + use_confluent_cloud: "TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD"
  30 + confluent:
  31 + sasl:
  32 + mechanism: "TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM"
  33 + username: "TB_QUEUE_KAFKA_CONFLUENT_USERNAME"
  34 + password: "TB_QUEUE_KAFKA_CONFLUENT_PASSWORD"
29 35
30 pubsub: 36 pubsub:
31 project_id: "TB_QUEUE_PUBSUB_PROJECT_ID" 37 project_id: "TB_QUEUE_PUBSUB_PROJECT_ID"
@@ -25,7 +25,11 @@ kafka: @@ -25,7 +25,11 @@ kafka:
25 # Kafka Bootstrap Servers 25 # Kafka Bootstrap Servers
26 servers: "localhost:9092" 26 servers: "localhost:9092"
27 replication_factor: "1" 27 replication_factor: "1"
28 - topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600" 28 + topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100"
  29 + use_confluent_cloud: false
  30 + confluent:
  31 + sasl:
  32 + mechanism: "PLAIN"
29 33
30 pubsub: 34 pubsub:
31 queue_properties: "ackDeadlineInSec:30;messageRetentionInSec:604800" 35 queue_properties: "ackDeadlineInSec:30;messageRetentionInSec:604800"
@@ -1872,12 +1872,14 @@ @@ -1872,12 +1872,14 @@
1872 "balanced-match": { 1872 "balanced-match": {
1873 "version": "1.0.0", 1873 "version": "1.0.0",
1874 "bundled": true, 1874 "bundled": true,
1875 - "dev": true 1875 + "dev": true,
  1876 + "optional": true
1876 }, 1877 },
1877 "brace-expansion": { 1878 "brace-expansion": {
1878 "version": "1.1.11", 1879 "version": "1.1.11",
1879 "bundled": true, 1880 "bundled": true,
1880 "dev": true, 1881 "dev": true,
  1882 + "optional": true,
1881 "requires": { 1883 "requires": {
1882 "balanced-match": "^1.0.0", 1884 "balanced-match": "^1.0.0",
1883 "concat-map": "0.0.1" 1885 "concat-map": "0.0.1"
@@ -1892,17 +1894,20 @@ @@ -1892,17 +1894,20 @@
1892 "code-point-at": { 1894 "code-point-at": {
1893 "version": "1.1.0", 1895 "version": "1.1.0",
1894 "bundled": true, 1896 "bundled": true,
1895 - "dev": true 1897 + "dev": true,
  1898 + "optional": true
1896 }, 1899 },
1897 "concat-map": { 1900 "concat-map": {
1898 "version": "0.0.1", 1901 "version": "0.0.1",
1899 "bundled": true, 1902 "bundled": true,
1900 - "dev": true 1903 + "dev": true,
  1904 + "optional": true
1901 }, 1905 },
1902 "console-control-strings": { 1906 "console-control-strings": {
1903 "version": "1.1.0", 1907 "version": "1.1.0",
1904 "bundled": true, 1908 "bundled": true,
1905 - "dev": true 1909 + "dev": true,
  1910 + "optional": true
1906 }, 1911 },
1907 "core-util-is": { 1912 "core-util-is": {
1908 "version": "1.0.2", 1913 "version": "1.0.2",
@@ -2019,7 +2024,8 @@ @@ -2019,7 +2024,8 @@
2019 "inherits": { 2024 "inherits": {
2020 "version": "2.0.3", 2025 "version": "2.0.3",
2021 "bundled": true, 2026 "bundled": true,
2022 - "dev": true 2027 + "dev": true,
  2028 + "optional": true
2023 }, 2029 },
2024 "ini": { 2030 "ini": {
2025 "version": "1.3.5", 2031 "version": "1.3.5",
@@ -2031,6 +2037,7 @@ @@ -2031,6 +2037,7 @@
2031 "version": "1.0.0", 2037 "version": "1.0.0",
2032 "bundled": true, 2038 "bundled": true,
2033 "dev": true, 2039 "dev": true,
  2040 + "optional": true,
2034 "requires": { 2041 "requires": {
2035 "number-is-nan": "^1.0.0" 2042 "number-is-nan": "^1.0.0"
2036 } 2043 }
@@ -2045,6 +2052,7 @@ @@ -2045,6 +2052,7 @@
2045 "version": "3.0.4", 2052 "version": "3.0.4",
2046 "bundled": true, 2053 "bundled": true,
2047 "dev": true, 2054 "dev": true,
  2055 + "optional": true,
2048 "requires": { 2056 "requires": {
2049 "brace-expansion": "^1.1.7" 2057 "brace-expansion": "^1.1.7"
2050 } 2058 }
@@ -2156,7 +2164,8 @@ @@ -2156,7 +2164,8 @@
2156 "number-is-nan": { 2164 "number-is-nan": {
2157 "version": "1.0.1", 2165 "version": "1.0.1",
2158 "bundled": true, 2166 "bundled": true,
2159 - "dev": true 2167 + "dev": true,
  2168 + "optional": true
2160 }, 2169 },
2161 "object-assign": { 2170 "object-assign": {
2162 "version": "4.1.1", 2171 "version": "4.1.1",
@@ -2168,6 +2177,7 @@ @@ -2168,6 +2177,7 @@
2168 "version": "1.4.0", 2177 "version": "1.4.0",
2169 "bundled": true, 2178 "bundled": true,
2170 "dev": true, 2179 "dev": true,
  2180 + "optional": true,
2171 "requires": { 2181 "requires": {
2172 "wrappy": "1" 2182 "wrappy": "1"
2173 } 2183 }
@@ -2289,6 +2299,7 @@ @@ -2289,6 +2299,7 @@
2289 "version": "1.0.2", 2299 "version": "1.0.2",
2290 "bundled": true, 2300 "bundled": true,
2291 "dev": true, 2301 "dev": true,
  2302 + "optional": true,
2292 "requires": { 2303 "requires": {
2293 "code-point-at": "^1.0.0", 2304 "code-point-at": "^1.0.0",
2294 "is-fullwidth-code-point": "^1.0.0", 2305 "is-fullwidth-code-point": "^1.0.0",
1 { 1 {
2 "name": "thingsboard-js-executor", 2 "name": "thingsboard-js-executor",
3 "private": true, 3 "private": true,
4 - "version": "2.5.3", 4 + "version": "2.5.5",
5 "description": "ThingsBoard JavaScript Executor Microservice", 5 "description": "ThingsBoard JavaScript Executor Microservice",
6 "main": "server.js", 6 "main": "server.js",
7 "bin": "server.js", 7 "bin": "server.js",
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>msa</artifactId> 24 <artifactId>msa</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa</groupId> 26 <groupId>org.thingsboard.msa</groupId>
@@ -34,7 +34,7 @@ function KafkaProducer() { @@ -34,7 +34,7 @@ function KafkaProducer() {
34 this.send = async (responseTopic, scriptId, rawResponse, headers) => { 34 this.send = async (responseTopic, scriptId, rawResponse, headers) => {
35 35
36 if (!topics.includes(responseTopic)) { 36 if (!topics.includes(responseTopic)) {
37 - let createResponseTopicResult = await createTopic(responseTopic); 37 + let createResponseTopicResult = await createTopic(responseTopic, 1);
38 topics.push(responseTopic); 38 topics.push(responseTopic);
39 if (createResponseTopicResult) { 39 if (createResponseTopicResult) {
40 logger.info('Created new topic: %s', requestTopic); 40 logger.info('Created new topic: %s', requestTopic);
@@ -61,22 +61,45 @@ function KafkaProducer() { @@ -61,22 +61,45 @@ function KafkaProducer() {
61 61
62 const kafkaBootstrapServers = config.get('kafka.bootstrap.servers'); 62 const kafkaBootstrapServers = config.get('kafka.bootstrap.servers');
63 const requestTopic = config.get('request_topic'); 63 const requestTopic = config.get('request_topic');
  64 + const useConfluent = config.get('kafka.use_confluent_cloud');
64 65
65 logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers); 66 logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers);
66 logger.info('Kafka Requests Topic: %s', requestTopic); 67 logger.info('Kafka Requests Topic: %s', requestTopic);
67 68
68 - kafkaClient = new Kafka({ 69 + let kafkaConfig = {
69 brokers: kafkaBootstrapServers.split(','), 70 brokers: kafkaBootstrapServers.split(','),
70 - logLevel: logLevel.INFO,  
71 - logCreator: KafkaJsWinstonLogCreator  
72 - }); 71 + logLevel: logLevel.INFO,
  72 + logCreator: KafkaJsWinstonLogCreator
  73 + };
  74 +
  75 + if (useConfluent) {
  76 + kafkaConfig['sasl'] = {
  77 + mechanism: config.get('kafka.confluent.sasl.mechanism'),
  78 + username: config.get('kafka.confluent.username'),
  79 + password: config.get('kafka.confluent.password')
  80 + };
  81 + kafkaConfig['ssl'] = true;
  82 + }
  83 +
  84 + kafkaClient = new Kafka(kafkaConfig);
73 85
74 parseTopicProperties(); 86 parseTopicProperties();
75 87
76 kafkaAdmin = kafkaClient.admin(); 88 kafkaAdmin = kafkaClient.admin();
77 await kafkaAdmin.connect(); 89 await kafkaAdmin.connect();
78 90
79 - let createRequestTopicResult = await createTopic(requestTopic); 91 + let partitions = 1;
  92 +
  93 + for (let i = 0; i < configEntries.length; i++) {
  94 + let param = configEntries[i];
  95 + if (param.name === 'partitions') {
  96 + partitions = param.value;
  97 + configEntries.splice(i, 1);
  98 + break;
  99 + }
  100 + }
  101 +
  102 + let createRequestTopicResult = await createTopic(requestTopic, partitions);
80 103
81 if (createRequestTopicResult) { 104 if (createRequestTopicResult) {
82 logger.info('Created new topic: %s', requestTopic); 105 logger.info('Created new topic: %s', requestTopic);
@@ -109,10 +132,11 @@ function KafkaProducer() { @@ -109,10 +132,11 @@ function KafkaProducer() {
109 } 132 }
110 })(); 133 })();
111 134
112 -function createTopic(topic) { 135 +function createTopic(topic, partitions) {
113 return kafkaAdmin.createTopics({ 136 return kafkaAdmin.createTopics({
114 topics: [{ 137 topics: [{
115 topic: topic, 138 topic: topic,
  139 + numPartitions: partitions,
116 replicationFactor: replicationFactor, 140 replicationFactor: replicationFactor,
117 configEntries: configEntries 141 configEntries: configEntries
118 }] 142 }]
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>msa</artifactId> 26 <artifactId>msa</artifactId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>msa</artifactId> 24 <artifactId>msa</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa</groupId> 26 <groupId>org.thingsboard.msa</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>msa</artifactId> 24 <artifactId>msa</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa</groupId> 26 <groupId>org.thingsboard.msa</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.msa</groupId> 22 <groupId>org.thingsboard.msa</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa.transport</groupId> 26 <groupId>org.thingsboard.msa.transport</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.msa</groupId> 22 <groupId>org.thingsboard.msa</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa.transport</groupId> 26 <groupId>org.thingsboard.msa.transport</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard.msa</groupId> 22 <groupId>org.thingsboard.msa</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa.transport</groupId> 26 <groupId>org.thingsboard.msa.transport</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>msa</artifactId> 24 <artifactId>msa</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa</groupId> 26 <groupId>org.thingsboard.msa</groupId>
1 { 1 {
2 "name": "thingsboard-web-ui", 2 "name": "thingsboard-web-ui",
3 "private": true, 3 "private": true,
4 - "version": "2.5.3", 4 + "version": "2.5.5",
5 "description": "ThingsBoard Web UI Microservice", 5 "description": "ThingsBoard Web UI Microservice",
6 "main": "server.js", 6 "main": "server.js",
7 "bin": "server.js", 7 "bin": "server.js",
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <artifactId>msa</artifactId> 24 <artifactId>msa</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.msa</groupId> 26 <groupId>org.thingsboard.msa</groupId>