Commit c526d13e453cc8acd07dcca96598abc06ae6e4d6

Authored by Volodymyr Babak
2 parents 4686b232 3f9f6efc

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

Showing 130 changed files with 1488 additions and 503 deletions
@@ -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>
@@ -19,11 +19,11 @@ @@ -19,11 +19,11 @@
19 <modelVersion>4.0.0</modelVersion> 19 <modelVersion>4.0.0</modelVersion>
20 <parent> 20 <parent>
21 <groupId>org.thingsboard</groupId> 21 <groupId>org.thingsboard</groupId>
22 - <version>2.5.3-SNAPSHOT</version> 22 + <version>2.5.5-SNAPSHOT</version>
23 <artifactId>thingsboard</artifactId> 23 <artifactId>thingsboard</artifactId>
24 </parent> 24 </parent>
25 <artifactId>netty-mqtt</artifactId> 25 <artifactId>netty-mqtt</artifactId>
26 - <version>2.5.3-SNAPSHOT</version> 26 + <version>2.5.5-SNAPSHOT</version>
27 <packaging>jar</packaging> 27 <packaging>jar</packaging>
28 28
29 <name>Netty MQTT Client</name> 29 <name>Netty MQTT Client</name>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <groupId>org.thingsboard</groupId> 21 <groupId>org.thingsboard</groupId>
22 <artifactId>thingsboard</artifactId> 22 <artifactId>thingsboard</artifactId>
23 - <version>2.5.3-SNAPSHOT</version> 23 + <version>2.5.5-SNAPSHOT</version>
24 <packaging>pom</packaging> 24 <packaging>pom</packaging>
25 25
26 <name>Thingsboard</name> 26 <name>Thingsboard</name>
@@ -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>rest-client</artifactId> 26 <artifactId>rest-client</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>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>rule-engine</artifactId> 26 <artifactId>rule-engine</artifactId>
@@ -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>rule-engine</artifactId> 26 <artifactId>rule-engine</artifactId>
27 </parent> 27 </parent>
28 <groupId>org.thingsboard.rule-engine</groupId> 28 <groupId>org.thingsboard.rule-engine</groupId>
@@ -17,11 +17,15 @@ package org.thingsboard.rule.engine.api.util; @@ -17,11 +17,15 @@ package org.thingsboard.rule.engine.api.util;
17 17
18 import com.fasterxml.jackson.core.JsonProcessingException; 18 import com.fasterxml.jackson.core.JsonProcessingException;
19 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import org.springframework.util.CollectionUtils;
20 import org.thingsboard.rule.engine.api.TbNodeConfiguration; 21 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
21 import org.thingsboard.rule.engine.api.TbNodeException; 22 import org.thingsboard.rule.engine.api.TbNodeException;
22 import org.thingsboard.server.common.msg.TbMsgMetaData; 23 import org.thingsboard.server.common.msg.TbMsgMetaData;
23 24
  25 +import java.util.Collections;
  26 +import java.util.List;
24 import java.util.Map; 27 import java.util.Map;
  28 +import java.util.stream.Collectors;
25 29
26 /** 30 /**
27 * Created by ashvayka on 19.01.18. 31 * Created by ashvayka on 19.01.18.
@@ -41,6 +45,13 @@ public class TbNodeUtils { @@ -41,6 +45,13 @@ public class TbNodeUtils {
41 } 45 }
42 } 46 }
43 47
  48 + public static List<String> processPatterns(List<String> patterns, TbMsgMetaData metaData) {
  49 + if (!CollectionUtils.isEmpty(patterns)) {
  50 + return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList());
  51 + }
  52 + return Collections.emptyList();
  53 + }
  54 +
44 public static String processPattern(String pattern, TbMsgMetaData metaData) { 55 public static String processPattern(String pattern, TbMsgMetaData metaData) {
45 String result = new String(pattern); 56 String result = new String(pattern);
46 for (Map.Entry<String,String> keyVal : metaData.values().entrySet()) { 57 for (Map.Entry<String,String> keyVal : metaData.values().entrySet()) {
@@ -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>rule-engine</artifactId> 26 <artifactId>rule-engine</artifactId>
27 </parent> 27 </parent>
28 <groupId>org.thingsboard.rule-engine</groupId> 28 <groupId>org.thingsboard.rule-engine</groupId>
@@ -18,15 +18,16 @@ package org.thingsboard.rule.engine.action; @@ -18,15 +18,16 @@ package org.thingsboard.rule.engine.action;
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import com.google.common.util.concurrent.Futures; 19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
21 -import com.google.common.util.concurrent.MoreExecutors;  
22 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
23 import org.thingsboard.rule.engine.api.RuleNode; 22 import org.thingsboard.rule.engine.api.RuleNode;
24 import org.thingsboard.rule.engine.api.TbContext; 23 import org.thingsboard.rule.engine.api.TbContext;
25 import org.thingsboard.rule.engine.api.TbNodeConfiguration; 24 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
26 import org.thingsboard.rule.engine.api.TbNodeException; 25 import org.thingsboard.rule.engine.api.TbNodeException;
27 import org.thingsboard.rule.engine.api.util.TbNodeUtils; 26 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  27 +import org.thingsboard.server.common.data.EntityType;
28 import org.thingsboard.server.common.data.alarm.Alarm; 28 import org.thingsboard.server.common.data.alarm.Alarm;
29 import org.thingsboard.server.common.data.alarm.AlarmStatus; 29 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  30 +import org.thingsboard.server.common.data.id.AlarmId;
30 import org.thingsboard.server.common.data.plugin.ComponentType; 31 import org.thingsboard.server.common.data.plugin.ComponentType;
31 import org.thingsboard.server.common.data.rule.RuleChainType; 32 import org.thingsboard.server.common.data.rule.RuleChainType;
32 import org.thingsboard.server.common.msg.TbMsg; 33 import org.thingsboard.server.common.msg.TbMsg;
@@ -56,8 +57,13 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig @@ -56,8 +57,13 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
56 @Override 57 @Override
57 protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) { 58 protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
58 String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData()); 59 String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData());
59 - ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);  
60 - return Futures.transformAsync(latest, a -> { 60 + ListenableFuture<Alarm> alarmFuture;
  61 + if (msg.getOriginator().getEntityType().equals(EntityType.ALARM)) {
  62 + alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId()));
  63 + } else {
  64 + alarmFuture = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
  65 + }
  66 + return Futures.transformAsync(alarmFuture, a -> {
61 if (a != null && !a.getStatus().isCleared()) { 67 if (a != null && !a.getStatus().isCleared()) {
62 return clearAlarm(ctx, msg, a); 68 return clearAlarm(ctx, msg, a);
63 } 69 }
@@ -16,8 +16,13 @@ @@ -16,8 +16,13 @@
16 package org.thingsboard.rule.engine.filter; 16 package org.thingsboard.rule.engine.filter;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
  20 +import org.thingsboard.rule.engine.api.RuleNode;
  21 +import org.thingsboard.rule.engine.api.TbContext;
  22 +import org.thingsboard.rule.engine.api.TbNode;
  23 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  24 +import org.thingsboard.rule.engine.api.TbNodeException;
19 import org.thingsboard.rule.engine.api.util.TbNodeUtils; 25 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
20 -import org.thingsboard.rule.engine.api.*;  
21 import org.thingsboard.server.common.data.DataConstants; 26 import org.thingsboard.server.common.data.DataConstants;
22 import org.thingsboard.server.common.data.plugin.ComponentType; 27 import org.thingsboard.server.common.data.plugin.ComponentType;
23 import org.thingsboard.server.common.data.rule.RuleChainType; 28 import org.thingsboard.server.common.data.rule.RuleChainType;
@@ -31,7 +36,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -31,7 +36,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
31 configClazz = EmptyNodeConfiguration.class, 36 configClazz = EmptyNodeConfiguration.class,
32 relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event", 37 relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event",
33 "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", 38 "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned",
34 - "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other"}, 39 + "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant"},
35 nodeDescription = "Route incoming messages by Message Type", 40 nodeDescription = "Route incoming messages by Message Type",
36 nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.", 41 nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.",
37 uiResources = {"static/rulenode/rulenode-core-config.js"}, 42 uiResources = {"static/rulenode/rulenode-core-config.js"},
@@ -83,6 +88,10 @@ public class TbMsgTypeSwitchNode implements TbNode { @@ -83,6 +88,10 @@ public class TbMsgTypeSwitchNode implements TbNode {
83 relationType = "Alarm Cleared"; 88 relationType = "Alarm Cleared";
84 } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) { 89 } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) {
85 relationType = "RPC Request to Device"; 90 relationType = "RPC Request to Device";
  91 + } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_FROM_TENANT)) {
  92 + relationType = "Entity Assigned From Tenant";
  93 + } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_TO_TENANT)) {
  94 + relationType = "Entity Assigned To Tenant";
86 } else { 95 } else {
87 relationType = "Other"; 96 relationType = "Other";
88 } 97 }
@@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbContext; @@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbContext;
29 import org.thingsboard.rule.engine.api.TbNode; 29 import org.thingsboard.rule.engine.api.TbNode;
30 import org.thingsboard.rule.engine.api.TbNodeConfiguration; 30 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
31 import org.thingsboard.rule.engine.api.TbNodeException; 31 import org.thingsboard.rule.engine.api.TbNodeException;
  32 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
32 import org.thingsboard.server.common.data.id.EntityId; 33 import org.thingsboard.server.common.data.id.EntityId;
33 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 34 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
34 import org.thingsboard.server.common.data.kv.KvEntry; 35 import org.thingsboard.server.common.data.kv.KvEntry;
@@ -91,10 +92,10 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC @@ -91,10 +92,10 @@ public abstract class TbAbstractGetAttributesNode<C extends TbGetAttributesNodeC
91 } 92 }
92 ConcurrentHashMap<String, List<String>> failuresMap = new ConcurrentHashMap<>(); 93 ConcurrentHashMap<String, List<String>> failuresMap = new ConcurrentHashMap<>();
93 ListenableFuture<List<Void>> allFutures = Futures.allAsList( 94 ListenableFuture<List<Void>> allFutures = Futures.allAsList(
94 - putLatestTelemetry(ctx, entityId, msg, LATEST_TS, config.getLatestTsKeyNames(), failuresMap),  
95 - putAttrAsync(ctx, entityId, msg, CLIENT_SCOPE, config.getClientAttributeNames(), failuresMap, "cs_"),  
96 - putAttrAsync(ctx, entityId, msg, SHARED_SCOPE, config.getSharedAttributeNames(), failuresMap, "shared_"),  
97 - putAttrAsync(ctx, entityId, msg, SERVER_SCOPE, config.getServerAttributeNames(), failuresMap, "ss_") 95 + putLatestTelemetry(ctx, entityId, msg, LATEST_TS, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg.getMetaData()), failuresMap),
  96 + putAttrAsync(ctx, entityId, msg, CLIENT_SCOPE, TbNodeUtils.processPatterns(config.getClientAttributeNames(), msg.getMetaData()), failuresMap, "cs_"),
  97 + putAttrAsync(ctx, entityId, msg, SHARED_SCOPE, TbNodeUtils.processPatterns(config.getSharedAttributeNames(), msg.getMetaData()), failuresMap, "shared_"),
  98 + putAttrAsync(ctx, entityId, msg, SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg.getMetaData()), failuresMap, "ss_")
98 ); 99 );
99 withCallback(allFutures, i -> { 100 withCallback(allFutures, i -> {
100 if (!failuresMap.isEmpty()) { 101 if (!failuresMap.isEmpty()) {
@@ -105,9 +105,10 @@ public class TbGetTelemetryNode implements TbNode { @@ -105,9 +105,10 @@ public class TbGetTelemetryNode implements TbNode {
105 if (config.isUseMetadataIntervalPatterns()) { 105 if (config.isUseMetadataIntervalPatterns()) {
106 checkMetadataKeyPatterns(msg); 106 checkMetadataKeyPatterns(msg);
107 } 107 }
108 - ListenableFuture<List<TsKvEntry>> list = ctx.getTimeseriesService().findAll(ctx.getTenantId(), msg.getOriginator(), buildQueries(msg)); 108 + List<String> keys = TbNodeUtils.processPatterns(tsKeyNames, msg.getMetaData());
  109 + ListenableFuture<List<TsKvEntry>> list = ctx.getTimeseriesService().findAll(ctx.getTenantId(), msg.getOriginator(), buildQueries(msg, keys));
109 DonAsynchron.withCallback(list, data -> { 110 DonAsynchron.withCallback(list, data -> {
110 - process(data, msg); 111 + process(data, msg, keys);
111 ctx.tellSuccess(ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), msg.getData())); 112 ctx.tellSuccess(ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), msg.getData()));
112 }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor()); 113 }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor());
113 } catch (Exception e) { 114 } catch (Exception e) {
@@ -120,8 +121,8 @@ public class TbGetTelemetryNode implements TbNode { @@ -120,8 +121,8 @@ public class TbGetTelemetryNode implements TbNode {
120 public void destroy() { 121 public void destroy() {
121 } 122 }
122 123
123 - private List<ReadTsKvQuery> buildQueries(TbMsg msg) {  
124 - return tsKeyNames.stream() 124 + private List<ReadTsKvQuery> buildQueries(TbMsg msg, List<String> keys) {
  125 + return keys.stream()
125 .map(key -> new BaseReadTsKvQuery(key, getInterval(msg).getStartTs(), getInterval(msg).getEndTs(), 1, limit, NONE, getOrderBy())) 126 .map(key -> new BaseReadTsKvQuery(key, getInterval(msg).getStartTs(), getInterval(msg).getEndTs(), 1, limit, NONE, getOrderBy()))
126 .collect(Collectors.toList()); 127 .collect(Collectors.toList());
127 } 128 }
@@ -137,7 +138,7 @@ public class TbGetTelemetryNode implements TbNode { @@ -137,7 +138,7 @@ public class TbGetTelemetryNode implements TbNode {
137 } 138 }
138 } 139 }
139 140
140 - private void process(List<TsKvEntry> entries, TbMsg msg) { 141 + private void process(List<TsKvEntry> entries, TbMsg msg, List<String> keys) {
141 ObjectNode resultNode = mapper.createObjectNode(); 142 ObjectNode resultNode = mapper.createObjectNode();
142 if (FETCH_MODE_ALL.equals(fetchMode)) { 143 if (FETCH_MODE_ALL.equals(fetchMode)) {
143 entries.forEach(entry -> processArray(resultNode, entry)); 144 entries.forEach(entry -> processArray(resultNode, entry));
@@ -145,7 +146,7 @@ public class TbGetTelemetryNode implements TbNode { @@ -145,7 +146,7 @@ public class TbGetTelemetryNode implements TbNode {
145 entries.forEach(entry -> processSingle(resultNode, entry)); 146 entries.forEach(entry -> processSingle(resultNode, entry));
146 } 147 }
147 148
148 - for (String key : tsKeyNames) { 149 + for (String key : keys) {
149 if (resultNode.has(key)) { 150 if (resultNode.has(key)) {
150 msg.getMetaData().putValue(key, resultNode.get(key).toString()); 151 msg.getMetaData().putValue(key, resultNode.get(key).toString());
151 } 152 }
@@ -35,6 +35,7 @@ import org.thingsboard.rule.engine.api.TbContext; @@ -35,6 +35,7 @@ import org.thingsboard.rule.engine.api.TbContext;
35 import org.thingsboard.rule.engine.api.TbNodeConfiguration; 35 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
36 import org.thingsboard.rule.engine.api.TbNodeException; 36 import org.thingsboard.rule.engine.api.TbNodeException;
37 import org.thingsboard.server.common.data.alarm.Alarm; 37 import org.thingsboard.server.common.data.alarm.Alarm;
  38 +import org.thingsboard.server.common.data.id.AlarmId;
38 import org.thingsboard.server.common.data.id.DeviceId; 39 import org.thingsboard.server.common.data.id.DeviceId;
39 import org.thingsboard.server.common.data.id.EntityId; 40 import org.thingsboard.server.common.data.id.EntityId;
40 import org.thingsboard.server.common.data.id.RuleChainId; 41 import org.thingsboard.server.common.data.id.RuleChainId;
@@ -95,6 +96,7 @@ public class TbAlarmNodeTest { @@ -95,6 +96,7 @@ public class TbAlarmNodeTest {
95 private ListeningExecutor dbExecutor; 96 private ListeningExecutor dbExecutor;
96 97
97 private EntityId originator = new DeviceId(UUIDs.timeBased()); 98 private EntityId originator = new DeviceId(UUIDs.timeBased());
  99 + private EntityId alarmOriginator = new AlarmId(UUIDs.timeBased());
98 private TenantId tenantId = new TenantId(UUIDs.timeBased()); 100 private TenantId tenantId = new TenantId(UUIDs.timeBased());
99 private TbMsgMetaData metaData = new TbMsgMetaData(); 101 private TbMsgMetaData metaData = new TbMsgMetaData();
100 private String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; 102 private String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
@@ -325,6 +327,55 @@ public class TbAlarmNodeTest { @@ -325,6 +327,55 @@ public class TbAlarmNodeTest {
325 assertEquals(expectedAlarm, actualAlarm); 327 assertEquals(expectedAlarm, actualAlarm);
326 } 328 }
327 329
  330 + @Test
  331 + public void alarmCanBeClearedWithAlarmOriginator() throws ScriptException, IOException {
  332 + initWithClearAlarmScript();
  333 + metaData.putValue("key", "value");
  334 + TbMsg msg = TbMsg.newMsg( "USER", alarmOriginator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
  335 +
  336 + long oldEndDate = System.currentTimeMillis();
  337 + AlarmId id = new AlarmId(alarmOriginator.getId());
  338 + Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).status(ACTIVE_UNACK).severity(WARNING).endTs(oldEndDate).build();
  339 + activeAlarm.setId(id);
  340 +
  341 + when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
  342 + when(alarmService.findAlarmByIdAsync(tenantId, id)).thenReturn(Futures.immediateFuture(activeAlarm));
  343 + when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), org.mockito.Mockito.any(JsonNode.class), anyLong())).thenReturn(Futures.immediateFuture(true));
  344 +// doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(activeAlarm);
  345 +
  346 + node.onMsg(ctx, msg);
  347 +
  348 + verify(ctx).tellNext(any(), eq("Cleared"));
  349 +
  350 + ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
  351 + ArgumentCaptor<String> typeCaptor = ArgumentCaptor.forClass(String.class);
  352 + ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
  353 + ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
  354 + ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
  355 + verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
  356 +
  357 + assertEquals("ALARM", typeCaptor.getValue());
  358 + assertEquals(alarmOriginator, originatorCaptor.getValue());
  359 + assertEquals("value", metadataCaptor.getValue().getValue("key"));
  360 + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_CLEARED_ALARM));
  361 + assertNotSame(metaData, metadataCaptor.getValue());
  362 +
  363 + Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class);
  364 + Alarm expectedAlarm = Alarm.builder()
  365 + .tenantId(tenantId)
  366 + .originator(originator)
  367 + .status(CLEARED_UNACK)
  368 + .severity(WARNING)
  369 + .propagate(false)
  370 + .type("SomeType")
  371 + .details(null)
  372 + .endTs(oldEndDate)
  373 + .build();
  374 + expectedAlarm.setId(id);
  375 +
  376 + assertEquals(expectedAlarm, actualAlarm);
  377 + }
  378 +
328 private void initWithCreateAlarmScript() { 379 private void initWithCreateAlarmScript() {
329 try { 380 try {
330 TbCreateAlarmNodeConfiguration config = new TbCreateAlarmNodeConfiguration(); 381 TbCreateAlarmNodeConfiguration config = new TbCreateAlarmNodeConfiguration();
@@ -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>tools</artifactId> 26 <artifactId>tools</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>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.transport</groupId> 26 <groupId>org.thingsboard.transport</groupId>
@@ -69,13 +69,21 @@ queue: @@ -69,13 +69,21 @@ queue:
69 linger.ms: "${TB_KAFKA_LINGER_MS:1}" 69 linger.ms: "${TB_KAFKA_LINGER_MS:1}"
70 buffer.memory: "${TB_BUFFER_MEMORY:33554432}" 70 buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
71 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" 71 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}"
  72 + use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}"
  73 + confluent:
  74 + ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}"
  75 + sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
  76 + 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\";}"
  77 + security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
  78 + other:
72 topic-properties: 79 topic-properties:
73 - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
74 - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
75 - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
76 - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
77 - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" 80 + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  81 + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  82 + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  83 + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  84 + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
78 aws_sqs: 85 aws_sqs:
  86 + use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
79 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" 87 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
80 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" 88 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
81 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" 89 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
@@ -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>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.transport</groupId> 26 <groupId>org.thingsboard.transport</groupId>
@@ -62,13 +62,21 @@ queue: @@ -62,13 +62,21 @@ queue:
62 linger.ms: "${TB_KAFKA_LINGER_MS:1}" 62 linger.ms: "${TB_KAFKA_LINGER_MS:1}"
63 buffer.memory: "${TB_BUFFER_MEMORY:33554432}" 63 buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
64 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" 64 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}"
  65 + use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}"
  66 + confluent:
  67 + ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}"
  68 + sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
  69 + 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\";}"
  70 + security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
  71 + other:
65 topic-properties: 72 topic-properties:
66 - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
67 - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
68 - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
69 - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
70 - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" 73 + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  74 + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  75 + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  76 + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  77 + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
71 aws_sqs: 78 aws_sqs:
  79 + use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
72 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" 80 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
73 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" 81 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
74 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" 82 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
@@ -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>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.transport</groupId> 26 <groupId>org.thingsboard.transport</groupId>
@@ -90,13 +90,21 @@ queue: @@ -90,13 +90,21 @@ queue:
90 linger.ms: "${TB_KAFKA_LINGER_MS:1}" 90 linger.ms: "${TB_KAFKA_LINGER_MS:1}"
91 buffer.memory: "${TB_BUFFER_MEMORY:33554432}" 91 buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
92 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" 92 replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}"
  93 + use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}"
  94 + confluent:
  95 + ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}"
  96 + sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}"
  97 + 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\";}"
  98 + security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
  99 + other:
93 topic-properties: 100 topic-properties:
94 - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
95 - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
96 - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
97 - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
98 - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" 101 + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  102 + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  103 + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  104 + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  105 + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
99 aws_sqs: 106 aws_sqs:
  107 + use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
100 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" 108 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
101 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" 109 secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}"
102 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" 110 region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}"
@@ -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>transport</artifactId> 26 <artifactId>transport</artifactId>
1 { 1 {
2 "name": "thingsboard", 2 "name": "thingsboard",
3 "private": true, 3 "private": true,
4 - "version": "2.5.3", 4 + "version": "2.5.5",
5 "description": "ThingsBoard UI", 5 "description": "ThingsBoard UI",
6 "licenses": [ 6 "licenses": [
7 { 7 {
@@ -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 <groupId>org.thingsboard</groupId> 26 <groupId>org.thingsboard</groupId>
@@ -123,16 +123,16 @@ @@ -123,16 +123,16 @@
123 </md-input-container> 123 </md-input-container>
124 <md-input-container class="md-block"> 124 <md-input-container class="md-block">
125 <label translate>admin.proxy-password</label> 125 <label translate>admin.proxy-password</label>
126 - <input name="proxyPassword" ng-model="vm.settings.jsonValue.proxyPassword"> 126 + <input name="proxyPassword" type="password" autocomplete="new-password" ng-model="vm.settings.jsonValue.proxyPassword">
127 </md-input-container> 127 </md-input-container>
128 </section> 128 </section>
129 <md-input-container class="md-block"> 129 <md-input-container class="md-block">
130 <label translate>common.username</label> 130 <label translate>common.username</label>
131 - <input name="username" placeholder="{{ 'common.enter-username' | translate }}" ng-model="vm.settings.jsonValue.username"> 131 + <input placeholder="{{ 'common.enter-username' | translate }}" ng-model="vm.settings.jsonValue.username" autocomplete="new-username" >
132 </md-input-container> 132 </md-input-container>
133 <md-input-container class="md-block"> 133 <md-input-container class="md-block">
134 <label translate>common.password</label> 134 <label translate>common.password</label>
135 - <input name="password" placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password"> 135 + <input placeholder="{{ 'common.enter-password' | translate }}" type="password" ng-model="vm.settings.jsonValue.password" autocomplete="new-password">
136 </md-input-container> 136 </md-input-container>
137 <div layout="row" layout-align="end center" width="100%" layout-wrap> 137 <div layout="row" layout-align="end center" width="100%" layout-wrap>
138 <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button> 138 <md-button ng-disabled="$root.loading || vm.settingsForm.$invalid" ng-click="vm.sendTestMail()" class="md-raised">{{'admin.send-test-mail' | translate}}</md-button>
@@ -228,7 +228,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) @@ -228,7 +228,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService)
228 } 228 }
229 var deleteEntityAttributesPromise; 229 var deleteEntityAttributesPromise;
230 if (deleteAttributes.length) { 230 if (deleteAttributes.length) {
231 - deleteEntityAttributesPromise = deleteEntityAttributes(entityType, entityId, attributeScope, deleteAttributes); 231 + deleteEntityAttributesPromise = deleteEntityAttributes(entityType, entityId, attributeScope, deleteAttributes, config);
232 } 232 }
233 if (Object.keys(attributesData).length) { 233 if (Object.keys(attributesData).length) {
234 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope; 234 var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/' + attributeScope;
@@ -223,6 +223,12 @@ export default angular.module('thingsboard.types', []) @@ -223,6 +223,12 @@ export default angular.module('thingsboard.types', [])
223 "LOCKOUT": { 223 "LOCKOUT": {
224 name: "audit-log.type-lockout" 224 name: "audit-log.type-lockout"
225 }, 225 },
  226 + "ASSIGNED_FROM_TENANT": {
  227 + name: "audit-log.type-assigned-from-tenant"
  228 + },
  229 + "ASSIGNED_TO_TENANT": {
  230 + name: "audit-log.type-assigned-to-tenant"
  231 + },
226 "ASSIGNED_TO_EDGE": { 232 "ASSIGNED_TO_EDGE": {
227 name: "audit-log.type-assigned-to-edge" 233 name: "audit-log.type-assigned-to-edge"
228 }, 234 },
@@ -150,7 +150,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t @@ -150,7 +150,8 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
150 customTranslation: customTranslation, 150 customTranslation: customTranslation,
151 objToBase64: objToBase64, 151 objToBase64: objToBase64,
152 base64toObj: base64toObj, 152 base64toObj: base64toObj,
153 - loadImageAspect: loadImageAspect 153 + loadImageAspect: loadImageAspect,
  154 + sortObjectKeys: sortObjectKeys
154 } 155 }
155 156
156 return service; 157 return service;
@@ -605,4 +606,14 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t @@ -605,4 +606,14 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
605 return deferred.promise; 606 return deferred.promise;
606 } 607 }
607 608
  609 + function sortObjectKeys(obj) {
  610 + var sortedObj = {};
  611 + var keys = Object.keys(obj).sort();
  612 + for (var i = 0; i < keys.length; i++) {
  613 + var key = keys[i];
  614 + sortedObj[key] = obj[key];
  615 + }
  616 + return sortedObj;
  617 + }
  618 +
608 } 619 }
@@ -27,7 +27,7 @@ import eventRowEdgeEventTemplate from './event-row-edge-event.tpl.html'; @@ -27,7 +27,7 @@ import eventRowEdgeEventTemplate from './event-row-edge-event.tpl.html';
27 27
28 /*@ngInject*/ 28 /*@ngInject*/
29 export default function EventRowDirective($compile, $templateCache, $mdDialog, $document, $translate, 29 export default function EventRowDirective($compile, $templateCache, $mdDialog, $document, $translate,
30 - types, toast, entityService, ruleChainService) { 30 + types, utils, toast, entityService, ruleChainService) {
31 31
32 var linker = function (scope, element, attrs) { 32 var linker = function (scope, element, attrs) {
33 33
@@ -76,11 +76,18 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ @@ -76,11 +76,18 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
76 if (!contentType) { 76 if (!contentType) {
77 contentType = null; 77 contentType = null;
78 } 78 }
  79 + var sortedContent;
  80 + try {
  81 + sortedContent = angular.toJson(utils.sortObjectKeys(angular.fromJson(content)));
  82 + }
  83 + catch(err) {
  84 + sortedContent = content;
  85 + }
79 $mdDialog.show({ 86 $mdDialog.show({
80 controller: 'EventContentDialogController', 87 controller: 'EventContentDialogController',
81 controllerAs: 'vm', 88 controllerAs: 'vm',
82 templateUrl: eventErrorDialogTemplate, 89 templateUrl: eventErrorDialogTemplate,
83 - locals: {content: content, title: title, contentType: contentType, showingCallback: onShowingCallback}, 90 + locals: {content: sortedContent, title: title, contentType: contentType, showingCallback: onShowingCallback},
84 parent: angular.element($document[0].body), 91 parent: angular.element($document[0].body),
85 fullscreen: true, 92 fullscreen: true,
86 targetEvent: $event, 93 targetEvent: $event,
@@ -44,6 +44,7 @@ var ruleNodeClazzHelpLinkMap = { @@ -44,6 +44,7 @@ var ruleNodeClazzHelpLinkMap = {
44 'org.thingsboard.rule.engine.aws.sqs.TbSqsNode': 'ruleNodeAwsSqs', 44 'org.thingsboard.rule.engine.aws.sqs.TbSqsNode': 'ruleNodeAwsSqs',
45 'org.thingsboard.rule.engine.kafka.TbKafkaNode': 'ruleNodeKafka', 45 'org.thingsboard.rule.engine.kafka.TbKafkaNode': 'ruleNodeKafka',
46 'org.thingsboard.rule.engine.mqtt.TbMqttNode': 'ruleNodeMqtt', 46 'org.thingsboard.rule.engine.mqtt.TbMqttNode': 'ruleNodeMqtt',
  47 + 'org.thingsboard.rule.engine.mqtt.azure.TbAzureIotHubNode': 'ruleNodeAzureIotHub',
47 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode': 'ruleNodeRabbitMq', 48 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode': 'ruleNodeRabbitMq',
48 'org.thingsboard.rule.engine.rest.TbRestApiCallNode': 'ruleNodeRestApiCall', 49 'org.thingsboard.rule.engine.rest.TbRestApiCallNode': 'ruleNodeRestApiCall',
49 'org.thingsboard.rule.engine.mail.TbSendEmailNode': 'ruleNodeSendEmail' 50 'org.thingsboard.rule.engine.mail.TbSendEmailNode': 'ruleNodeSendEmail'
@@ -88,6 +89,7 @@ export default angular.module('thingsboard.help', []) @@ -88,6 +89,7 @@ export default angular.module('thingsboard.help', [])
88 ruleNodeAwsSqs: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#aws-sqs-node", 89 ruleNodeAwsSqs: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#aws-sqs-node",
89 ruleNodeKafka: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#kafka-node", 90 ruleNodeKafka: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#kafka-node",
90 ruleNodeMqtt: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#mqtt-node", 91 ruleNodeMqtt: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#mqtt-node",
  92 + ruleNodeAzureIotHub: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#azure-iot-hub-node",
91 ruleNodeRabbitMq: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#rabbitmq-node", 93 ruleNodeRabbitMq: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#rabbitmq-node",
92 ruleNodeRestApiCall: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#rest-api-call-node", 94 ruleNodeRestApiCall: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#rest-api-call-node",
93 ruleNodeSendEmail: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#send-email-node", 95 ruleNodeSendEmail: helpBaseUrl + "/docs/user-guide/rule-engine-2-0/external-nodes/#send-email-node",
@@ -373,7 +373,9 @@ @@ -373,7 +373,9 @@
373 "action-data": "Action data", 373 "action-data": "Action data",
374 "failure-details": "Failure details", 374 "failure-details": "Failure details",
375 "search": "Search audit logs", 375 "search": "Search audit logs",
376 - "clear-search": "Clear search" 376 + "clear-search": "Clear search",
  377 + "type-assigned-from-tenant": "Assigned from Tenant",
  378 + "type-assigned-to-tenant": "Assigned to Tenant"
377 }, 379 },
378 "confirm-on-exit": { 380 "confirm-on-exit": {
379 "message": "You have unsaved changes. Are you sure you want to leave this page?", 381 "message": "You have unsaved changes. Are you sure you want to leave this page?",
@@ -20,7 +20,7 @@ import nodeScriptTestTemplate from './node-script-test.tpl.html'; @@ -20,7 +20,7 @@ import nodeScriptTestTemplate from './node-script-test.tpl.html';
20 /* eslint-enable import/no-unresolved, import/default */ 20 /* eslint-enable import/no-unresolved, import/default */
21 21
22 /*@ngInject*/ 22 /*@ngInject*/
23 -export default function NodeScriptTest($q, $mdDialog, $document, ruleChainService) { 23 +export default function NodeScriptTest($q, $mdDialog, $document, ruleChainService, utils) {
24 24
25 var service = { 25 var service = {
26 testNodeScript: testNodeScript 26 testNodeScript: testNodeScript
@@ -89,6 +89,8 @@ export default function NodeScriptTest($q, $mdDialog, $document, ruleChainServic @@ -89,6 +89,8 @@ export default function NodeScriptTest($q, $mdDialog, $document, ruleChainServic
89 deviceName: "Test Device", 89 deviceName: "Test Device",
90 ts: new Date().getTime() + "" 90 ts: new Date().getTime() + ""
91 }; 91 };
  92 + } else {
  93 + metadata = utils.sortObjectKeys(metadata);
92 } 94 }
93 if (!msgType) { 95 if (!msgType) {
94 msgType = "POST_TELEMETRY_REQUEST"; 96 msgType = "POST_TELEMETRY_REQUEST";