Browse Source

merge with develop/3.4

pull/6534/head
YevhenBondarenko 4 years ago
parent
commit
6c1031536d
  1. 6
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  2. 378
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  3. 2
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  4. 4
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  5. 2
      application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java
  6. 3
      application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java
  7. 48
      application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java
  8. 4
      application/src/main/java/org/thingsboard/server/service/entitiy/SimpleTbEntityService.java
  9. 17
      application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java
  10. 14
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
  11. 6
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
  12. 6
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
  13. 5
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java
  14. 18
      application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java
  15. 4
      application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java
  16. 275
      application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java
  17. 49
      application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/TbDashboardService.java
  18. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
  19. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java
  20. 8
      application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java
  21. 4
      application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java
  22. 73
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  23. 16
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
  24. 6
      application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
  25. 7
      application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java
  26. 8
      application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java
  27. 6
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java
  28. 2
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  29. 53
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  30. 14
      application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
  31. 16
      application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java
  32. 17
      application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
  33. 88
      application/src/test/java/org/thingsboard/server/transport/mqtt/MqttTestCallback.java
  34. 126
      application/src/test/java/org/thingsboard/server/transport/mqtt/MqttTestClient.java
  35. 653
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java
  36. 2
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/MqttAttributesRequestBackwardCompatibilityIntegrationTest.java
  37. 1
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/MqttAttributesRequestProtoIntegrationTest.java
  38. 18
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesBackwardCompatibilityIntegrationTest.java
  39. 11
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java
  40. 11
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesJsonIntegrationTest.java
  41. 14
      application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesProtoIntegrationTest.java
  42. 31
      application/src/test/java/org/thingsboard/server/transport/mqtt/claim/MqttClaimDeviceTest.java
  43. 5
      application/src/test/java/org/thingsboard/server/transport/mqtt/claim/MqttClaimProtoDeviceTest.java
  44. 65
      application/src/test/java/org/thingsboard/server/transport/mqtt/credentials/BasicMqttCredentialsTest.java
  45. 149
      application/src/test/java/org/thingsboard/server/transport/mqtt/provision/MqttProvisionJsonDeviceTest.java
  46. 114
      application/src/test/java/org/thingsboard/server/transport/mqtt/provision/MqttProvisionProtoDeviceTest.java
  47. 396
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
  48. 23
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcBackwardCompatibilityIntegrationTest.java
  49. 16
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcDefaultIntegrationTest.java
  50. 23
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcJsonIntegrationTest.java
  51. 17
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcProtoIntegrationTest.java
  52. 48
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/MqttAttributesIntegrationTest.java
  53. 10
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/MqttAttributesProtoIntegrationTest.java
  54. 172
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java
  55. 46
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java
  56. 87
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java
  57. 5
      dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java
  58. 3
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
  59. 10
      ui-ngx/src/app/core/api/alias-controller.ts
  60. 4
      ui-ngx/src/app/core/api/widget-api.models.ts
  61. 8
      ui-ngx/src/app/core/api/widget-subscription.ts
  62. 6
      ui-ngx/src/app/core/http/entity.service.ts
  63. 87
      ui-ngx/src/app/core/schema-utils.ts
  64. 19
      ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts
  65. 5
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts
  66. 60
      ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts
  67. 1
      ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts
  68. 1603
      ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts
  69. 6
      ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts
  70. 45
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
  71. 8
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.html
  72. 2
      ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts
  73. 3
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  74. 1
      ui-ngx/src/app/shared/models/widget.models.ts
  75. 1
      ui-ngx/src/assets/locale/locale.constant-en_US.json

6
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -32,22 +32,16 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.customer.TbCustomerService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES;

378
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -22,6 +22,7 @@ import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.Example;
import io.swagger.annotations.ExampleProperty;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@ -38,15 +39,12 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HomeDashboard;
import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
@ -55,6 +53,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.dashboard.TbDashboardService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@ -62,6 +61,7 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
@ -90,9 +90,11 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI
@RestController
@TbCoreComponent
@RequiredArgsConstructor
@RequestMapping("/api")
public class DashboardController extends BaseController {
private final TbDashboardService tbDashboardService;
public static final String DASHBOARD_ID = "dashboardId";
private static final String HOME_DASHBOARD_ID = "homeDashboardId";
@ -180,28 +182,9 @@ public class DashboardController extends BaseController {
public Dashboard saveDashboard(
@ApiParam(value = "A JSON value representing the dashboard.")
@RequestBody Dashboard dashboard) throws ThingsboardException {
try {
dashboard.setTenantId(getCurrentUser().getTenantId());
checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
logEntityAction(savedDashboard.getId(), savedDashboard,
null,
dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (dashboard.getId() != null) {
sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED);
}
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), dashboard,
null, dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e);
}
dashboard.setTenantId(getCurrentUser().getTenantId());
checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
return tbDashboardService.save(dashboard, getCurrentUser());
}
@ApiOperation(value = "Delete the Dashboard (deleteDashboard)",
@ -213,28 +196,9 @@ public class DashboardController extends BaseController {
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), dashboardId);
dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
logEntityAction(dashboardId, dashboard,
null,
ActionType.DELETED, null, strDashboardId);
sendDeleteNotificationMsg(getTenantId(), dashboardId, relatedEdgeIds);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD),
null,
null,
ActionType.DELETED, e, strDashboardId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
tbDashboardService.delete(dashboard, getCurrentUser());
}
@ApiOperation(value = "Assign the Dashboard (assignDashboardToCustomer)",
@ -251,30 +215,13 @@ public class DashboardController extends BaseController {
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName());
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId, Operation.READ);
sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId, strCustomerId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
return tbDashboardService.assignDashboardToCustomer(dashboardId, customer, getCurrentUser());
}
@ApiOperation(value = "Unassign the Dashboard (unassignDashboardFromCustomer)",
@ -291,29 +238,11 @@ public class DashboardController extends BaseController {
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName());
sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
Customer customer = checkCustomerId(customerId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
return tbDashboardService.unassignDashboardFromCustomer(dashboard, customer, getCurrentUser());
}
@ApiOperation(value = "Update the Dashboard Customers (updateDashboardCustomers)",
@ -331,69 +260,15 @@ public class DashboardController extends BaseController {
@ApiParam(value = "JSON array with the list of customer ids, or empty to remove all customers")
@RequestBody(required = false) String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
customerIds.add(new CustomerId(toUUID(strCustomerId)));
}
}
Set<CustomerId> addedCustomerIds = new HashSet<>();
Set<CustomerId> removedCustomerIds = new HashSet<>();
for (CustomerId customerId : customerIds) {
if (!dashboard.isAssignedToCustomer(customerId)) {
addedCustomerIds.add(customerId);
}
}
Set<ShortCustomerInfo> assignedCustomers = dashboard.getAssignedCustomers();
if (assignedCustomers != null) {
for (ShortCustomerInfo customerInfo : assignedCustomers) {
if (!customerIds.contains(customerInfo.getCustomerId())) {
removedCustomerIds.add(customerInfo.getCustomerId());
}
}
}
if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
}
for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds, dashboard);
return tbDashboardService.updateDashboardCustomers(dashboard, customerIds, getCurrentUser());
}
@ApiOperation(value = "Adds the Dashboard Customers (addDashboardCustomers)",
notes = "Adds the list of Customers to the existing list of assignments for the Dashboard. Keeps previous assignments to customers that are not in the provided list. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@ -405,42 +280,10 @@ public class DashboardController extends BaseController {
@ApiParam(value = "JSON array with the list of customer ids")
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
if (!dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
}
}
if (customerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds, dashboard);
return tbDashboardService.addDashboardCustomers(dashboard, customerIds, getCurrentUser());
}
@ApiOperation(value = "Remove the Dashboard Customers (removeDashboardCustomers)",
@ -457,42 +300,10 @@ public class DashboardController extends BaseController {
@ApiParam(value = "JSON array with the list of customer ids")
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
if (dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
}
}
if (customerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, customerId));
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Set<CustomerId> customerIds = customerIdFromStr(strCustomerIds, dashboard);
return tbDashboardService.removeDashboardCustomers(dashboard, customerIds, getCurrentUser());
}
@ApiOperation(value = "Assign the Dashboard to Public Customer (assignDashboardToPublicCustomer)",
@ -510,25 +321,9 @@ public class DashboardController extends BaseController {
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
logEntityAction(dashboardId, savedDashboard,
publicCustomer.getId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName());
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER);
return tbDashboardService.assignDashboardToPublicCustomer(dashboardId, getCurrentUser());
}
@ApiOperation(value = "Unassign the Dashboard from Public Customer (unassignDashboardFromPublicCustomer)",
@ -542,26 +337,9 @@ public class DashboardController extends BaseController {
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(getCurrentUser().getTenantId(), dashboardId, publicCustomer.getId()));
logEntityAction(dashboardId, dashboard,
publicCustomer.getId(),
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName());
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER);
return tbDashboardService.unassignDashboardFromPublicCustomer(dashboard, getCurrentUser());
}
@ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboards)",
@ -775,6 +553,7 @@ public class DashboardController extends BaseController {
public void setTenantHomeDashboardInfo(
@ApiParam(value = "A JSON object that represents home dashboard id and other parameters", required = true)
@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException {
try {
if (homeDashboardInfo.getDashboardId() != null) {
checkDashboardId(homeDashboardInfo.getDashboardId(), Operation.READ);
@ -847,30 +626,13 @@ public class DashboardController extends BaseController {
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.READ);
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, savedDashboard,
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName());
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDashboardId, strEdgeId);
throw handleException(e);
}
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.READ);
return tbDashboardService.asignDashboardToEdge(dashboardId, edge, getCurrentUser());
}
@ApiOperation(value = "Unassign dashboard from edge (unassignDashboardFromEdge)",
@ -886,37 +648,22 @@ public class DashboardController extends BaseController {
@ResponseBody
public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId,
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
checkParameter(EDGE_ID, strEdgeId);
checkParameter(DASHBOARD_ID, strDashboardId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, dashboard,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, strEdgeId, edge.getName());
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId, strEdgeId);
throw handleException(e);
}
return tbDashboardService.unassignDashboardFromEdge(dashboard, edge, getCurrentUser());
}
@ApiOperation(value = "Get Edge Dashboards (getEdgeDashboards)",
notes = "Returns a page of dashboard info objects assigned to the specified edge. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
notes = "Returns a page of dashboard info objects assigned to the specified edge. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
@ -957,4 +704,17 @@ public class DashboardController extends BaseController {
throw handleException(e);
}
}
private Set<CustomerId> customerIdFromStr(String [] strCustomerIds, Dashboard dashboard) {
Set<CustomerId> customerIds = new HashSet<>();
if (strCustomerIds != null) {
for (String strCustomerId : strCustomerIds) {
CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId));
if (dashboard.isAssignedToCustomer(customerId)) {
customerIds.add(customerId);
}
}
}
return customerIds;
}
}

2
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -206,7 +206,7 @@ public class DeviceController extends BaseController {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.DELETE);
try {
tbDeviceService.deleteDevice(device, getCurrentUser()).get();
tbDeviceService.delete(device, getCurrentUser()).get();
} catch (Exception e) {
throw handleException(e);
}

4
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -164,7 +164,7 @@ public class EdgeController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edge.getId(), edge);
return tbEdgeService.saveEdge(edge, edgeTemplateRootRuleChain, getCurrentUser());
return tbEdgeService.save(edge, edgeTemplateRootRuleChain, getCurrentUser());
}
@ApiOperation(value = "Delete edge (deleteEdge)",
@ -177,7 +177,7 @@ public class EdgeController extends BaseController {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.DELETE);
tbEdgeService.deleteEdge(edge, getCurrentUser());
tbEdgeService.delete(edge, getCurrentUser());
}
@ApiOperation(value = "Get Tenant Edges (getEdges)",

2
application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java

@ -76,7 +76,7 @@ public class EdgeBulkImportService extends AbstractBulkImportService<Edge> {
@Override
protected Edge saveEntity(SecurityUser user, Edge entity, Map<BulkImportColumnType, String> fields) {
RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(user.getTenantId());
return tbEdgeService.saveEdge(entity, edgeTemplateRootRuleChain, user);
return tbEdgeService.save(entity, edgeTemplateRootRuleChain, user);
}
@Override

3
application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java

@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
@ -105,6 +106,8 @@ public abstract class AbstractTbEntityService {
protected EdgeNotificationService edgeNotificationService;
@Autowired
protected QueueService queueService;
@Autowired
protected DashboardService dashboardService;
protected ListenableFuture<Void> removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) {
ListenableFuture<PageData<AlarmInfo>> alarmsFuture =

48
application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java

@ -22,7 +22,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.HasName;
@ -73,7 +72,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
List<EdgeId> relatedEdgeIds,
SecurityUser user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds);
sendDeleteNotificationMsg(tenantId, entityId, entity, relatedEdgeIds);
}
public void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId,
CustomerId customerId, ActionType actionType,
List<EdgeId> relatedEdgeIds,
SecurityUser user,
String body, Object... additionalInfo) {
logEntityAction(tenantId, originatorId, alarm, customerId, actionType, user, additionalInfo);
sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body);
}
@Override
@ -126,7 +134,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
gatewayNotificationsService.onDeviceDeleted(device);
tbClusterService.onDeviceDeleted(device, null);
notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, false, additionalInfo);
notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, additionalInfo);
}
@Override
@ -145,9 +153,9 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
}
@Override
public <E extends HasName, I extends EntityId> void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Object... additionalInfo) {
public <E extends HasName, I extends EntityId> void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId, ActionType actionType, SecurityUser user, Object... additionalInfo) {
logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo);
if (actionType == ActionType.UPDATED) {
if (actionType == ActionType.UPDATED) {
sendEntityNotificationMsg(tenantId, entityId, EdgeEventActionType.UPDATED);
}
}
@ -191,20 +199,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
@Override
public void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, SecurityUser user, Object... additionalInfo) {
logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo);
sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType (actionType));
}
@Override
public void notifyDeleteAlarm(Alarm alarm, SecurityUser user, List<EdgeId> relatedEdgeIds) {
logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), ActionType.ALARM_DELETE, user, null);
sendAlarmDeleteNotificationMsg(alarm, relatedEdgeIds);
}
@Override
public void notifyDeleteCustomer(Customer customer, SecurityUser user, List<EdgeId> edgeIds) {
logEntityAction(customer.getTenantId(), customer.getId(), customer, customer.getId(), ActionType.DELETED, user, null);
sendDeleteNotificationMsg(customer.getTenantId(), customer.getId(), customer, edgeIds);
tbClusterService.broadcastEntityStateChangeEvent(customer.getTenantId(), customer.getId(), ComponentLifecycleEvent.DELETED);
sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType));
}
private <E extends HasName, I extends EntityId> void logEntityAction(TenantId tenantId, I entityId, E entity, CustomerId customerId,
@ -233,19 +228,20 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
}
}
protected <E extends HasName, I extends EntityId> void sendDeleteNotificationMsg(TenantId tenantId, I entityId, E entity, List<EdgeId> edgeIds) {
protected void sendAlarmDeleteNotificationMsg(TenantId tenantId, Alarm alarm, List<EdgeId> edgeIds, String body) {
try {
sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null);
sendDeleteNotificationMsg(tenantId, alarm.getId(), edgeIds, body);
} catch (Exception e) {
log.warn("Failed to push delete " + entity.getClass().getName() + " msg to core: {}", entity, e);
log.warn("Failed to push delete msg to core: {}", alarm, e);
}
}
protected void sendAlarmDeleteNotificationMsg(Alarm alarm, List<EdgeId> relatedEdgeIds) {
protected <E extends HasName, I extends EntityId> void sendDeleteNotificationMsg(TenantId tenantId, I entityId, E entity,
List<EdgeId> edgeIds) {
try {
sendDeleteNotificationMsg(alarm.getTenantId(), alarm.getId(), relatedEdgeIds, json.writeValueAsString(alarm));
sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null);
} catch (Exception e) {
log.warn("Failed to push delete alarm msg to core: {}", alarm, e);
log.warn("Failed to push delete msg to core: {}", entity, e);
}
}
@ -289,7 +285,7 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
return null;
}
private EdgeEventActionType edgeTypeByActionType (ActionType actionType) {
private EdgeEventActionType edgeTypeByActionType(ActionType actionType) {
switch (actionType) {
case ADDED:
return EdgeEventActionType.ADDED;

4
application/src/main/java/org/thingsboard/server/service/entitiy/SimpleTbEntityService.java

@ -20,6 +20,8 @@ import org.thingsboard.server.service.security.model.SecurityUser;
public interface SimpleTbEntityService<T> {
T save(T entity, SecurityUser user) throws ThingsboardException;
T save(T entity, SecurityUser user) throws ThingsboardException;
void delete (T entity, SecurityUser user) throws ThingsboardException;
}

17
application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.service.entitiy;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.Tenant;
@ -44,9 +43,15 @@ public interface TbNotificationEntityService {
CustomerId customerId, ActionType actionType,
SecurityUser user, Object... additionalInfo);
<E extends HasName, I extends EntityId> void notifyDeleteEntity(TenantId tenantId, I entityId, E entity, CustomerId customerId,
ActionType actionType, List<EdgeId> relatedEdgeIds, SecurityUser user,
Object... additionalInfo);
<E extends HasName, I extends EntityId> void notifyDeleteEntity(TenantId tenantId, I entityId, E entity,
CustomerId customerId, ActionType actionType,
List<EdgeId> relatedEdgeIds,
SecurityUser user, Object... additionalInfo);
void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId,
CustomerId customerId, ActionType actionType,
List<EdgeId> relatedEdgeIds,
SecurityUser user, String body, Object... additionalInfo);
<E extends HasName, I extends EntityId> void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId,
CustomerId customerId, E entity,
@ -80,8 +85,4 @@ public interface TbNotificationEntityService {
void notifyEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, SecurityUser user, Object... additionalInfo);
void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, SecurityUser user, Object... additionalInfo);
void notifyDeleteAlarm(Alarm alarm, SecurityUser user, List<EdgeId> relatedEdgeIds);
void notifyDeleteCustomer(Customer customer, SecurityUser user, List<EdgeId> relatedEdgeIds);
}

14
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.entitiy.alarm;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
@ -77,8 +78,13 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
@Override
public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException {
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(alarm.getTenantId(), alarm.getOriginator());
notificationEntityService.notifyDeleteAlarm(alarm, user, relatedEdgeIds);
return alarmService.deleteAlarm(alarm.getTenantId(), alarm.getId()).isSuccessful();
try {
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator());
notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(),
ActionType.DELETED, relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm));
return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful();
} catch (Exception e) {
throw handleException(e);
}
}
}
}

6
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java

@ -16,12 +16,12 @@
package org.thingsboard.server.service.entitiy.alarm;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbAlarmService extends SimpleTbEntityService<Alarm> {
public interface TbAlarmService {
Alarm save(Alarm entity, SecurityUser user) throws ThingsboardException;
void ack(Alarm alarm, SecurityUser user) throws ThingsboardException;

6
application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java

@ -39,6 +39,7 @@ import java.util.List;
@TbCoreComponent
@AllArgsConstructor
public class DefaultTbAssetService extends AbstractTbEntityService implements TbAssetService {
@Override
public Asset save(Asset asset, SecurityUser user) throws ThingsboardException {
ActionType actionType = asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
@ -60,12 +61,13 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try {
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(tenantId, assetId);
assetService.deleteAsset(tenantId, assetId);
notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), ActionType.DELETED, relatedEdgeIds, user, false, asset.toString());
notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), ActionType.DELETED,
relatedEdgeIds, user, assetId.toString());
return removeAlarmsByEntityId(tenantId, assetId);
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ASSET), null, null,
ActionType.DELETED, user, e, asset.toString());
ActionType.DELETED, user, e, assetId.toString());
throw handleException(e);
}
}

5
application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java

@ -22,10 +22,11 @@ import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbAssetService extends SimpleTbEntityService<Asset> {
public interface TbAssetService {
Asset save(Asset asset, SecurityUser user) throws ThingsboardException;
ListenableFuture<Void> delete(Asset asset, SecurityUser user) throws ThingsboardException;

18
application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java

@ -17,12 +17,15 @@ package org.thingsboard.server.service.entitiy.customer;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -34,6 +37,8 @@ import java.util.List;
@AllArgsConstructor
public class DefaultTbCustomerService extends AbstractTbEntityService implements TbCustomerService {
private final TbClusterService tbClusterService;
@Override
public Customer save(Customer customer, SecurityUser user) throws ThingsboardException {
ActionType actionType = customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
@ -48,15 +53,20 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements
}
}
@Override
public void delete(Customer customer, SecurityUser user) throws ThingsboardException {
TenantId tenantId = customer.getTenantId();
CustomerId customerId = customer.getId();
try {
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(tenantId, customer.getId());
customerService.deleteCustomer(tenantId, customer.getId());
notificationEntityService.notifyDeleteCustomer(customer, user, relatedEdgeIds);
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(tenantId, customerId);
customerService.deleteCustomer(tenantId, customerId);
notificationEntityService.notifyDeleteEntity(tenantId, customerId, customer, customerId,
ActionType.DELETED, relatedEdgeIds, user, customerId.toString());
tbClusterService.broadcastEntityStateChangeEvent(tenantId, customerId, ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), null, null, ActionType.DELETED, user, e);
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.CUSTOMER), null, null,
ActionType.DELETED, user, e, customerId.toString());
throw handleException(e);
}
}

4
application/src/main/java/org/thingsboard/server/service/entitiy/customer/TbCustomerService.java

@ -16,12 +16,8 @@
package org.thingsboard.server.service.entitiy.customer;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbCustomerService extends SimpleTbEntityService<Customer> {
void delete(Customer customer, SecurityUser user) throws ThingsboardException;
}

275
application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java

@ -0,0 +1,275 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.dashboard;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
@TbCoreComponent
@AllArgsConstructor
public class DefaultTbDashboardService extends AbstractTbEntityService implements TbDashboardService {
@Override
public Dashboard save(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
ActionType actionType = dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = dashboard.getTenantId();
try {
Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedDashboard.getId(), savedDashboard,
null, actionType, user);
return savedDashboard;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), dashboard, null, actionType, user, e);
throw handleException(e);
}
}
@Override
public void delete(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
TenantId tenantId = dashboard.getTenantId();
DashboardId dashboardId = dashboard.getId();
try {
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(tenantId, dashboardId);
dashboardService.deleteDashboard(tenantId, dashboardId);
notificationEntityService.notifyDeleteEntity(tenantId, dashboardId, dashboard, user.getCustomerId(),
ActionType.DELETED, relatedEdgeIds, user, dashboardId.toString());
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
ActionType.DELETED, user, e, dashboardId.toString());
throw handleException(e);
}
}
@Override
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, Customer customer, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
CustomerId customerId = customer.getId();
try {
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(user.getTenantId(), dashboardId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(user.getTenantId(), dashboardId, customerId, savedDashboard,
actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerId.toString(), customer.getName());
return savedDashboard;
} catch (Exception e) {
notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboardId.toString(), customerId.toString());
throw handleException(e);
}
}
@Override
public Dashboard assignDashboardToPublicCustomer(DashboardId dashboardId, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
try {
Customer publicCustomer = customerService.findOrCreatePublicCustomer(user.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(user.getTenantId(), dashboardId, publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(user.getTenantId(), dashboardId, user.getCustomerId(), savedDashboard,
actionType, null, user, false, dashboardId.toString(),
publicCustomer.getId().toString(), publicCustomer.getName());
return savedDashboard;
} catch (Exception e) {
notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboardId.toString());
throw handleException(e);
}
}
@Override
public Dashboard unassignDashboardFromPublicCustomer(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
try {
Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId());
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(user.getTenantId(), dashboard.getId(), publicCustomer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(user.getTenantId(), dashboard.getId(), user.getCustomerId(), dashboard,
actionType, null, user, false, dashboard.getId().toString(),
publicCustomer.getId().toString(), publicCustomer.getName());
return savedDashboard;
} catch (Exception e) {
notificationEntityService.notifyEntity(user.getTenantId(), emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboard.getId().toString());
throw handleException(e);
}
}
@Override
public Dashboard updateDashboardCustomers(Dashboard dashboard, Set<CustomerId> customerIds, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
TenantId tenantId = user.getTenantId();
try {
Set<CustomerId> addedCustomerIds = new HashSet<>();
Set<CustomerId> removedCustomerIds = new HashSet<>();
for (CustomerId customerId : customerIds) {
if (!dashboard.isAssignedToCustomer(customerId)) {
addedCustomerIds.add(customerId);
}
}
Set<ShortCustomerInfo> assignedCustomers = dashboard.getAssignedCustomers();
if (assignedCustomers != null) {
for (ShortCustomerInfo customerInfo : assignedCustomers) {
if (!customerIds.contains(customerInfo.getCustomerId())) {
removedCustomerIds.add(customerInfo.getCustomerId());
}
}
}
if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : addedCustomerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerInfo.getTitle());
}
for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
ActionType.UNASSIGNED_FROM_CUSTOMER, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customerInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboard.getId().toString());
throw handleException(e);
}
}
@Override
public Dashboard addDashboardCustomers(Dashboard dashboard, Set<CustomerId> customerIds, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER;
TenantId tenantId = user.getTenantId();
try {
if (customerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId));
ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId);
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboard.getId().toString());
throw handleException(e);
}
}
@Override
public Dashboard removeDashboardCustomers(Dashboard dashboard, Set<CustomerId> customerIds, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
TenantId tenantId = user.getTenantId();
try {
if (customerIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (CustomerId customerId : customerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard,
actionType, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customerInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboard.getId().toString());
throw handleException(e);
}
}
@Override
public Dashboard asignDashboardToEdge(DashboardId dashboardId, Edge edge, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.ASSIGNED_TO_EDGE;
TenantId tenantId = user.getTenantId();
EdgeId edgeId = edge.getId();
try {
Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(tenantId, dashboardId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, user.getCustomerId(),
edgeId, savedDashboard, actionType, EdgeEventActionType.ASSIGNED_TO_EDGE, user, dashboardId.toString(),
edgeId.toString(), edge.getName());
return savedDashboard;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DEVICE), null, null,
actionType, user, e, dashboardId.toString(), edgeId.toString());
throw handleException(e);
}
}
@Override
public Dashboard unassignDashboardFromEdge(Dashboard dashboard, Edge edge, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE;
TenantId tenantId = dashboard.getTenantId();
DashboardId dashboardId = dashboard.getId();
EdgeId edgeId = edge.getId();
try {
Dashboard savedDevice = checkNotNull(dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edgeId));
notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, user.getCustomerId(),
edgeId, dashboard, actionType, EdgeEventActionType.UNASSIGNED_FROM_EDGE, user, dashboardId.toString(),
edgeId.toString(), edge.getName());
return savedDevice;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboardId.toString(), edgeId.toString());
throw handleException(e);
}
}
@Override
public Dashboard unassignDashboardFromCustomer(Dashboard dashboard, Customer customer, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER;
TenantId tenantId = dashboard.getTenantId();
try {
Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customer.getId()));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboard.getId(), customer.getId(), savedDashboard,
actionType, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user, true, customer.getId().toString(), customer.getName());
return savedDashboard;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.DASHBOARD), null, null,
actionType, user, e, dashboard.getId().toString());
throw handleException(e);
}
}
}

49
application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/TbDashboardService.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.dashboard;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.Set;
public interface TbDashboardService extends SimpleTbEntityService<Dashboard> {
Dashboard assignDashboardToCustomer(DashboardId dashboardId, Customer customer, SecurityUser user) throws ThingsboardException;
Dashboard assignDashboardToPublicCustomer(DashboardId dashboardId, SecurityUser user) throws ThingsboardException;
Dashboard unassignDashboardFromPublicCustomer(Dashboard dashboard, SecurityUser user) throws ThingsboardException;
Dashboard updateDashboardCustomers(Dashboard dashboard, Set<CustomerId> customerIds, SecurityUser user) throws ThingsboardException;
Dashboard addDashboardCustomers(Dashboard dashboard, Set<CustomerId> customerIds, SecurityUser user) throws ThingsboardException;
Dashboard removeDashboardCustomers(Dashboard dashboard, Set<CustomerId> customerIds, SecurityUser user) throws ThingsboardException;
Dashboard asignDashboardToEdge(DashboardId dashboardId, Edge edge, SecurityUser user) throws ThingsboardException;
Dashboard unassignDashboardFromEdge(Dashboard dashboard, Edge edge, SecurityUser user) throws ThingsboardException;
Dashboard unassignDashboardFromCustomer(Dashboard dashboard, Customer customer, SecurityUser user) throws ThingsboardException;
}

2
application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java

@ -80,7 +80,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
}
@Override
public ListenableFuture<Void> deleteDevice(Device device, SecurityUser user) throws ThingsboardException {
public ListenableFuture<Void> delete(Device device, SecurityUser user) throws ThingsboardException {
TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId();
try {

2
application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java

@ -35,7 +35,7 @@ public interface TbDeviceService {
Device saveDeviceWithCredentials(TenantId tenantId, Device device, DeviceCredentials deviceCredentials, SecurityUser user) throws ThingsboardException;
ListenableFuture<Void> deleteDevice(Device device, SecurityUser user) throws ThingsboardException;
ListenableFuture<Void> delete(Device device, SecurityUser user) throws ThingsboardException;
Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, Customer customer, SecurityUser user) throws ThingsboardException;

8
application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java

@ -39,7 +39,7 @@ import org.thingsboard.server.service.security.model.SecurityUser;
public class DefaultTbEdgeService extends AbstractTbEntityService implements TbEdgeService {
@Override
public Edge saveEdge(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException {
public Edge save(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException {
ActionType actionType = edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = edge.getTenantId();
try {
@ -62,16 +62,16 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE
}
@Override
public void deleteEdge(Edge edge, SecurityUser user) throws ThingsboardException {
public void delete(Edge edge, SecurityUser user) throws ThingsboardException {
ActionType actionType = ActionType.DELETED;
EdgeId edgeId = edge.getId();
TenantId tenantId = edge.getTenantId();
try {
edgeService.deleteEdge(tenantId, edgeId);
notificationEntityService.notifyEdge(tenantId, edgeId, edge.getCustomerId(), edge, actionType, user, edgeId.toString());
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.EDGE), edge, null, actionType, user, e);
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.EDGE), edge, null, actionType,
user, e, edgeId.toString());
throw handleException(e);
}
}

4
application/src/main/java/org/thingsboard/server/service/entitiy/edge/TbEdgeService.java

@ -25,9 +25,9 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbEdgeService {
Edge saveEdge(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException;
Edge save(Edge edge, RuleChain edgeTemplateRootRuleChain, SecurityUser user) throws ThingsboardException;
void deleteEdge(Edge edge, SecurityUser user) throws ThingsboardException;
void delete(Edge edge, SecurityUser user) throws ThingsboardException;
Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, Customer customer, SecurityUser user) throws ThingsboardException;

73
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -228,7 +228,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
} else if (!theCtx.isInitialDataSent()) {
EntityDataUpdate update = new EntityDataUpdate(theCtx.getCmdId(), theCtx.getData(), null, theCtx.getMaxEntitiesPerDataSubscription());
wsService.sendWsMsg(theCtx.getSessionId(), update);
theCtx.sendWsMsg(update);
theCtx.setInitialDataSent(true);
}
} catch (RuntimeException e) {
@ -287,7 +287,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
ctx.clearEntitySubscriptions();
if (entities.isEmpty()) {
AlarmDataUpdate update = new AlarmDataUpdate(cmd.getCmdId(), new PageData<>(), null, 0, 0);
wsService.sendWsMsg(ctx.getSessionId(), update);
ctx.sendWsMsg(update);
} else {
ctx.fetchAlarms();
ctx.createLatestValuesSubscriptions(cmd.getQuery().getLatestValues());
@ -420,22 +420,26 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
} catch (InterruptedException | ExecutionException e) {
log.warn("[{}][{}][{}] Failed to fetch historical data", ctx.getSessionId(), ctx.getCmdId(), entityData.getEntityId(), e);
wsService.sendWsMsg(ctx.getSessionId(),
new EntityDataUpdate(ctx.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR.getCode(), "Failed to fetch historical data!"));
ctx.sendWsMsg(new EntityDataUpdate(ctx.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR.getCode(), "Failed to fetch historical data!"));
}
});
EntityDataUpdate update;
if (!ctx.isInitialDataSent()) {
update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.setInitialDataSent(true);
} else {
update = new EntityDataUpdate(ctx.getCmdId(), null, ctx.getData().getData(), ctx.getMaxEntitiesPerDataSubscription());
}
wsService.sendWsMsg(ctx.getSessionId(), update);
if (subscribe) {
ctx.createTimeseriesSubscriptions(keys.stream().map(key -> new EntityKey(EntityKeyType.TIME_SERIES, key)).collect(Collectors.toList()), cmd.getStartTs(), cmd.getEndTs());
ctx.getWsLock().lock();
try {
EntityDataUpdate update;
if (!ctx.isInitialDataSent()) {
update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.setInitialDataSent(true);
} else {
update = new EntityDataUpdate(ctx.getCmdId(), null, ctx.getData().getData(), ctx.getMaxEntitiesPerDataSubscription());
}
if (subscribe) {
ctx.createTimeseriesSubscriptions(keys.stream().map(key -> new EntityKey(EntityKeyType.TIME_SERIES, key)).collect(Collectors.toList()), cmd.getStartTs(), cmd.getEndTs());
}
ctx.sendWsMsg(update);
ctx.getData().getData().forEach(ed -> ed.getTimeseries().clear());
} finally {
ctx.getWsLock().unlock();
}
ctx.getData().getData().forEach(ed -> ed.getTimeseries().clear());
return ctx;
}, wsCallBackExecutor);
}
@ -464,7 +468,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
ListenableFuture<List<TsKvEntry>> missingTsData = tsService.findLatest(ctx.getTenantId(), entityData.getEntityId(), missingTsKeys);
missingTelemetryFutures.put(entityData, Futures.transform(missingTsData, this::toTsValue, MoreExecutors.directExecutor()));
}
Futures.addCallback(Futures.allAsList(missingTelemetryFutures.values()), new FutureCallback<List<Map<String, TsValue>>>() {
Futures.addCallback(Futures.allAsList(missingTelemetryFutures.values()), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<Map<String, TsValue>> result) {
missingTelemetryFutures.forEach((key, value) -> {
@ -475,30 +479,39 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
});
EntityDataUpdate update;
if (!ctx.isInitialDataSent()) {
update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.setInitialDataSent(true);
} else {
update = new EntityDataUpdate(ctx.getCmdId(), null, ctx.getData().getData(), ctx.getMaxEntitiesPerDataSubscription());
ctx.getWsLock().lock();
try {
ctx.createLatestValuesSubscriptions(latestCmd.getKeys());
if (!ctx.isInitialDataSent()) {
update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.setInitialDataSent(true);
} else {
update = new EntityDataUpdate(ctx.getCmdId(), null, ctx.getData().getData(), ctx.getMaxEntitiesPerDataSubscription());
}
ctx.sendWsMsg(update);
} finally {
ctx.getWsLock().unlock();
}
wsService.sendWsMsg(ctx.getSessionId(), update);
ctx.createLatestValuesSubscriptions(latestCmd.getKeys());
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}][{}] Failed to process websocket command: {}:{}", ctx.getSessionId(), ctx.getCmdId(), ctx.getQuery(), latestCmd, t);
wsService.sendWsMsg(ctx.getSessionId(),
new EntityDataUpdate(ctx.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR.getCode(), "Failed to process websocket command!"));
ctx.sendWsMsg(new EntityDataUpdate(ctx.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR.getCode(), "Failed to process websocket command!"));
}
}, wsCallBackExecutor);
} else {
if (!ctx.isInitialDataSent()) {
EntityDataUpdate update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
wsService.sendWsMsg(ctx.getSessionId(), update);
ctx.setInitialDataSent(true);
ctx.getWsLock().lock();
try {
ctx.createLatestValuesSubscriptions(latestCmd.getKeys());
if (!ctx.isInitialDataSent()) {
EntityDataUpdate update = new EntityDataUpdate(ctx.getCmdId(), ctx.getData(), null, ctx.getMaxEntitiesPerDataSubscription());
ctx.sendWsMsg(update);
ctx.setInitialDataSent(true);
}
} finally {
ctx.getWsLock().unlock();
}
ctx.createLatestValuesSubscriptions(latestCmd.getKeys());
}
}

16
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java

@ -41,6 +41,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
import java.util.ArrayList;
@ -52,14 +53,18 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
@Data
public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
@Getter
protected final Lock wsLock = new ReentrantLock(true);
protected final String serviceId;
protected final SubscriptionServiceStatistics stats;
protected final TelemetryWebSocketService wsService;
private final TelemetryWebSocketService wsService;
protected final EntityService entityService;
protected final TbLocalSubscriptionService localSubscriptionService;
protected final AttributesService attributesService;
@ -314,4 +319,13 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
private final String sourceAttribute;
}
public void sendWsMsg(CmdUpdate update) {
wsLock.lock();
try {
wsService.sendWsMsg(sessionRef.getSessionId(), update);
} finally {
wsLock.unlock();
}
}
}

6
application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java

@ -117,7 +117,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
} else {
update = new AlarmDataUpdate(cmdId, new PageData<>(), null, maxEntitiesPerAlarmSubscription, data.getTotalElements());
}
wsService.sendWsMsg(getSessionId(), update);
sendWsMsg(update);
}
public void fetchData() {
@ -198,7 +198,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
return alarm;
}).collect(Collectors.toList());
if (!update.isEmpty()) {
wsService.sendWsMsg(sessionId, new AlarmDataUpdate(cmdId, null, update, maxEntitiesPerAlarmSubscription, data.getTotalElements()));
sendWsMsg(new AlarmDataUpdate(cmdId, null, update, maxEntitiesPerAlarmSubscription, data.getTotalElements()));
}
} else {
log.trace("[{}][{}][{}][{}] Received stale subscription update: {}", sessionId, cmdId, subscriptionUpdate.getSubscriptionId(), keyType, subscriptionUpdate);
@ -222,7 +222,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
AlarmData updated = new AlarmData(alarm, current.getOriginatorName(), current.getEntityId());
updated.getLatest().putAll(current.getLatest());
alarmsMap.put(alarmId, updated);
wsService.sendWsMsg(sessionId, new AlarmDataUpdate(cmdId, null, Collections.singletonList(updated), maxEntitiesPerAlarmSubscription, data.getTotalElements()));
sendWsMsg(new AlarmDataUpdate(cmdId, null, Collections.singletonList(updated), maxEntitiesPerAlarmSubscription, data.getTotalElements()));
} else {
fetchAlarms();
}

7
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java

@ -17,14 +17,11 @@ package org.thingsboard.server.service.subscription;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.query.EntityCountQuery;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
@Slf4j
public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> {
@ -40,7 +37,7 @@ public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> {
@Override
public void fetchData() {
result = (int) entityService.countEntitiesByQuery(getTenantId(), getCustomerId(), query);
wsService.sendWsMsg(sessionRef.getSessionId(), new EntityCountUpdate(cmdId, result));
sendWsMsg(new EntityCountUpdate(cmdId, result));
}
@Override
@ -48,7 +45,7 @@ public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> {
int newCount = (int) entityService.countEntitiesByQuery(getTenantId(), getCustomerId(), query);
if (newCount != result) {
result = newCount;
wsService.sendWsMsg(sessionRef.getSessionId(), new EntityCountUpdate(cmdId, result));
sendWsMsg(new EntityCountUpdate(cmdId, result));
}
}

8
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java

@ -51,7 +51,7 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
@Getter
@Setter
private boolean initialDataSent;
private volatile boolean initialDataSent;
private TimeSeriesCmd curTsCmd;
private LatestValueCmd latestValueCmd;
@Getter
@ -121,7 +121,7 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
if (!latestUpdate.isEmpty()) {
Map<EntityKeyType, Map<String, TsValue>> latestMap = Collections.singletonMap(keyType, latestUpdate);
entityData = new EntityData(entityId, latestMap, null);
wsService.sendWsMsg(sessionId, new EntityDataUpdate(cmdId, null, Collections.singletonList(entityData), maxEntitiesPerDataSubscription));
sendWsMsg(new EntityDataUpdate(cmdId, null, Collections.singletonList(entityData), maxEntitiesPerDataSubscription));
}
}
@ -162,7 +162,7 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
Map<String, TsValue[]> tsMap = new HashMap<>();
tsUpdate.forEach((key, tsValue) -> tsMap.put(key, tsValue.toArray(new TsValue[tsValue.size()])));
EntityData entityData = new EntityData(entityId, null, tsMap);
wsService.sendWsMsg(sessionId, new EntityDataUpdate(cmdId, null, Collections.singletonList(entityData), maxEntitiesPerDataSubscription));
sendWsMsg(new EntityDataUpdate(cmdId, null, Collections.singletonList(entityData), maxEntitiesPerDataSubscription));
}
}
@ -219,9 +219,9 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
}
}
}
wsService.sendWsMsg(sessionRef.getSessionId(), new EntityDataUpdate(cmdId, data, null, maxEntitiesPerDataSubscription));
subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getSessionId(), subId));
subsToAdd.forEach(localSubscriptionService::addSubscription);
sendWsMsg(new EntityDataUpdate(cmdId, data, null, maxEntitiesPerDataSubscription));
}
public void setCurrentCmd(EntityDataCmd cmd) {

6
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetryWebSocketService.java

@ -442,7 +442,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
private void handleWsAttributesSubscriptionByKeys(TelemetryWebSocketSessionRef sessionRef,
AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId,
List<String> keys) {
FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<List<AttributeKvEntry>>() {
FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<>() {
@Override
public void onSuccess(List<AttributeKvEntry> data) {
List<TsKvEntry> attributesData = data.stream().map(d -> new BasicTsKvEntry(d.getLastUpdateTs(), d)).collect(Collectors.toList());
@ -542,7 +542,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
private void handleWsAttributesSubscription(TelemetryWebSocketSessionRef sessionRef,
AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) {
FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<List<AttributeKvEntry>>() {
FutureCallback<List<AttributeKvEntry>> callback = new FutureCallback<>() {
@Override
public void onSuccess(List<AttributeKvEntry> data) {
List<TsKvEntry> attributesData = data.stream().map(d -> new BasicTsKvEntry(d.getLastUpdateTs(), d)).collect(Collectors.toList());
@ -666,7 +666,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
}
private FutureCallback<List<TsKvEntry>> getSubscriptionCallback(final TelemetryWebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List<String> keys) {
return new FutureCallback<List<TsKvEntry>>() {
return new FutureCallback<>() {
@Override
public void onSuccess(List<TsKvEntry> data) {
sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data));

2
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -298,7 +298,7 @@ public class DefaultTransportApiService implements TransportApiService {
tbClusterService.onDeviceUpdated(savedDevice, null);
device = savedDevice;
relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
relationService.saveRelation(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
TbMsgMetaData metaData = new TbMsgMetaData();
CustomerId customerId = gateway.getCustomerId();

53
application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java

@ -62,6 +62,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat;
@ -368,7 +369,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
public void testTheCopyOfAttrsIntoTSForTheView() throws Exception {
Set<String> expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4");
Set<String> actualAttributesSet =
getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
putAttributesAndWait("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
log.debug("got correct actualAttributesSet, saving new entity view...");
EntityView savedView = getNewSavedEntityView("Test entity view");
@ -389,13 +390,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testTheCopyOfAttrsOutOfTSForTheView() throws Exception {
long now = System.currentTimeMillis();
Set<String> expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4");
Set<String> actualAttributesSet =
getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
putAttributesAndWait("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
List<Map<String, Object>> valueTelemetryOfDevices = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + testDevice.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {
List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + testDevice.getId() +
"/values/attributes?keys=" + String.join(",", expectedActualAttributesSet), new TypeReference<>() {
});
assertEquals(expectedActualAttributesSet.size(), values.size());
EntityView view = new EntityView();
view.setEntityId(testDevice.getId());
@ -403,12 +406,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
view.setName("Test entity view");
view.setType("default");
view.setKeys(telemetry);
view.setStartTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") * 10);
view.setEndTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") / 10);
view.setStartTimeMs(now - HOURS.toMillis(1));
view.setEndTimeMs(now - 1);
EntityView savedView = doPost("/api/entityView", view, EntityView.class);
List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {
values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", expectedActualAttributesSet), new TypeReference<>() {
});
assertEquals(0, values.size());
}
@ -431,19 +434,19 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
uploadTelemetry("{\"tsKey1\":\"value1\", \"tsKey2\":true, \"tsKey3\":40.0}", accessToken);
getWsClient().waitForUpdate();
long startTimeMs = System.currentTimeMillis();
long startTimeMs = getCurTsButNotPrevTs(now);
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value2\", \"tsKey2\":false, \"tsKey3\":80.0}", accessToken);
getWsClient().waitForUpdate();
Thread.sleep(3);
long middleOfTestMs = getCurTsButNotPrevTs(startTimeMs);
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value3\", \"tsKey2\":false, \"tsKey3\":120.0}", accessToken);
getWsClient().waitForUpdate();
long endTimeMs = System.currentTimeMillis();
long endTimeMs = getCurTsButNotPrevTs(middleOfTestMs);
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value4\", \"tsKey2\":true, \"tsKey3\":160.0}", accessToken);
getWsClient().waitForUpdate();
@ -455,15 +458,25 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
EntityView savedView = doPost("/api/entityView", view, EntityView.class);
String entityViewId = savedView.getId().getId().toString();
Map<String, List<Map<String, String>>> expectedValues = getTelemetryValues("DEVICE", deviceId, keys, 0L, (startTimeMs + endTimeMs) / 2);
Assert.assertEquals(2, expectedValues.get("tsKey1").size());
Assert.assertEquals(2, expectedValues.get("tsKey2").size());
Assert.assertEquals(2, expectedValues.get("tsKey3").size());
Map<String, List<Map<String, String>>> actualDeviceValues = getTelemetryValues("DEVICE", deviceId, keys, 0L, middleOfTestMs);
Assert.assertEquals(2, actualDeviceValues.get("tsKey1").size());
Assert.assertEquals(2, actualDeviceValues.get("tsKey2").size());
Assert.assertEquals(2, actualDeviceValues.get("tsKey3").size());
Map<String, List<Map<String, String>>> actualEntityViewValues = getTelemetryValues("ENTITY_VIEW", entityViewId, keys, 0L, middleOfTestMs);
Assert.assertEquals(1, actualEntityViewValues.get("tsKey1").size());
Assert.assertEquals(1, actualEntityViewValues.get("tsKey2").size());
Assert.assertEquals(1, actualEntityViewValues.get("tsKey3").size());
}
Map<String, List<Map<String, String>>> actualValues = getTelemetryValues("ENTITY_VIEW", entityViewId, keys, 0L, (startTimeMs + endTimeMs) / 2);
Assert.assertEquals(1, actualValues.get("tsKey1").size());
Assert.assertEquals(1, actualValues.get("tsKey2").size());
Assert.assertEquals(1, actualValues.get("tsKey3").size());
private static long getCurTsButNotPrevTs(long prevTs) throws InterruptedException {
long result = System.currentTimeMillis();
if (prevTs == result) {
Thread.sleep(1);
return getCurTsButNotPrevTs(prevTs);
} else {
return result;
}
}
private void uploadTelemetry(String strKvs, String accessToken) throws Exception {
@ -504,7 +517,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
});
}
private Set<String> getAttributesByKeys(String stringKV, Set<String> expectedKeySet) throws Exception {
private Set<String> putAttributesAndWait(String stringKV, Set<String> expectedKeySet) throws Exception {
DeviceTypeFilter dtf = new DeviceTypeFilter(testDevice.getType(), testDevice.getName());
List<EntityKey> keysToSubscribe = expectedKeySet.stream()
.map(key -> new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, key))

14
application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java

@ -102,7 +102,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
List<TsKvEntry> tsData = Arrays.asList(dataPoint1, dataPoint2, dataPoint3);
sendTelemetry(device, tsData);
Thread.sleep(100);
update = getWsClient().sendHistoryCmd(keys, now, TimeUnit.HOURS.toMillis(1), dtf);
@ -136,7 +135,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
List<TsKvEntry> tsData = Arrays.asList(dataPoint1, dataPoint2, dataPoint3);
sendTelemetry(device, tsData);
Thread.sleep(100);
update = getWsClient().subscribeTsUpdate(List.of("temperature"), now, TimeUnit.HOURS.toMillis(1));
Assert.assertEquals(1, update.getCmdId());
@ -153,7 +151,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
now = System.currentTimeMillis();
TsKvEntry dataPoint4 = new BasicTsKvEntry(now, new LongDataEntry("temperature", 45L));
getWsClient().registerWaitForUpdate();
Thread.sleep(100);
sendTelemetry(device, Arrays.asList(dataPoint4));
String msg = getWsClient().waitForUpdate();
@ -309,13 +306,12 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getTs());
Assert.assertEquals("", pageData.getData().get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getValue());
getWsClient().registerWaitForUpdate();
TsKvEntry dataPoint1 = new BasicTsKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("temperature", 42L));
List<TsKvEntry> tsData = Arrays.asList(dataPoint1);
sendTelemetry(device, tsData);
Thread.sleep(100);
update = getWsClient().subscribeLatestUpdate(keys, dtf);
update = getWsClient().parseDataReply(getWsClient().waitForUpdate());
Assert.assertEquals(1, update.getCmdId());
@ -329,7 +325,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
now = System.currentTimeMillis();
TsKvEntry dataPoint2 = new BasicTsKvEntry(now, new LongDataEntry("temperature", 52L));
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint2));
update = getWsClient().parseDataReply(getWsClient().waitForUpdate());
@ -371,7 +366,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
Assert.assertEquals("", pageData.getData().get(0).getLatest().get(EntityKeyType.SERVER_ATTRIBUTE).get("serverAttributeKey").getValue());
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("serverAttributeKey", 42L));
List<AttributeKvEntry> tsData = Arrays.asList(dataPoint1);
@ -394,7 +388,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
AttributeKvEntry dataPoint2 = new BaseAttributeKvEntry(now, new LongDataEntry("serverAttributeKey", 52L));
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint2));
msg = getWsClient().waitForUpdate();
Assert.assertNotNull(msg);
@ -411,14 +404,12 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
//Sending update from the past, while latest value has new timestamp;
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
//Sending duplicate update again
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint2));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
@ -456,7 +447,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
getWsClient().registerWaitForUpdate();
AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("serverAttributeKey", 42L));
List<AttributeKvEntry> tsData = Arrays.asList(dataPoint1);
Thread.sleep(100);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData);

16
application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java

@ -44,8 +44,8 @@ import java.util.concurrent.TimeUnit;
public class TbTestWebSocketClient extends WebSocketClient {
private volatile String lastMsg;
private CountDownLatch reply;
private CountDownLatch update;
private volatile CountDownLatch reply;
private volatile CountDownLatch update;
public TbTestWebSocketClient(URI serverUri) {
super(serverUri);
@ -60,12 +60,12 @@ public class TbTestWebSocketClient extends WebSocketClient {
public void onMessage(String s) {
log.info("RECEIVED: {}", s);
lastMsg = s;
if (reply != null) {
reply.countDown();
}
if (update != null) {
update.countDown();
}
if (reply != null) {
reply.countDown();
}
}
@Override
@ -79,8 +79,12 @@ public class TbTestWebSocketClient extends WebSocketClient {
}
public void registerWaitForUpdate() {
registerWaitForUpdate(1);
}
public void registerWaitForUpdate(int count) {
lastMsg = null;
update = new CountDownLatch(1);
update = new CountDownLatch(count);
}
@Override

17
application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java

@ -52,6 +52,7 @@ import static org.junit.Assert.assertNotNull;
@TestPropertySource(properties = {
"transport.mqtt.enabled=true",
"js.evaluator=mock",
})
@Slf4j
public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest {
@ -83,22 +84,6 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg
}
}
protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException {
String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken);
client.connect(options).waitForCompletion();
return client;
}
protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException {
MqttMessage message = new MqttMessage();
message.setPayload(payload);
client.publish(topic, message);
}
protected DeviceProfile createMqttDeviceProfile(MqttTestConfigProperties config) throws Exception {
TransportPayloadType transportPayloadType = config.getTransportPayloadType();
if (transportPayloadType == null) {

88
application/src/test/java/org/thingsboard/server/transport/mqtt/MqttTestCallback.java

@ -0,0 +1,88 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.mqtt;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
import java.util.concurrent.CountDownLatch;
@Slf4j
@Data
public class MqttTestCallback implements MqttCallback {
protected CountDownLatch subscribeLatch;
protected final CountDownLatch deliveryLatch;
protected int qoS;
protected byte[] payloadBytes;
protected String awaitSubTopic;
protected boolean pubAckReceived;
public MqttTestCallback() {
this.subscribeLatch = new CountDownLatch(1);
this.deliveryLatch = new CountDownLatch(1);
}
public MqttTestCallback(int subscribeCount) {
this.subscribeLatch = new CountDownLatch(subscribeCount);
this.deliveryLatch = new CountDownLatch(1);
}
public MqttTestCallback(String awaitSubTopic) {
this.subscribeLatch = new CountDownLatch(1);
this.deliveryLatch = new CountDownLatch(1);
this.awaitSubTopic = awaitSubTopic;
}
@Override
public void connectionLost(Throwable throwable) {
log.warn("connectionLost: ", throwable);
deliveryLatch.countDown();
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) {
if (awaitSubTopic == null) {
log.warn("messageArrived on topic: {}", requestTopic);
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
subscribeLatch.countDown();
} else {
messageArrivedOnAwaitSubTopic(requestTopic, mqttMessage);
}
}
protected void messageArrivedOnAwaitSubTopic(String requestTopic, MqttMessage mqttMessage) {
log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic);
if (awaitSubTopic.equals(requestTopic)) {
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
subscribeLatch.countDown();
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
log.warn("delivery complete: {}", iMqttDeliveryToken.getResponse());
pubAckReceived = iMqttDeliveryToken.getResponse().getType() == MqttWireMessage.MESSAGE_TYPE_PUBACK;
deliveryLatch.countDown();
}
}

126
application/src/test/java/org/thingsboard/server/transport/mqtt/MqttTestClient.java

@ -0,0 +1,126 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.mqtt;
import io.netty.handler.codec.mqtt.MqttQoS;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit;
public class MqttTestClient {
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds
private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private final MqttAsyncClient client;
public void setCallback(MqttTestCallback callback) {
client.setCallback(callback);
}
public MqttTestClient() throws MqttException {
this.client = createClient();
}
public MqttTestClient(String clientId) throws MqttException {
this.client = createClient(clientId);
}
public void connectAndWait(String userName, String password) throws MqttException {
IMqttToken connect = connect(userName, password);
connect.waitForCompletion(TIMEOUT_MS);
}
public void connectAndWait(String userName) throws MqttException {
connectAndWait(userName, null);
}
public void connectAndWait() throws MqttException {
connectAndWait(null, null);
}
private IMqttToken connect(String userName, String password) throws MqttException {
if (client == null) {
throw new RuntimeException("Failed to connect! MqttAsyncClient is not initialized!");
}
MqttConnectOptions options = new MqttConnectOptions();
if (StringUtils.isNotEmpty(userName)) {
options.setUserName(userName);
}
if (StringUtils.isNotEmpty(password)) {
options.setPassword(password.toCharArray());
}
return client.connect(options);
}
public void disconnectAndWait() throws MqttException {
disconnect().waitForCompletion(TIMEOUT_MS);
}
public IMqttToken disconnect() throws MqttException {
return client.disconnect();
}
public void disconnectForcibly() throws MqttException {
client.disconnectForcibly(TIMEOUT_MS);
}
public void publishAndWait(String topic, byte[] payload) throws MqttException {
publish(topic, payload).waitForCompletion(TIMEOUT_MS);
}
public IMqttDeliveryToken publish(String topic, byte[] payload) throws MqttException {
MqttMessage message = new MqttMessage();
message.setPayload(payload);
return client.publish(topic, message);
}
public void subscribeAndWait(String topic, MqttQoS qoS) throws MqttException {
subscribe(topic, qoS).waitForCompletion(TIMEOUT_MS);
}
public IMqttToken subscribe(String topic, MqttQoS qoS) throws MqttException {
return client.subscribe(topic, qoS.value());
}
public void enableManualAcks() {
client.setManualAcks(true);
}
public void messageArrivedComplete(MqttMessage mqttMessage) throws MqttException {
client.messageArrivedComplete(mqttMessage.getId(), mqttMessage.getQos());
}
private MqttAsyncClient createClient(String clientId) throws MqttException {
if (StringUtils.isEmpty(clientId)) {
clientId = MqttAsyncClient.generateClientId();
}
return new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
}
private MqttAsyncClient createClient() throws MqttException {
return createClient(null);
}
}

653
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/AbstractMqttAttributesIntegrationTest.java

@ -22,35 +22,44 @@ import com.google.protobuf.InvalidProtocolBufferException;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.SingleEntityFilter;
import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_ATTRIBUTES_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_CONNECT_TOPIC;
import static org.thingsboard.server.common.data.query.EntityKeyType.CLIENT_ATTRIBUTE;
import static org.thingsboard.server.common.data.query.EntityKeyType.SHARED_ATTRIBUTE;
@Slf4j
public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
@ -60,11 +69,11 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
"package test;\n" +
"\n" +
"message PostAttributes {\n" +
" string attribute1 = 1;\n" +
" bool attribute2 = 2;\n" +
" double attribute3 = 3;\n" +
" int32 attribute4 = 4;\n" +
" JsonObject attribute5 = 5;\n" +
" string clientStr = 1;\n" +
" bool clientBool = 2;\n" +
" double clientDbl = 3;\n" +
" int32 clientLong = 4;\n" +
" JsonObject clientJson = 5;\n" +
"\n" +
" message JsonObject {\n" +
" int32 someNumber = 6;\n" +
@ -76,17 +85,20 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
" }\n" +
"}";
protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," +
"\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}";
private static final String CLIENT_ATTRIBUTES_PAYLOAD = "{\"clientStr\":\"value1\",\"clientBool\":true,\"clientDbl\":42.0,\"clientLong\":73," +
"\"clientJson\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}";
private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}";
private static final String SHARED_ATTRIBUTES_PAYLOAD = "{\"sharedStr\":\"value1\",\"sharedBool\":true,\"sharedDbl\":42.0,\"sharedLong\":73," +
"\"sharedJson\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}";
protected List<TransportProtos.TsKvProto> getTsKvProtoList() {
TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V);
TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V);
TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V);
TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V);
TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V);
private static final String SHARED_ATTRIBUTES_DELETED_RESPONSE = "{\"deleted\":[\"sharedJson\"]}";
private List<TransportProtos.TsKvProto> getTsKvProtoList(String attributePrefix) {
TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto(attributePrefix + "Str", "value1", TransportProtos.KeyValueType.STRING_V);
TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto(attributePrefix + "Bool", "true", TransportProtos.KeyValueType.BOOLEAN_V);
TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto(attributePrefix + "Dbl", "42.0", TransportProtos.KeyValueType.DOUBLE_V);
TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto(attributePrefix + "Long", "73", TransportProtos.KeyValueType.LONG_V);
TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto(attributePrefix + "Json", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V);
List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>();
tsKvProtoList.add(tsKvProtoAttribute1);
tsKvProtoList.add(tsKvProtoAttribute2);
@ -103,118 +115,56 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
return tsKvProtoBuilder.build();
}
protected TestMqttCallback getTestMqttCallback() {
CountDownLatch latch = new CountDownLatch(1);
return new TestMqttCallback(latch);
}
protected static class TestMqttCallback implements MqttCallback {
private final CountDownLatch latch;
private Integer qoS;
private byte[] payloadBytes;
TestMqttCallback(CountDownLatch latch) {
this.latch = latch;
}
public int getQoS() {
return qoS;
}
public byte[] getPayloadBytes() {
return payloadBytes;
}
public CountDownLatch getLatch() {
return latch;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
}
// subscribe to attributes updates from server methods
protected void processJsonTestSubscribeToAttributesUpdates(String attrSubTopic) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttCallback onUpdateCallback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback onUpdateCallback = new MqttTestCallback();
client.setCallback(onUpdateCallback);
client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE);
client.subscribe(attrSubTopic, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
validateUpdateAttributesJsonResponse(onUpdateCallback, SHARED_ATTRIBUTES_PAYLOAD);
validateUpdateAttributesJsonResponse(onUpdateCallback);
TestMqttCallback onDeleteCallback = getTestMqttCallback();
MqttTestCallback onDeleteCallback = new MqttTestCallback();
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
validateDeleteAttributesJsonResponse(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
onDeleteCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
validateUpdateAttributesJsonResponse(onDeleteCallback, SHARED_ATTRIBUTES_DELETED_RESPONSE);
client.disconnect();
}
protected void processProtoTestSubscribeToAttributesUpdates(String attrSubTopic) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttCallback onUpdateCallback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback onUpdateCallback = new MqttTestCallback();
client.setCallback(onUpdateCallback);
client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE);
client.subscribe(attrSubTopic, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
validateUpdateAttributesProtoResponse(onUpdateCallback);
TestMqttCallback onDeleteCallback = getTestMqttCallback();
MqttTestCallback onDeleteCallback = new MqttTestCallback();
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
onDeleteCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
validateDeleteAttributesProtoResponse(onDeleteCallback);
client.disconnect();
}
protected void validateUpdateAttributesJsonResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
protected void validateUpdateAttributesJsonResponse(MqttTestCallback callback, String expectedResponse) {
assertNotNull(callback.getPayloadBytes());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response));
assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
protected void validateDeleteAttributesJsonResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response));
}
protected void validateUpdateAttributesProtoResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
protected void validateUpdateAttributesProtoResponse(MqttTestCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList("shared");
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
@ -227,134 +177,99 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList));
}
protected void validateDeleteAttributesProtoResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
protected void validateDeleteAttributesProtoResponse(MqttTestCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5");
attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson");
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes());
assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size());
assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0));
assertEquals("sharedJson", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0));
}
protected void processJsonGatewayTestSubscribeToAttributesUpdates() throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
TestMqttCallback onUpdateCallback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback onUpdateCallback = new MqttTestCallback();
client.setCallback(onUpdateCallback);
Device device = new Device();
device.setName("Gateway Device Subscribe to attribute updates");
device.setType("default");
byte[] connectPayloadBytes = getJsonConnectPayloadBytes();
String deviceName = "Gateway Device Subscribe to attribute updates";
byte[] connectPayloadBytes = getJsonConnectPayloadBytes(deviceName, deviceProfile.getTransportType().name());
publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
client.publishAndWait(GATEWAY_CONNECT_TOPIC, connectPayloadBytes);
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class),
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(savedDevice);
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
client.subscribeAndWait(GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE);
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
validateJsonGatewayUpdateAttributesResponse(onUpdateCallback, deviceName, SHARED_ATTRIBUTES_PAYLOAD);
validateJsonGatewayUpdateAttributesResponse(onUpdateCallback);
TestMqttCallback onDeleteCallback = getTestMqttCallback();
MqttTestCallback onDeleteCallback = new MqttTestCallback();
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
validateJsonGatewayDeleteAttributesResponse(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
onDeleteCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
validateJsonGatewayUpdateAttributesResponse(onDeleteCallback, deviceName, SHARED_ATTRIBUTES_DELETED_RESPONSE);
client.disconnect();
}
protected void processProtoGatewayTestSubscribeToAttributesUpdates() throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
TestMqttCallback onUpdateCallback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback onUpdateCallback = new MqttTestCallback();
client.setCallback(onUpdateCallback);
Device device = new Device();
device.setName("Gateway Device Subscribe to attribute updates");
device.setType("default");
byte[] connectPayloadBytes = getProtoConnectPayloadBytes();
publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class),
String deviceName = "Gateway Device Subscribe to attribute updates";
byte[] connectPayloadBytes = getProtoConnectPayloadBytes(deviceName, TransportPayloadType.PROTOBUF.name());
client.publishAndWait(GATEWAY_CONNECT_TOPIC, connectPayloadBytes);
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(savedDevice);
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
validateProtoGatewayUpdateAttributesResponse(onUpdateCallback);
TestMqttCallback onDeleteCallback = getTestMqttCallback();
assertNotNull(device);
client.subscribeAndWait(GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE);
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
validateProtoGatewayUpdateAttributesResponse(onUpdateCallback, deviceName);
MqttTestCallback onDeleteCallback = new MqttTestCallback();
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS);
validateProtoGatewayDeleteAttributesResponse(onDeleteCallback);
}
protected void validateJsonGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(getJsonResponseGatewayAttributesUpdatedPayload(), s);
doDelete("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
validateProtoGatewayDeleteAttributesResponse(onDeleteCallback, deviceName);
client.disconnect();
}
protected void validateJsonGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
protected void validateJsonGatewayUpdateAttributesResponse(MqttTestCallback callback, String deviceName, String expectResultData) {
assertNotNull(callback.getPayloadBytes());
String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(s, getJsonResponseGatewayAttributesDeletedPayload());
assertEquals(JacksonUtil.toJsonNode(getGatewayAttributesResponseJson(deviceName, expectResultData)), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
protected byte[] getJsonConnectPayloadBytes() {
String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}";
protected byte[] getJsonConnectPayloadBytes(String deviceName, String deviceType) {
String connectPayload = "{\"device\":\"" + deviceName + "\", \"type\": \"" + deviceType + "\"}";
return connectPayload.getBytes();
}
private static String getJsonResponseGatewayAttributesUpdatedPayload() {
return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\"," +
"\"data\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
}
private static String getJsonResponseGatewayAttributesDeletedPayload() {
return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\",\"data\":{\"deleted\":[\"attribute5\"]}}";
private static String getGatewayAttributesResponseJson(String deviceName, String expectResultData) {
return "{\"device\":\"" + deviceName + "\"," + "\"data\":" + expectResultData + "}";
}
protected void validateProtoGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
protected void validateProtoGatewayUpdateAttributesResponse(MqttTestCallback callback, String deviceName) throws InvalidProtocolBufferException, InterruptedException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertNotNull(callback.getPayloadBytes());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList("shared");
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder();
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates");
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName(deviceName);
gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(expectedAttributeUpdateNotificationMsg);
TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build();
@ -367,17 +282,17 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size());
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList));
}
protected void validateProtoGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException {
protected void validateProtoGatewayDeleteAttributesResponse(MqttTestCallback callback, String deviceName) throws InvalidProtocolBufferException, InterruptedException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertNotNull(callback.getPayloadBytes());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5");
attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson");
TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder();
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates");
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName(deviceName);
gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(attributeUpdateNotificationMsg);
TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build();
@ -389,118 +304,190 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg();
assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size());
assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0));
}
protected byte[] getProtoConnectPayloadBytes() {
TransportApiProtos.ConnectMsg connectProto = getConnectProto();
return connectProto.toByteArray();
assertEquals("sharedJson", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0));
}
private TransportApiProtos.ConnectMsg getConnectProto() {
TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder();
builder.setDeviceName("Gateway Device Subscribe to attribute updates");
builder.setDeviceType(TransportPayloadType.PROTOBUF.name());
return builder.build();
private byte[] getProtoConnectPayloadBytes(String deviceName, String deviceType) {
TransportApiProtos.ConnectMsg connectMsg = TransportApiProtos.ConnectMsg.newBuilder()
.setDeviceName(deviceName)
.setDeviceType(deviceType)
.build();
return connectMsg.toByteArray();
}
// request attributes from server methods
protected void processJsonTestRequestAttributesValuesFromTheServer(String attrPubTopic, String attrSubTopic, String attrReqTopicPrefix) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
postJsonAttributesAndSubscribeToTopic(savedDevice, client, attrPubTopic, attrSubTopic);
Thread.sleep(5000);
TestMqttCallback callback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
SingleEntityFilter dtf = new SingleEntityFilter();
dtf.setSingleEntity(savedDevice.getId());
String clientKeysStr = "clientStr,clientBool,clientDbl,clientLong,clientJson";
String sharedKeysStr = "sharedStr,sharedBool,sharedDbl,sharedLong,sharedJson";
List<String> clientKeysList = List.of(clientKeysStr.split(","));
List<String> sharedKeysList = List.of(sharedKeysStr.split(","));
List<EntityKey> csKeys = getEntityKeys(clientKeysList, CLIENT_ATTRIBUTE);
List<EntityKey> shKeys = getEntityKeys(sharedKeysList, SHARED_ATTRIBUTE);
List<EntityKey> keys = new ArrayList<>();
keys.addAll(csKeys);
keys.addAll(shKeys);
getWsClient().subscribeLatestUpdate(keys, dtf);
getWsClient().registerWaitForUpdate(2);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE",
SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
client.publishAndWait(attrPubTopic, CLIENT_ATTRIBUTES_PAYLOAD.getBytes());
client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE);
String update = getWsClient().waitForUpdate();
assertThat(update).as("ws update received").isNotBlank();
MqttTestCallback callback = new MqttTestCallback(attrSubTopic.replace("+", "1"));
client.setCallback(callback);
validateJsonResponse(client, callback.getLatch(), callback, attrReqTopicPrefix);
String payloadStr = "{\"clientKeys\":\"" + clientKeysStr + "\", \"sharedKeys\":\"" + sharedKeysStr + "\"}";
client.publishAndWait(attrReqTopicPrefix + "1", payloadStr.getBytes());
String expectedResponse = "{\"client\":" + CLIENT_ATTRIBUTES_PAYLOAD + ",\"shared\":" + SHARED_ATTRIBUTES_PAYLOAD + "}";
validateJsonResponse(callback, expectedResponse);
client.disconnect();
}
protected void processProtoTestRequestAttributesValuesFromTheServer(String attrPubTopic, String attrSubTopic, String attrReqTopicPrefix) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
postProtoAttributesAndSubscribeToTopic(savedDevice, client, attrPubTopic, attrSubTopic);
Thread.sleep(5000);
TestMqttCallback callback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
DeviceTypeFilter dtf = new DeviceTypeFilter(savedDevice.getType(), savedDevice.getName());
String clientKeysStr = "clientStr,clientBool,clientDbl,clientLong,clientJson";
String sharedKeysStr = "sharedStr,sharedBool,sharedDbl,sharedLong,sharedJson";
List<String> clientKeysList = List.of(clientKeysStr.split(","));
List<String> sharedKeysList = List.of(sharedKeysStr.split(","));
List<EntityKey> csKeys = getEntityKeys(clientKeysList, CLIENT_ATTRIBUTE);
List<EntityKey> shKeys = getEntityKeys(sharedKeysList, SHARED_ATTRIBUTE);
List<EntityKey> keys = new ArrayList<>();
keys.addAll(csKeys);
keys.addAll(shKeys);
getWsClient().subscribeLatestUpdate(keys, dtf);
getWsClient().registerWaitForUpdate(2);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
client.publishAndWait(attrPubTopic, getAttributesProtoPayloadBytes());
client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE);
String update = getWsClient().waitForUpdate();
assertThat(update).as("ws update received").isNotBlank();
MqttTestCallback callback = new MqttTestCallback(attrSubTopic.replace("+", "1"));
client.setCallback(callback);
validateProtoResponse(client, callback.getLatch(), callback, attrReqTopicPrefix);
TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder();
attributesRequestBuilder.setClientKeys(clientKeysStr);
attributesRequestBuilder.setSharedKeys(sharedKeysStr);
TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build();
client.publishAndWait(attrReqTopicPrefix + "1", attributesRequest.toByteArray());
validateProtoResponse(callback, getExpectedAttributeResponseMsg());
client.disconnect();
}
protected void processJsonTestGatewayRequestAttributesValuesFromTheServer() throws Exception {
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
String deviceName = "Gateway Device Request Attributes";
String postClientAttributes = "{\"" + deviceName + "\":" + CLIENT_ATTRIBUTES_PAYLOAD + "}";
client.publishAndWait(GATEWAY_ATTRIBUTES_TOPIC, postClientAttributes.getBytes());
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
postJsonGatewayDeviceClientAttributes(client);
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class),
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(savedDevice);
Thread.sleep(2000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
Thread.sleep(5000);
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value()).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
TestMqttCallback clientAttributesCallback = getTestMqttCallback();
assertNotNull(device);
SingleEntityFilter dtf = new SingleEntityFilter();
dtf.setSingleEntity(device.getId());
String clientKeysStr = "clientStr,clientBool,clientDbl,clientLong,clientJson";
String sharedKeysStr = "sharedStr,sharedBool,sharedDbl,sharedLong,sharedJson";
List<String> clientKeysList = List.of(clientKeysStr.split(","));
List<String> sharedKeysList = List.of(sharedKeysStr.split(","));
List<EntityKey> csKeys = getEntityKeys(clientKeysList, CLIENT_ATTRIBUTE);
List<EntityKey> shKeys = getEntityKeys(sharedKeysList, SHARED_ATTRIBUTE);
List<EntityKey> keys = new ArrayList<>();
keys.addAll(csKeys);
keys.addAll(shKeys);
EntityDataUpdate initUpdate = getWsClient().subscribeLatestUpdate(keys, dtf);
assertNotNull(initUpdate);
PageData<EntityData> data = initUpdate.getData();
assertNotNull(data);
assertFalse(data.getData().isEmpty());
getWsClient().registerWaitForUpdate();
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
String update = getWsClient().waitForUpdate();
assertThat(update).as("ws update received").isNotBlank();
client.subscribeAndWait(GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE);
MqttTestCallback clientAttributesCallback = new MqttTestCallback(GATEWAY_ATTRIBUTES_RESPONSE_TOPIC);
client.setCallback(clientAttributesCallback);
validateJsonClientResponseGateway(client, clientAttributesCallback);
String csKeysStr = "[\"clientStr\", \"clientBool\", \"clientDbl\", \"clientLong\", \"clientJson\"]";
String csRequestPayloadStr = "{\"id\": 1, \"device\": \"" + deviceName + "\", \"client\": true, \"keys\": " + csKeysStr + "}";
client.publishAndWait(GATEWAY_ATTRIBUTES_REQUEST_TOPIC, csRequestPayloadStr.getBytes());
validateJsonResponseGateway(clientAttributesCallback, deviceName, CLIENT_ATTRIBUTES_PAYLOAD);
TestMqttCallback sharedAttributesCallback = getTestMqttCallback();
MqttTestCallback sharedAttributesCallback = new MqttTestCallback(GATEWAY_ATTRIBUTES_RESPONSE_TOPIC);
client.setCallback(sharedAttributesCallback);
validateJsonSharedResponseGateway(client, sharedAttributesCallback);
String shKeysStr = "[\"sharedStr\", \"sharedBool\", \"sharedDbl\", \"sharedLong\", \"sharedJson\"]";
String shRequestPayloadStr = "{\"id\": 1, \"device\": \"" + deviceName + "\", \"client\": false, \"keys\": " + shKeysStr + "}";
client.publishAndWait(GATEWAY_ATTRIBUTES_REQUEST_TOPIC, shRequestPayloadStr.getBytes());
validateJsonResponseGateway(sharedAttributesCallback, deviceName, SHARED_ATTRIBUTES_PAYLOAD);
client.disconnect();
}
protected void processProtoTestGatewayRequestAttributesValuesFromTheServer() throws Exception {
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
String deviceName = "Gateway Device Request Attributes";
String clientKeysStr = "clientStr,clientBool,clientDbl,clientLong,clientJson";
List<String> clientKeysList = List.of(clientKeysStr.split(","));
client.publishAndWait(GATEWAY_ATTRIBUTES_TOPIC, getProtoGatewayDeviceClientAttributesPayload(deviceName, clientKeysList));
postProtoGatewayDeviceClientAttributes(client);
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class),
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(savedDevice);
Thread.sleep(2000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
Thread.sleep(5000);
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value()).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
TestMqttCallback clientAttributesCallback = getTestMqttCallback();
assertNotNull(device);
SingleEntityFilter dtf = new SingleEntityFilter();
dtf.setSingleEntity(device.getId());
String sharedKeysStr = "sharedStr,sharedBool,sharedDbl,sharedLong,sharedJson";
List<String> sharedKeysList = List.of(sharedKeysStr.split(","));
List<EntityKey> csKeys = getEntityKeys(clientKeysList, CLIENT_ATTRIBUTE);
List<EntityKey> shKeys = getEntityKeys(sharedKeysList, SHARED_ATTRIBUTE);
List<EntityKey> keys = new ArrayList<>();
keys.addAll(csKeys);
keys.addAll(shKeys);
EntityDataUpdate initUpdate = getWsClient().subscribeLatestUpdate(keys, dtf);
assertNotNull(initUpdate);
PageData<EntityData> data = initUpdate.getData();
assertNotNull(data);
assertFalse(data.getData().isEmpty());
getWsClient().registerWaitForUpdate();
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
String update = getWsClient().waitForUpdate();
assertThat(update).as("ws update received").isNotBlank();
client.subscribeAndWait(GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE);
MqttTestCallback clientAttributesCallback = new MqttTestCallback(GATEWAY_ATTRIBUTES_RESPONSE_TOPIC);
client.setCallback(clientAttributesCallback);
validateProtoClientResponseGateway(client, clientAttributesCallback);
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(deviceName, clientKeysList, true);
client.publishAndWait(GATEWAY_ATTRIBUTES_REQUEST_TOPIC, gatewayAttributesRequestMsg.toByteArray());
validateProtoClientResponseGateway(clientAttributesCallback, deviceName);
TestMqttCallback sharedAttributesCallback = getTestMqttCallback();
MqttTestCallback sharedAttributesCallback = new MqttTestCallback(GATEWAY_ATTRIBUTES_RESPONSE_TOPIC);
client.setCallback(sharedAttributesCallback);
validateProtoSharedResponseGateway(client, sharedAttributesCallback);
gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(deviceName, sharedKeysList, false);
client.publishAndWait(GATEWAY_ATTRIBUTES_REQUEST_TOPIC, gatewayAttributesRequestMsg.toByteArray());
validateProtoSharedResponseGateway(sharedAttributesCallback, deviceName);
client.disconnect();
}
protected void postJsonAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client, String attrPubTopic, String attrSubTopic) throws Exception {
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
client.publish(attrPubTopic, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes())).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
client.subscribe(attrSubTopic, MqttQoS.AT_MOST_ONCE.value()).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
private List<EntityKey> getEntityKeys(List<String> keys, EntityKeyType scope) {
return keys.stream().map(key -> new EntityKey(scope, key)).collect(Collectors.toList());
}
protected void postProtoAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client, String attrPubTopic, String attrSubTopic) throws Exception {
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", AbstractMqttAttributesIntegrationTest.POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
private byte[] getAttributesProtoPayloadBytes() {
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
@ -530,64 +517,39 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType();
assertNotNull(postAttributesMsgDescriptor);
DynamicMessage postAttributesMsg = postAttributesBuilder
.setField(postAttributesMsgDescriptor.findFieldByName("attribute1"), "value1")
.setField(postAttributesMsgDescriptor.findFieldByName("attribute2"), true)
.setField(postAttributesMsgDescriptor.findFieldByName("attribute3"), 42.0)
.setField(postAttributesMsgDescriptor.findFieldByName("attribute4"), 73)
.setField(postAttributesMsgDescriptor.findFieldByName("attribute5"), jsonObject)
.setField(postAttributesMsgDescriptor.findFieldByName("clientStr"), "value1")
.setField(postAttributesMsgDescriptor.findFieldByName("clientBool"), true)
.setField(postAttributesMsgDescriptor.findFieldByName("clientDbl"), 42.0)
.setField(postAttributesMsgDescriptor.findFieldByName("clientLong"), 73)
.setField(postAttributesMsgDescriptor.findFieldByName("clientJson"), jsonObject)
.build();
byte[] payload = postAttributesMsg.toByteArray();
client.publish(attrPubTopic, new MqttMessage(payload));
client.subscribe(attrSubTopic, MqttQoS.AT_MOST_ONCE.value());
return postAttributesMsg.toByteArray();
}
protected void postJsonGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception {
String postClientAttributes = "{\"" + "Gateway Device Request Attributes" + "\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes())).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
}
protected void postProtoGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception {
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
List<String> expectedKeys = Arrays.asList(keys.split(","));
TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys);
protected byte[] getProtoGatewayDeviceClientAttributesPayload(String deviceName, List<String> clientKeysList) {
TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(clientKeysList);
TransportApiProtos.AttributesMsg.Builder attributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder();
attributesMsgBuilder.setDeviceName("Gateway Device Request Attributes");
attributesMsgBuilder.setDeviceName(deviceName);
attributesMsgBuilder.setMsg(postAttributeMsg);
TransportApiProtos.AttributesMsg attributesMsg = attributesMsgBuilder.build();
TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributeMsgBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder();
gatewayAttributeMsgBuilder.addMsg(attributesMsg);
byte[] bytes = gatewayAttributeMsgBuilder.build().toByteArray();
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes));
return gatewayAttributeMsgBuilder.build().toByteArray();
}
protected void validateJsonResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback, String attrReqTopicPrefix) throws MqttException, InterruptedException, InvalidProtocolBufferException {
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}";
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(payloadStr.getBytes());
client.publish(attrReqTopicPrefix + "1", mqttMessage).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
latch.await(1, TimeUnit.MINUTES);
protected void validateJsonResponse(MqttTestCallback callback, String expectedResponse) throws InterruptedException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
protected void validateProtoResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback, String attrReqTopic) throws MqttException, InterruptedException, InvalidProtocolBufferException {
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder();
attributesRequestBuilder.setClientKeys(keys);
attributesRequestBuilder.setSharedKeys(keys);
TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build();
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(attributesRequest.toByteArray());
client.publish(attrReqTopic + "1", mqttMessage);
latch.await(3, TimeUnit.SECONDS);
protected void validateProtoResponse(MqttTestCallback callback, TransportProtos.GetAttributeResponseMsg expectedResponse) throws InterruptedException, InvalidProtocolBufferException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg();
TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes());
assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId());
List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
assertEquals(expectedResponse.getRequestId(), actualAttributesResponse.getRequestId());
List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos));
@ -596,42 +558,25 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() {
TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
result.addAllClientAttributeList(tsKvProtoList);
result.addAllSharedAttributeList(tsKvProtoList);
List<TransportProtos.TsKvProto> csTsKvProtoList = getTsKvProtoList("client");
List<TransportProtos.TsKvProto> shTsKvProtoList = getTsKvProtoList("shared");
result.addAllClientAttributeList(csTsKvProtoList);
result.addAllSharedAttributeList(shTsKvProtoList);
result.setRequestId(1);
return result.build();
}
protected void validateJsonClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(payloadStr.getBytes());
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
callback.getLatch().await(1, TimeUnit.MINUTES);
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
}
protected void validateJsonSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
MqttMessage mqttMessage = new MqttMessage();
mqttMessage.setPayload(payloadStr.getBytes());
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage).waitForCompletion(TimeUnit.MINUTES.toMillis(1));
callback.getLatch().await(1, TimeUnit.MINUTES);
protected void validateJsonResponseGateway(MqttTestCallback callback, String deviceName, String expectedValues) throws InterruptedException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
String expectedRequestPayload = "{\"id\":1,\"device\":\"" + deviceName + "\",\"values\":" + expectedValues + "}";
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
protected void validateProtoClientResponseGateway(MqttAsyncClient client, AbstractMqttAttributesIntegrationTest.TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, true);
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray()));
callback.getLatch().await(3, TimeUnit.SECONDS);
protected void validateProtoClientResponseGateway(MqttTestCallback callback, String deviceName) throws InterruptedException, InvalidProtocolBufferException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(true);
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(deviceName, true);
TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes());
assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName());
@ -644,13 +589,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos));
}
protected void validateProtoSharedResponseGateway(MqttAsyncClient client, AbstractMqttAttributesIntegrationTest.TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, false);
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray()));
callback.getLatch().await(3, TimeUnit.SECONDS);
protected void validateProtoSharedResponseGateway(MqttTestCallback callback, String deviceName) throws InterruptedException, InvalidProtocolBufferException {
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(false);
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(deviceName, false);
TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes());
assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName());
@ -664,27 +606,26 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos));
}
private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(boolean client) {
private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(String deviceName, boolean client) {
TransportApiProtos.GatewayAttributeResponseMsg.Builder gatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder();
TransportProtos.GetAttributeResponseMsg.Builder getAttributeResponseMsgBuilder = TransportProtos.GetAttributeResponseMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
if (client) {
getAttributeResponseMsgBuilder.addAllClientAttributeList(tsKvProtoList);
getAttributeResponseMsgBuilder.addAllClientAttributeList(getTsKvProtoList("client"));
} else {
getAttributeResponseMsgBuilder.addAllSharedAttributeList(tsKvProtoList);
getAttributeResponseMsgBuilder.addAllSharedAttributeList(getTsKvProtoList("shared"));
}
getAttributeResponseMsgBuilder.setRequestId(1);
TransportProtos.GetAttributeResponseMsg getAttributeResponseMsg = getAttributeResponseMsgBuilder.build();
gatewayAttributeResponseMsg.setDeviceName("Gateway Device Request Attributes");
gatewayAttributeResponseMsg.setDeviceName(deviceName);
gatewayAttributeResponseMsg.setResponseMsg(getAttributeResponseMsg);
return gatewayAttributeResponseMsg.build();
}
private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String keys, boolean client) {
private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String deviceName, List<String> keysList, boolean client) {
return TransportApiProtos.GatewayAttributesRequestMsg.newBuilder()
.setDeviceName(deviceName)
.addAllKeys(keysList)
.setClient(client)
.addAllKeys(Arrays.asList(keys.split(",")))
.setDeviceName("Gateway Device Request Attributes")
.setId(1).build();
}
}

2
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/MqttAttributesRequestBackwardCompatibilityIntegrationTest.java

@ -85,7 +85,6 @@ public class MqttAttributesRequestBackwardCompatibilityIntegrationTest extends A
@Test
public void testRequestAttributesValuesFromTheServerGatewayWithEnabledJsonCompatibilityAndJsonDownlinks() throws Exception {
MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder()
.deviceName("Test Request attribute values from the server proto")
.gatewayName("Gateway Test Request attribute values from the server proto")
.transportPayloadType(TransportPayloadType.PROTOBUF)
.enableCompatibilityWithJsonPayloadFormat(true)
@ -99,7 +98,6 @@ public class MqttAttributesRequestBackwardCompatibilityIntegrationTest extends A
public void testRequestAttributesValuesFromTheServerOnShortJsonTopicWithEnabledJsonCompatibilityAndJsonDownlinks() throws Exception {
MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder()
.deviceName("Test Request attribute values from the server proto")
.gatewayName("Gateway Test Request attribute values from the server proto")
.transportPayloadType(TransportPayloadType.PROTOBUF)
.enableCompatibilityWithJsonPayloadFormat(true)
.useJsonPayloadFormatForDefaultDownlinkTopics(true)

1
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/request/MqttAttributesRequestProtoIntegrationTest.java

@ -67,7 +67,6 @@ public class MqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttri
@Test
public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder()
.deviceName("Test Request attribute values from the server proto")
.gatewayName("Gateway Test Request attribute values from the server proto")
.transportPayloadType(TransportPayloadType.PROTOBUF)
.build();

18
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesBackwardCompatibilityIntegrationTest.java

@ -16,14 +16,17 @@
package org.thingsboard.server.transport.mqtt.attributes.updates;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttAttributesUpdatesBackwardCompatibilityIntegrationTest extends AbstractMqttAttributesIntegrationTest {
@ -36,7 +39,7 @@ public class MqttAttributesUpdatesBackwardCompatibilityIntegrationTest extends A
.enableCompatibilityWithJsonPayloadFormat(true)
.build();
processBeforeTest(configProperties);
processProtoTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
processProtoTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC);
}
@Test
@ -48,7 +51,7 @@ public class MqttAttributesUpdatesBackwardCompatibilityIntegrationTest extends A
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC);
}
@Test
@ -60,7 +63,7 @@ public class MqttAttributesUpdatesBackwardCompatibilityIntegrationTest extends A
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processProtoTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC);
processProtoTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_TOPIC);
}
@Test
@ -72,7 +75,7 @@ public class MqttAttributesUpdatesBackwardCompatibilityIntegrationTest extends A
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
}
@Test
@ -84,13 +87,12 @@ public class MqttAttributesUpdatesBackwardCompatibilityIntegrationTest extends A
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processProtoTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC);
processProtoTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC);
}
@Test
public void testProtoSubscribeToAttributesUpdatesFromTheServerGatewayWithEnabledJsonCompatibilityAndJsonDownlinks() throws Exception {
MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder()
.deviceName("Test Subscribe to attribute updates")
.gatewayName("Gateway Test Subscribe to attribute updates")
.transportPayloadType(TransportPayloadType.PROTOBUF)
.enableCompatibilityWithJsonPayloadFormat(true)

11
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesIntegrationTest.java

@ -19,11 +19,14 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributesIntegrationTest {
@ -40,17 +43,17 @@ public class MqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributes
@Test
public void testJsonSubscribeToAttributesUpdatesFromTheServer() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC);
}
@Test
public void testJsonSubscribeToAttributesUpdatesFromTheServerOnShortTopic() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_TOPIC);
}
@Test
public void testJsonSubscribeToAttributesUpdatesFromTheServerOnShortJsonTopic() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
}
@Test

11
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesJsonIntegrationTest.java

@ -19,11 +19,14 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesIntegrationTest {
@ -40,17 +43,17 @@ public class MqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttrib
@Test
public void testJsonSubscribeToAttributesUpdatesFromTheServer() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC);
}
@Test
public void testJsonSubscribeToAttributesUpdatesFromTheServerOnShortTopic() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_TOPIC);
}
@Test
public void testJsonSubscribeToAttributesUpdatesFromTheServerOnShortJsonTopic() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
}
@Test

14
application/src/test/java/org/thingsboard/server/transport/mqtt/attributes/updates/MqttAttributesUpdatesProtoIntegrationTest.java

@ -19,11 +19,15 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import org.thingsboard.server.transport.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttributesIntegrationTest {
@ -40,22 +44,22 @@ public class MqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttri
@Test
public void testProtoSubscribeToAttributesUpdatesFromTheServer() throws Exception {
processProtoTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_TOPIC);
processProtoTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_TOPIC);
}
@Test
public void testProtoSubscribeToAttributesUpdatesFromTheServerOnShortTopic() throws Exception {
processProtoTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC);
processProtoTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_TOPIC);
}
@Test
public void testProtoSubscribeToAttributesUpdatesFromTheServerOnShortJsonTopic() throws Exception {
processJsonTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
processJsonTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC);
}
@Test
public void testProtoSubscribeToAttributesUpdatesFromTheServerOnShortProtoTopic() throws Exception {
processProtoTestSubscribeToAttributesUpdates(MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC);
processProtoTestSubscribeToAttributesUpdates(DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC);
}
@Test

31
application/src/test/java/org/thingsboard/server/transport/mqtt/claim/MqttClaimDeviceTest.java

@ -16,28 +16,26 @@
package org.thingsboard.server.transport.mqtt.claim;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.ClaimRequest;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_CLAIM_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_CLAIM_TOPIC;
@Slf4j
@DaoSqlTest
@ -99,7 +97,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
protected void processTestClaimingDevice(boolean emptyPayload) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
byte[] payloadBytes;
byte[] failurePayloadBytes;
if (emptyPayload) {
@ -112,8 +111,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes);
}
protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception {
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
protected void validateClaimResponse(boolean emptyPayload, MqttTestClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception {
client.publishAndWait(DEVICE_CLAIM_TOPIC, failurePayloadBytes);
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
ClaimRequest claimRequest;
@ -131,7 +130,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
assertEquals(claimResponse, ClaimResponse.FAILURE);
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes));
client.publishAndWait(DEVICE_CLAIM_TOPIC, payloadBytes);
client.disconnect();
ClaimResult claimResult = doExecuteWithRetriesAndInterval(
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()),
@ -148,8 +148,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
assertEquals(claimResponse, ClaimResponse.CLAIMED);
}
protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception {
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttTestClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception {
client.publishAndWait(GATEWAY_CLAIM_TOPIC, failurePayloadBytes);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
@ -170,7 +170,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
assertEquals(claimResponse, ClaimResponse.FAILURE);
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes));
client.publishAndWait(GATEWAY_CLAIM_TOPIC, payloadBytes);
client.disconnect();
ClaimResult claimResult = doExecuteWithRetriesAndInterval(
() -> doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()),
@ -189,7 +190,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
}
protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
byte[] failurePayloadBytes;
byte[] payloadBytes;
String failurePayload;
@ -207,7 +209,8 @@ public class MqttClaimDeviceTest extends AbstractMqttIntegrationTest {
}
protected void processProtoTestGatewayClaimDevice(String deviceName, boolean emptyPayload) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
byte[] failurePayloadBytes;
byte[] payloadBytes;
if (emptyPayload) {

5
application/src/test/java/org/thingsboard/server/transport/mqtt/claim/MqttClaimProtoDeviceTest.java

@ -16,12 +16,12 @@
package org.thingsboard.server.transport.mqtt.claim;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
@Slf4j
@ -60,7 +60,8 @@ public class MqttClaimProtoDeviceTest extends MqttClaimDeviceTest {
}
protected void processTestClaimingDevice(boolean emptyPayload) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
byte[] payloadBytes;
if (emptyPayload) {
payloadBytes = getClaimDevice(0, emptyPayload).toByteArray();

65
application/src/test/java/org/thingsboard/server/transport/mqtt/credentials/BasicMqttCredentialsTest.java

@ -17,22 +17,17 @@ package org.thingsboard.server.transport.mqtt.credentials;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttSecurityException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import java.util.Arrays;
import java.util.HashSet;
@ -43,6 +38,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_TOPIC;
@DaoSqlTest
public class BasicMqttCredentialsTest extends AbstractMqttIntegrationTest {
@ -93,33 +89,51 @@ public class BasicMqttCredentialsTest extends AbstractMqttIntegrationTest {
@Test
public void testCorrectCredentials() throws Exception {
// Check that correct devices receive telemetry
testTelemetryIsDelivered(accessTokenDevice, getMqttAsyncClient(null, USER_NAME1, null));
testTelemetryIsDelivered(clientIdDevice, getMqttAsyncClient(CLIENT_ID, null, null));
testTelemetryIsDelivered(clientIdAndUserNameDevice1, getMqttAsyncClient(CLIENT_ID, USER_NAME1, null));
testTelemetryIsDelivered(clientIdAndUserNameAndPasswordDevice2, getMqttAsyncClient(CLIENT_ID, USER_NAME2, PASSWORD));
MqttTestClient mqttTestClient1 = new MqttTestClient();
mqttTestClient1.connectAndWait(USER_NAME1);
MqttTestClient mqttTestClient2 = new MqttTestClient(CLIENT_ID);
mqttTestClient2.connectAndWait();
MqttTestClient mqttTestClient3 = new MqttTestClient(CLIENT_ID);
mqttTestClient3.connectAndWait(USER_NAME1);
MqttTestClient mqttTestClient4 = new MqttTestClient(CLIENT_ID);
mqttTestClient4.connectAndWait(USER_NAME2, PASSWORD);
// Also correct. Random clientId and password, but matches access token
MqttTestClient mqttTestClient5 = new MqttTestClient(RandomStringUtils.randomAlphanumeric(10));
mqttTestClient5.connectAndWait(USER_NAME2, RandomStringUtils.randomAlphanumeric(10));
testTelemetryIsDelivered(accessTokenDevice, mqttTestClient1);
testTelemetryIsDelivered(clientIdDevice, mqttTestClient2);
testTelemetryIsDelivered(clientIdAndUserNameDevice1, mqttTestClient3);
testTelemetryIsDelivered(clientIdAndUserNameAndPasswordDevice2, mqttTestClient4);
// Also correct. Random clientId and password, but matches access token
testTelemetryIsDelivered(accessToken2Device, getMqttAsyncClient(RandomStringUtils.randomAlphanumeric(10), USER_NAME2, RandomStringUtils.randomAlphanumeric(10)));
testTelemetryIsDelivered(accessToken2Device, mqttTestClient5);
}
@Test(expected = MqttSecurityException.class)
public void testCorrectClientIdAndUserNameButWrongPassword() throws Exception {
// Not correct. Correct clientId and username, but wrong password
testTelemetryIsNotDelivered(clientIdAndUserNameAndPasswordDevice3, getMqttAsyncClient(CLIENT_ID, USER_NAME3, "WRONG PASSWORD"));
MqttTestClient mqttTestClient = new MqttTestClient(CLIENT_ID);
mqttTestClient.connectAndWait(USER_NAME3, "WRONG PASSWORD");
testTelemetryIsNotDelivered(clientIdAndUserNameAndPasswordDevice3, mqttTestClient);
}
private void testTelemetryIsDelivered(Device device, MqttAsyncClient client) throws Exception {
private void testTelemetryIsDelivered(Device device, MqttTestClient client) throws Exception {
testTelemetryIsDelivered(device, client, true);
}
private void testTelemetryIsNotDelivered(Device device, MqttAsyncClient client) throws Exception {
private void testTelemetryIsNotDelivered(Device device, MqttTestClient client) throws Exception {
testTelemetryIsDelivered(device, client, false);
}
private void testTelemetryIsDelivered(Device device, MqttAsyncClient client, boolean ok) throws Exception {
private void testTelemetryIsDelivered(Device device, MqttTestClient client, boolean ok) throws Exception {
String randomKey = RandomStringUtils.randomAlphanumeric(10);
List<String> expectedKeys = Arrays.asList(randomKey);
publishMqttMsg(client, JacksonUtil.toString(JacksonUtil.newObjectNode().put(randomKey, true)).getBytes(), MqttTopics.DEVICE_TELEMETRY_TOPIC);
client.publishAndWait(DEVICE_TELEMETRY_TOPIC, JacksonUtil.toString(JacksonUtil.newObjectNode().put(randomKey, true)).getBytes());
String deviceId = device.getId().getId().toString();
@ -146,24 +160,7 @@ public class BasicMqttCredentialsTest extends AbstractMqttIntegrationTest {
} else {
assertNull(actualKeys);
}
client.disconnect().waitForCompletion();
}
protected MqttAsyncClient getMqttAsyncClient(String clientId, String username, String password) throws MqttException {
if (StringUtils.isEmpty(clientId)) {
clientId = MqttAsyncClient.generateClientId();
}
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
if (StringUtils.isNotEmpty(username)) {
options.setUserName(username);
}
if (StringUtils.isNotEmpty(password)) {
options.setPassword(password.toCharArray());
}
client.connect(options).waitForCompletion();
return client;
client.disconnect();
}
private Device createDevice(String deviceName, BasicMqttCredentials clientIdCredValue) throws Exception {

149
application/src/test/java/org/thingsboard/server/transport/mqtt/provision/MqttProvisionJsonDeviceTest.java

@ -15,13 +15,9 @@
*/
package org.thingsboard.server.transport.mqtt.provision;
import com.google.gson.JsonObject;
import com.fasterxml.jackson.databind.JsonNode;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -30,20 +26,22 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.EncryptionUtil;
import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
@ -97,10 +95,12 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.provisionType(DeviceProfileProvisionType.DISABLED)
.build();
super.processBeforeTest(configProperties);
byte[] result = createMqttClientAndPublish().getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").getAsString());
byte[] result = createMqttClientAndPublish();
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("errorMsg"));
Assert.assertTrue(response.hasNonNull("status"));
Assert.assertEquals("Provision data was not found!", response.get("errorMsg").asText());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").asText());
}
@ -113,8 +113,10 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
super.processBeforeTest(configProperties);
byte[] result = createMqttClientAndPublish().getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
byte[] result = createMqttClientAndPublish();
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("credentialsType"));
Assert.assertTrue(response.hasNonNull("status"));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -122,8 +124,8 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, createdDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").asText());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").asText());
}
@ -137,8 +139,10 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.build();
super.processBeforeTest(configProperties);
String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\"";
byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
byte[] result = createMqttClientAndPublish(requestCredentials);
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("credentialsType"));
Assert.assertTrue(response.hasNonNull("status"));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -146,10 +150,10 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, createdDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").asText());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "ACCESS_TOKEN");
Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token");
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").asText());
}
@ -163,8 +167,10 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.build();
super.processBeforeTest(configProperties);
String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\"";
byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
byte[] result = createMqttClientAndPublish(requestCredentials);
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("credentialsType"));
Assert.assertTrue(response.hasNonNull("status"));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -172,7 +178,7 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, createdDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").asText());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "X509_CERTIFICATE");
String cert = EncryptionUtil.certTrimNewLines(deviceCredentials.getCredentialsValue());
@ -181,7 +187,7 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash);
Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash");
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").asText());
}
@ -195,8 +201,10 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.build();
super.processBeforeTest(configProperties);
String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\"";
byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
byte[] result = createMqttClientAndPublish(requestCredentials);
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("credentialsType"));
Assert.assertTrue(response.hasNonNull("status"));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -204,7 +212,7 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, createdDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").asText());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "MQTT_BASIC");
Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username"));
@ -214,7 +222,7 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
mqttCredentials.setPassword("test_password");
Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials));
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").asText());
}
protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception {
@ -226,13 +234,15 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
super.processBeforeTest(configProperties);
byte[] result = createMqttClientAndPublish().getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
byte[] result = createMqttClientAndPublish();
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("credentialsType"));
Assert.assertTrue(response.hasNonNull("status"));
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, savedDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").asText());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").asText());
}
protected void processTestProvisioningWithBadKeyDevice() throws Exception {
@ -244,72 +254,29 @@ public class MqttProvisionJsonDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
super.processBeforeTest(configProperties);
byte[] result = createMqttClientAndPublish().getPayloadBytes();
JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject();
Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").getAsString());
byte[] result = createMqttClientAndPublish();
JsonNode response = JacksonUtil.fromBytes(result);
Assert.assertTrue(response.hasNonNull("errorMsg"));
Assert.assertTrue(response.hasNonNull("status"));
Assert.assertEquals("Provision data was not found!", response.get("errorMsg").asText());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").asText());
}
protected TestMqttCallback createMqttClientAndPublish() throws Exception {
protected byte[] createMqttClientAndPublish() throws Exception {
return createMqttClientAndPublish("");
}
protected TestMqttCallback createMqttClientAndPublish(String deviceCredentials) throws Exception {
protected byte[] createMqttClientAndPublish(String deviceCredentials) throws Exception {
String provisionRequestMsg = createTestProvisionMessage(deviceCredentials);
MqttAsyncClient client = getMqttAsyncClient("provision");
TestMqttCallback onProvisionCallback = getTestMqttCallback();
MqttTestClient client = new MqttTestClient();
client.connectAndWait("provision");
MqttTestCallback onProvisionCallback = new MqttTestCallback(DEVICE_PROVISION_RESPONSE_TOPIC);
client.setCallback(onProvisionCallback);
client.subscribe(MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
client.publish(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC, new MqttMessage(provisionRequestMsg.getBytes()));
onProvisionCallback.getLatch().await(3, TimeUnit.SECONDS);
return onProvisionCallback;
}
protected TestMqttCallback getTestMqttCallback() {
CountDownLatch latch = new CountDownLatch(1);
return new TestMqttCallback(latch);
}
protected static class TestMqttCallback implements MqttCallback {
private final CountDownLatch latch;
private Integer qoS;
private byte[] payloadBytes;
TestMqttCallback(CountDownLatch latch) {
this.latch = latch;
}
public int getQoS() {
return qoS;
}
public byte[] getPayloadBytes() {
return payloadBytes;
}
public CountDownLatch getLatch() {
return latch;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
client.subscribe(DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE);
client.publishAndWait(DEVICE_PROVISION_REQUEST_TOPIC, provisionRequestMsg.getBytes());
onProvisionCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
client.disconnect();
return onProvisionCallback.getPayloadBytes();
}
protected String createTestProvisionMessage(String deviceCredentials) {

114
application/src/test/java/org/thingsboard/server/transport/mqtt/provision/MqttProvisionProtoDeviceTest.java

@ -17,10 +17,6 @@ package org.thingsboard.server.transport.mqtt.provision;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -29,7 +25,6 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.msg.EncryptionUtil;
@ -46,11 +41,15 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCre
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
@ -104,9 +103,9 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.provisionType(DeviceProfileProvisionType.DISABLED)
.build();
processBeforeTest(configProperties);
ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish());
Assert.assertNotNull(result);
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().name());
}
protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception {
@ -118,7 +117,7 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
processBeforeTest(configProperties);
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish());
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -126,8 +125,8 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, createdDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().name());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().name());
}
protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception {
@ -139,9 +138,12 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
processBeforeTest(configProperties);
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build();
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder()
.setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build())
.build();
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes());
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(
createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -164,9 +166,13 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
processBeforeTest(configProperties);
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build();
CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder()
.setValidateDeviceX509CertRequestMsg(
ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build())
.build();
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes());
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(
createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -203,7 +209,8 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.build()
).build();
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.MQTT_BASIC, requestCredentials)).getPayloadBytes());
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(
createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.MQTT_BASIC, requestCredentials)));
Device createdDevice = deviceService.findDeviceByTenantIdAndName(tenantId, "Test Provision device");
@ -233,12 +240,12 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
processBeforeTest(configProperties);
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish());
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, savedDevice.getId());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString());
Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().name());
Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().name());
}
protected void processTestProvisioningWithBadKeyDevice() throws Exception {
@ -250,70 +257,25 @@ public class MqttProvisionProtoDeviceTest extends AbstractMqttIntegrationTest {
.provisionSecret("testProvisionSecret")
.build();
processBeforeTest(configProperties);
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString());
ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish());
Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().name());
}
protected TestMqttCallback createMqttClientAndPublish() throws Exception {
protected byte[] createMqttClientAndPublish() throws Exception {
byte[] provisionRequestMsg = createTestProvisionMessage();
return createMqttClientAndPublish(provisionRequestMsg);
}
protected TestMqttCallback createMqttClientAndPublish(byte[] provisionRequestMsg) throws Exception {
MqttAsyncClient client = getMqttAsyncClient("provision");
TestMqttCallback onProvisionCallback = getTestMqttCallback();
protected byte[] createMqttClientAndPublish(byte[] provisionRequestMsg) throws Exception {
MqttTestClient client = new MqttTestClient();
client.connectAndWait("provision");
MqttTestCallback onProvisionCallback = new MqttTestCallback(DEVICE_PROVISION_RESPONSE_TOPIC);
client.setCallback(onProvisionCallback);
client.subscribe(MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
client.publish(MqttTopics.DEVICE_PROVISION_REQUEST_TOPIC, new MqttMessage(provisionRequestMsg));
onProvisionCallback.getLatch().await(3, TimeUnit.SECONDS);
return onProvisionCallback;
}
protected TestMqttCallback getTestMqttCallback() {
CountDownLatch latch = new CountDownLatch(1);
return new TestMqttCallback(latch);
}
protected static class TestMqttCallback implements MqttCallback {
private final CountDownLatch latch;
private Integer qoS;
private byte[] payloadBytes;
TestMqttCallback(CountDownLatch latch) {
this.latch = latch;
}
public int getQoS() {
return qoS;
}
public byte[] getPayloadBytes() {
return payloadBytes;
}
public CountDownLatch getLatch() {
return latch;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
client.subscribe(DEVICE_PROVISION_RESPONSE_TOPIC, MqttQoS.AT_MOST_ONCE);
client.publishAndWait(DEVICE_PROVISION_REQUEST_TOPIC, provisionRequestMsg);
onProvisionCallback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
client.disconnect();
return onProvisionCallback.getPayloadBytes();
}
protected byte[] createTestsProvisionMessage(CredentialsType credentialsType, CredentialsDataProto credentialsData) throws Exception {

396
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java

@ -26,33 +26,34 @@ import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.junit.Assert;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.BASE_DEVICE_API_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.BASE_DEVICE_API_TOPIC_V2;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_CONNECT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_RPC_TOPIC;
@Slf4j
public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractMqttIntegrationTest {
@ -76,81 +77,93 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
protected static final Long asyncContextTimeoutToUseRpcPlugin = 10000L;
protected void processOneWayRpcTest(String rpcSubTopic) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
CountDownLatch latch = new CountDownLatch(1);
TestOneWayMqttCallback callback = new TestOneWayMqttCallback(client, latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback(rpcSubTopic.replace("+", "0"));
client.setCallback(callback);
client.subscribe(rpcSubTopic, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
client.subscribeAndWait(rpcSubTopic, MqttQoS.AT_MOST_ONCE);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
Assert.assertTrue(StringUtils.isEmpty(result));
latch.await(3, TimeUnit.SECONDS);
String result = doPostAsync("/api/rpc/oneway/" + savedDevice.getId(), setGpioRequest, String.class, status().isOk());
assertTrue(StringUtils.isEmpty(result));
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
DeviceTransportType deviceTransportType = deviceProfile.getTransportType();
if (deviceTransportType.equals(DeviceTransportType.MQTT)) {
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
MqttDeviceProfileTransportConfiguration configuration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
TransportPayloadType transportPayloadType = configuration.getTransportPayloadTypeConfiguration().getTransportPayloadType();
if (transportPayloadType.equals(TransportPayloadType.PROTOBUF)) {
// TODO: add correct validation of proto requests to device
assertTrue(callback.getPayloadBytes().length > 0);
} else {
assertEquals(JacksonUtil.toJsonNode(setGpioRequest), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
} else {
assertEquals(JacksonUtil.toJsonNode(setGpioRequest), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
client.disconnect();
}
protected void processJsonOneWayRpcTestGateway(String deviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
String payload = "{\"device\":\"" + deviceName + "\"}";
byte[] payloadBytes = payload.getBytes();
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes);
client.disconnect();
}
protected void processJsonTwoWayRpcTest(String rpcSubTopic) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
client.subscribe(rpcSubTopic, 1);
CountDownLatch latch = new CountDownLatch(1);
TestJsonMqttCallback callback = new TestJsonMqttCallback(client, latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
client.subscribeAndWait(rpcSubTopic, MqttQoS.AT_LEAST_ONCE);
MqttTestRpcJsonCallback callback = new MqttTestRpcJsonCallback(client, rpcSubTopic.replace("+", "0"));
client.setCallback(callback);
Thread.sleep(1000);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String expected = "{\"value1\":\"A\",\"value2\":\"B\"}";
latch.await(3, TimeUnit.SECONDS);
Assert.assertEquals(expected, result);
String actualRpcResponse = doPostAsync("/api/rpc/twoway/" + savedDevice.getId(), setGpioRequest, String.class, status().isOk());
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals(JacksonUtil.toJsonNode(setGpioRequest), JacksonUtil.fromBytes(callback.getPayloadBytes()));
assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", actualRpcResponse);
client.disconnect();
}
protected void processProtoTwoWayRpcTest(String rpcSubTopic) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
client.subscribe(rpcSubTopic, 1);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
client.subscribeAndWait(rpcSubTopic, MqttQoS.AT_LEAST_ONCE);
CountDownLatch latch = new CountDownLatch(1);
TestProtoMqttCallback callback = new TestProtoMqttCallback(client, latch);
MqttTestRpcProtoCallback callback = new MqttTestRpcProtoCallback(client, rpcSubTopic.replace("+", "0"));
client.setCallback(callback);
Thread.sleep(1000);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
latch.await(3, TimeUnit.SECONDS);
Assert.assertEquals(expected, result);
String actualRpcResponse = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
// TODO: add correct validation of proto requests to device
assertTrue(callback.getPayloadBytes().length > 0);
assertEquals("{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}", actualRpcResponse);
client.disconnect();
}
protected void processProtoTwoWayRpcTestGateway(String deviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
byte[] payloadBytes = connectMsgProto.toByteArray();
validateProtoTwoWayRpcGatewayResponse(deviceName, client, payloadBytes);
client.disconnect();
}
protected void processProtoOneWayRpcTestGateway(String deviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
byte[] payloadBytes = connectMsgProto.toByteArray();
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes);
client.disconnect();
}
private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) {
@ -175,29 +188,30 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
doPostAsync("/api/rpc/twoway/" + deviceId, JacksonUtil.toString(request), String.class, status().isOk());
}
MqttAsyncClient client = getMqttAsyncClient(accessToken);
client.setManualAcks(true);
CountDownLatch latch = new CountDownLatch(10);
TestSequenceMqttCallback callback = new TestSequenceMqttCallback(client, latch, result);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
client.enableManualAcks();
MqttTestSequenceCallback callback = new MqttTestSequenceCallback(client, 10, result);
client.setCallback(callback);
client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, 1);
client.subscribeAndWait(DEVICE_RPC_REQUESTS_SUB_TOPIC, MqttQoS.AT_LEAST_ONCE);
latch.await(10, TimeUnit.SECONDS);
Assert.assertEquals(expected, result);
callback.getSubscribeLatch().await(10, TimeUnit.SECONDS);
assertEquals(expected, result);
}
protected void processJsonTwoWayRpcTestGateway(String deviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
String payload = "{\"device\":\"" + deviceName + "\"}";
byte[] payloadBytes = payload.getBytes();
validateJsonTwoWayRpcGatewayResponse(deviceName, client, payloadBytes);
client.disconnect();
}
protected void validateOneWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
protected void validateOneWayRpcGatewayResponse(String deviceName, MqttTestClient client, byte[] connectPayloadBytes) throws Exception {
client.publish(GATEWAY_CONNECT_TOPIC, connectPayloadBytes);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> getDeviceByName(deviceName),
20,
@ -205,24 +219,45 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
);
assertNotNull(savedDevice);
CountDownLatch latch = new CountDownLatch(1);
TestOneWayMqttCallback callback = new TestOneWayMqttCallback(client, latch);
MqttTestCallback callback = new MqttTestCallback(GATEWAY_RPC_TOPIC);
client.setCallback(callback);
client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
client.subscribeAndWait(GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE);
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
Assert.assertTrue(StringUtils.isEmpty(result));
latch.await(3, TimeUnit.SECONDS);
assertTrue(StringUtils.isEmpty(result));
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
DeviceTransportType deviceTransportType = deviceProfile.getTransportType();
if (deviceTransportType.equals(DeviceTransportType.MQTT)) {
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration);
MqttDeviceProfileTransportConfiguration configuration = (MqttDeviceProfileTransportConfiguration) transportConfiguration;
TransportPayloadType transportPayloadType = configuration.getTransportPayloadTypeConfiguration().getTransportPayloadType();
if (transportPayloadType.equals(TransportPayloadType.PROTOBUF)) {
// TODO: add correct validation of proto requests to device
assertTrue(callback.getPayloadBytes().length > 0);
} else {
JsonNode expectedJsonRequestData = getExpectedGatewayJsonRequestData(deviceName, setGpioRequest);
assertEquals(expectedJsonRequestData, JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
} else {
JsonNode expectedJsonRequestData = getExpectedGatewayJsonRequestData(deviceName, setGpioRequest);
assertEquals(expectedJsonRequestData, JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
}
protected void validateJsonTwoWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
private JsonNode getExpectedGatewayJsonRequestData(String deviceName, String requestStr) {
ObjectNode deviceData = (ObjectNode) JacksonUtil.toJsonNode(requestStr);
deviceData.put("id", 0);
ObjectNode expectedRequest = JacksonUtil.newObjectNode();
expectedRequest.put("device", deviceName);
expectedRequest.set("data", deviceData);
return expectedRequest;
}
protected void validateJsonTwoWayRpcGatewayResponse(String deviceName, MqttTestClient client, byte[] connectPayloadBytes) throws Exception {
client.publish(GATEWAY_CONNECT_TOPIC, connectPayloadBytes);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> getDeviceByName(deviceName),
@ -231,25 +266,21 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
);
assertNotNull(savedDevice);
CountDownLatch latch = new CountDownLatch(1);
TestJsonMqttCallback callback = new TestJsonMqttCallback(client, latch);
MqttTestRpcJsonCallback callback = new MqttTestRpcJsonCallback(client, GATEWAY_RPC_TOPIC);
client.setCallback(callback);
client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
client.subscribeAndWait(GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE);
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
String expected = "{\"success\":true}";
assertEquals(expected, result);
String actualRpcResponse = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
log.warn("request payload: {}", JacksonUtil.fromBytes(callback.getPayloadBytes()));
assertEquals("{\"success\":true}", actualRpcResponse);
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
}
protected void validateProtoTwoWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
protected void validateProtoTwoWayRpcGatewayResponse(String deviceName, MqttTestClient client, byte[] connectPayloadBytes) throws Exception {
client.publish(GATEWAY_CONNECT_TOPIC, connectPayloadBytes);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> getDeviceByName(deviceName),
@ -258,20 +289,15 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
);
assertNotNull(savedDevice);
CountDownLatch latch = new CountDownLatch(1);
TestProtoMqttCallback callback = new TestProtoMqttCallback(client, latch);
MqttTestRpcProtoCallback callback = new MqttTestRpcProtoCallback(client, GATEWAY_RPC_TOPIC);
client.setCallback(callback);
client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(1000);
client.subscribeAndWait(GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE);
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
String expected = "{\"success\":true}";
assertEquals(expected, result);
String actualRpcResponse = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
callback.getSubscribeLatch().await(3, TimeUnit.SECONDS);
assertEquals("{\"success\":true}", actualRpcResponse);
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
}
@ -279,132 +305,82 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
return doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
}
protected MqttMessage processJsonMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
MqttMessage message = new MqttMessage();
if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC) || requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC_V2)) {
message.setPayload(DEVICE_RESPONSE.getBytes(StandardCharset.UTF_8));
protected byte[] processJsonMessageArrived(String requestTopic, MqttMessage mqttMessage) {
if (requestTopic.startsWith(BASE_DEVICE_API_TOPIC) || requestTopic.startsWith(BASE_DEVICE_API_TOPIC_V2)) {
return DEVICE_RESPONSE.getBytes(StandardCharset.UTF_8);
} else {
JsonNode requestMsgNode = JacksonUtil.toJsonNode(new String(mqttMessage.getPayload(), StandardCharset.UTF_8));
String deviceName = requestMsgNode.get("device").asText();
int requestId = requestMsgNode.get("data").get("id").asInt();
message.setPayload(("{\"device\": \"" + deviceName + "\", \"id\": " + requestId + ", \"data\": {\"success\": true}}").getBytes(StandardCharset.UTF_8));
}
return message;
}
protected class TestOneWayMqttCallback implements MqttCallback {
private final MqttAsyncClient client;
private final CountDownLatch latch;
private Integer qoS;
TestOneWayMqttCallback(MqttAsyncClient client, CountDownLatch latch) {
this.client = client;
this.latch = latch;
}
int getQoS() {
return qoS;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
qoS = mqttMessage.getQos();
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
String response = "{\"device\": \"" + deviceName + "\", \"id\": " + requestId + ", \"data\": {\"success\": true}}";
return response.getBytes(StandardCharset.UTF_8);
}
}
protected class TestJsonMqttCallback implements MqttCallback {
protected class MqttTestRpcJsonCallback extends MqttTestCallback {
private final MqttAsyncClient client;
private final CountDownLatch latch;
private Integer qoS;
private final MqttTestClient client;
TestJsonMqttCallback(MqttAsyncClient client, CountDownLatch latch) {
public MqttTestRpcJsonCallback(MqttTestClient client, String awaitSubTopic) {
super(awaitSubTopic);
this.client = client;
this.latch = latch;
}
int getQoS() {
return qoS;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
String responseTopic;
if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC_V2)) {
responseTopic = requestTopic.replace("req", "res");
} else {
responseTopic = requestTopic.replace("request", "response");
protected void messageArrivedOnAwaitSubTopic(String requestTopic, MqttMessage mqttMessage) {
log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic);
if (awaitSubTopic.equals(requestTopic)) {
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
String responseTopic;
if (requestTopic.startsWith(BASE_DEVICE_API_TOPIC_V2)) {
responseTopic = requestTopic.replace("req", "res");
} else {
responseTopic = requestTopic.replace("request", "response");
}
try {
client.publish(responseTopic, processJsonMessageArrived(requestTopic, mqttMessage));
} catch (MqttException e) {
log.warn("Failed to publish response on topic: {} due to: ", responseTopic, e);
}
subscribeLatch.countDown();
}
qoS = mqttMessage.getQos();
client.publish(responseTopic, processJsonMessageArrived(requestTopic, mqttMessage));
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
}
protected class TestProtoMqttCallback implements MqttCallback {
protected class MqttTestRpcProtoCallback extends MqttTestCallback {
private final MqttAsyncClient client;
private final CountDownLatch latch;
private Integer qoS;
private final MqttTestClient client;
TestProtoMqttCallback(MqttAsyncClient client, CountDownLatch latch) {
public MqttTestRpcProtoCallback(MqttTestClient client, String awaitSubTopic) {
super(awaitSubTopic);
this.client = client;
this.latch = latch;
}
int getQoS() {
return qoS;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
String responseTopic;
if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC_V2)) {
responseTopic = requestTopic.replace("req", "res");
} else {
responseTopic = requestTopic.replace("request", "response");
protected void messageArrivedOnAwaitSubTopic(String requestTopic, MqttMessage mqttMessage) {
log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic);
if (awaitSubTopic.equals(requestTopic)) {
qoS = mqttMessage.getQos();
payloadBytes = mqttMessage.getPayload();
String responseTopic;
if (requestTopic.startsWith(BASE_DEVICE_API_TOPIC_V2)) {
responseTopic = requestTopic.replace("req", "res");
} else {
responseTopic = requestTopic.replace("request", "response");
}
try {
client.publish(responseTopic, processProtoMessageArrived(requestTopic, mqttMessage));
} catch (Exception e) {
log.warn("Failed to publish response on topic: {} due to: ", responseTopic, e);
}
subscribeLatch.countDown();
}
qoS = mqttMessage.getQos();
client.publish(responseTopic, processProtoMessageArrived(requestTopic, mqttMessage));
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
}
protected MqttMessage processProtoMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
MqttMessage message = new MqttMessage();
if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC) || requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC_V2)) {
protected byte[] processProtoMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException {
if (requestTopic.startsWith(BASE_DEVICE_API_TOPIC) || requestTopic.startsWith(BASE_DEVICE_API_TOPIC_V2)) {
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = getProtoTransportPayloadConfiguration();
ProtoFileElement rpcRequestProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(RPC_REQUEST_PROTO_SCHEMA);
DynamicSchema rpcRequestProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(rpcRequestProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_REQUEST_PROTO_SCHEMA);
@ -428,9 +404,9 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
DynamicMessage rpcResponseMsg = rpcResponseBuilder
.setField(rpcResponseMsgDescriptor.findFieldByName("payload"), DEVICE_RESPONSE)
.build();
message.setPayload(rpcResponseMsg.toByteArray());
return rpcResponseMsg.toByteArray();
} catch (InvalidProtocolBufferException e) {
log.warn("Command Response Ack Error, Invalid response received: ", e);
throw new RuntimeException("Command Response Ack Error, Invalid response received: ", e);
}
} else {
TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload());
@ -441,9 +417,8 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
.setId(requestId)
.setData("{\"success\": true}")
.build();
message.setPayload(gatewayRpcResponseMsg.toByteArray());
return gatewayRpcResponseMsg.toByteArray();
}
return message;
}
private ProtoTransportPayloadConfiguration getProtoTransportPayloadConfiguration() {
@ -455,37 +430,30 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
return (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration;
}
protected class TestSequenceMqttCallback implements MqttCallback {
protected class MqttTestSequenceCallback extends MqttTestCallback {
private final MqttAsyncClient client;
private final CountDownLatch latch;
private final MqttTestClient client;
private final List<String> expected;
TestSequenceMqttCallback(MqttAsyncClient client, CountDownLatch latch, List<String> expected) {
MqttTestSequenceCallback(MqttTestClient client, int subscribeCount, List<String> expected) {
super(subscribeCount);
this.client = client;
this.latch = latch;
this.expected = expected;
}
@Override
public void connectionLost(Throwable throwable) {
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload()));
public void messageArrived(String requestTopic, MqttMessage mqttMessage) {
log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic);
expected.add(new String(mqttMessage.getPayload()));
String responseTopic = requestTopic.replace("request", "response");
var qoS = mqttMessage.getQos();
client.messageArrivedComplete(mqttMessage.getId(), qoS);
client.publish(responseTopic, processJsonMessageArrived(requestTopic, mqttMessage));
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
qoS = mqttMessage.getQos();
try {
client.messageArrivedComplete(mqttMessage);
client.publish(responseTopic, processJsonMessageArrived(requestTopic, mqttMessage));
} catch (MqttException e) {
log.warn("Failed to publish response on topic: {} due to: ", responseTopic, e);
}
subscribeLatch.countDown();
}
}
}

23
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcBackwardCompatibilityIntegrationTest.java

@ -16,13 +16,16 @@
package org.thingsboard.server.transport.mqtt.rpc;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
@ -37,7 +40,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
processBeforeTest(configProperties);
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
@ -50,7 +53,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
@ -63,7 +66,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
}
@Test
@ -75,7 +78,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.enableCompatibilityWithJsonPayloadFormat(true)
.build();
super.processBeforeTest(configProperties);
processProtoTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processProtoTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
@ -88,7 +91,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
@ -99,7 +102,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.rpcRequestProtoSchema(RPC_REQUEST_PROTO_SCHEMA)
.build();
super.processBeforeTest(configProperties);
processProtoTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processProtoTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
@ -112,7 +115,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processProtoTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
processProtoTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
}
@Test
@ -125,7 +128,7 @@ public class MqttServerSideRpcBackwardCompatibilityIntegrationTest extends Abstr
.useJsonPayloadFormatForDefaultDownlinkTopics(true)
.build();
super.processBeforeTest(configProperties);
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
}
@Test

16
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcDefaultIntegrationTest.java

@ -20,12 +20,14 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC;
@Slf4j
@DaoSqlTest
@ -80,32 +82,32 @@ public class MqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerS
@Test
public void testServerMqttOneWayRpc() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
public void testServerMqttOneWayRpcOnShortTopic() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
public void testServerMqttOneWayRpcOnShortJsonTopic() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
}
@Test
public void testServerMqttTwoWayRpc() throws Exception {
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
public void testServerMqttTwoWayRpcOnShortTopic() throws Exception {
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
public void testServerMqttTwoWayRpcOnShortJsonTopic() throws Exception {
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
}
@Test

23
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcJsonIntegrationTest.java

@ -16,14 +16,17 @@
package org.thingsboard.server.transport.mqtt.rpc;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
@ -40,32 +43,32 @@ public class MqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSide
@Test
public void testServerMqttOneWayRpc() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
public void testServerMqttOneWayRpcOnShortTopic() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
public void testServerMqttOneWayRpcOnShortJsonTopic() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
}
@Test
public void testServerMqttTwoWayRpc() throws Exception {
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
public void testServerMqttTwoWayRpcOnShortTopic() throws Exception {
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
public void testServerMqttTwoWayRpcOnShortJsonTopic() throws Exception {
processJsonTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC);
}
@Test
@ -79,10 +82,12 @@ public class MqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSide
}
protected void processJsonOneWayRpcTestGateway(String deviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
String payload = "{\"device\": \"" + deviceName + "\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}";
byte[] payloadBytes = payload.getBytes();
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes);
client.disconnect();
}
}

17
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/MqttServerSideRpcProtoIntegrationTest.java

@ -19,10 +19,13 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC;
@Slf4j
@DaoSqlTest
public class MqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
@ -40,32 +43,32 @@ public class MqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSid
@Test
public void testServerMqttOneWayRpc() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
public void testServerMqttOneWayRpcOnShortTopic() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
public void testServerMqttOneWayRpcOnShortProtoTopic() throws Exception {
processOneWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
processOneWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
}
@Test
public void testServerMqttTwoWayRpc() throws Exception {
processProtoTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC);
processProtoTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_TOPIC);
}
@Test
public void testServerMqttTwoWayRpcOnShortTopic() throws Exception {
processProtoTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
processProtoTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_TOPIC);
}
@Test
public void testServerMqttTwoWayRpcOnShortProtoTopic() throws Exception {
processProtoTwoWayRpcTest(MqttTopics.DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
processProtoTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_PROTO_TOPIC);
}
@Test

48
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/MqttAttributesIntegrationTest.java

@ -18,14 +18,13 @@ package org.thingsboard.server.transport.mqtt.telemetry.attributes;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import java.util.Arrays;
@ -38,6 +37,10 @@ import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_ATTRIBUTES_TOPIC;
@Slf4j
@DaoSqlTest
@ -58,19 +61,19 @@ public class MqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
@Test
public void testPushAttributes() throws Exception {
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
processJsonPayloadAttributesTest(DEVICE_ATTRIBUTES_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
}
@Test
public void testPushAttributesOnShortTopic() throws Exception {
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
processJsonPayloadAttributesTest(DEVICE_ATTRIBUTES_SHORT_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
}
@Test
public void testPushAttributesOnShortJsonTopic() throws Exception {
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
processJsonPayloadAttributesTest(DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes());
}
@Test
@ -87,9 +90,11 @@ public class MqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
}
protected void processAttributesTest(String topic, List<String> expectedKeys, byte[] payload, boolean presenceFieldsTest) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
publishMqttMsg(client, payload, topic);
client.publishAndWait(topic, payload);
client.disconnect();
DeviceId deviceId = savedDevice.getId();
@ -125,9 +130,11 @@ public class MqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
}
protected void processGatewayAttributesTest(List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
publishMqttMsg(client, payload, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC);
client.publishAndWait(GATEWAY_ATTRIBUTES_TOPIC, payload);
client.disconnect();
Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class),
20,
@ -141,12 +148,12 @@ public class MqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
assertNotNull(secondDevice);
Thread.sleep(2000);
List<String> firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
List<String> firstDeviceActualKeys = getActualKeysList(firstDevice.getId(), expectedKeys);
assertNotNull(firstDeviceActualKeys);
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
List<String> secondDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
List<String> secondDeviceActualKeys = getActualKeysList(secondDevice.getId(), expectedKeys);
assertNotNull(secondDeviceActualKeys);
Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
Set<String> expectedKeySet = new HashSet<>(expectedKeys);
@ -165,6 +172,21 @@ public class MqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
}
private List<String> getActualKeysList(DeviceId deviceId, List<String> expectedKeys) throws Exception {
long start = System.currentTimeMillis();
long end = System.currentTimeMillis() + 3000;
List<String> firstDeviceActualKeys = null;
while (start <= end) {
firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() {});
if (firstDeviceActualKeys.size() == expectedKeys.size()) {
break;
}
Thread.sleep(100);
start += 100;
}
return firstDeviceActualKeys;
}
@SuppressWarnings("unchecked")
protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> keySet) throws JsonProcessingException {
for (Map<String, Object> map : deviceValues) {

10
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/MqttAttributesProtoIntegrationTest.java

@ -25,7 +25,6 @@ import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.dao.service.DaoSqlTest;
@ -38,6 +37,9 @@ import java.util.List;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC;
@Slf4j
@DaoSqlTest
@ -122,7 +124,7 @@ public class MqttAttributesProtoIntegrationTest extends MqttAttributesIntegratio
.build();
processBeforeTest(configProperties);
DynamicMessage postAttributesMsg = getDefaultDynamicMessage();
processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
processAttributesTest(DEVICE_ATTRIBUTES_SHORT_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
}
@Test
@ -133,7 +135,7 @@ public class MqttAttributesProtoIntegrationTest extends MqttAttributesIntegratio
.attributesTopicFilter(POST_DATA_ATTRIBUTES_TOPIC)
.build();
processBeforeTest(configProperties);
processJsonPayloadAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes());
processJsonPayloadAttributesTest(DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes());
}
@Test
@ -145,7 +147,7 @@ public class MqttAttributesProtoIntegrationTest extends MqttAttributesIntegratio
.build();
processBeforeTest(configProperties);
DynamicMessage postAttributesMsg = getDefaultDynamicMessage();
processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
processAttributesTest(DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
}
@Test

172
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java

@ -18,19 +18,13 @@ package org.thingsboard.server.transport.mqtt.telemetry.timeseries;
import com.fasterxml.jackson.core.type.TypeReference;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import java.util.Arrays;
@ -38,12 +32,17 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_CONNECT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_TELEMETRY_TOPIC;
@Slf4j
public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest {
@ -66,26 +65,26 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
@Test
public void testPushTelemetry() throws Exception {
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
processJsonPayloadTelemetryTest(DEVICE_TELEMETRY_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
}
@Test
public void testPushTelemetryWithTs() throws Exception {
String payloadStr = "{\"ts\": 10000, \"values\": " + PAYLOAD_VALUES_STR + "}";
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true);
processJsonPayloadTelemetryTest(DEVICE_TELEMETRY_TOPIC, expectedKeys, payloadStr.getBytes(), true);
}
@Test
public void testPushTelemetryOnShortTopic() throws Exception {
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
processJsonPayloadTelemetryTest(DEVICE_TELEMETRY_SHORT_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
}
@Test
public void testPushTelemetryOnShortJsonTopic() throws Exception {
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
processJsonPayloadTelemetryTest(DEVICE_TELEMETRY_SHORT_JSON_TOPIC, expectedKeys, PAYLOAD_VALUES_STR.getBytes(), false);
}
@Test
@ -94,14 +93,15 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
String deviceName1 = "Device A";
String deviceName2 = "Device B";
String payload = getGatewayTelemetryJsonPayload(deviceName1, deviceName2, "10000", "20000");
processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2);
processGatewayTelemetryTest(GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2);
}
@Test
public void testGatewayConnect() throws Exception {
String payload = "{\"device\":\"Device A\"}";
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
client.publish(GATEWAY_CONNECT_TOPIC, payload.getBytes());
String deviceName = "Device A";
@ -110,6 +110,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
100);
assertNotNull(device);
client.disconnect();
}
protected void processJsonPayloadTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs) throws Exception {
@ -117,23 +118,14 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
}
protected void processTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, boolean withTs, boolean presenceFieldsTest) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(accessToken);
publishMqttMsg(client, payload, topic);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
client.publishAndWait(topic, payload);
client.disconnect();
String deviceId = savedDevice.getId().getId().toString();
DeviceId deviceId = savedDevice.getId();
long start = System.currentTimeMillis();
long end = System.currentTimeMillis() + 5000;
List<String> actualKeys = null;
while (start <= end) {
actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", new TypeReference<>() {});
if (actualKeys.size() == expectedKeys.size()) {
break;
}
Thread.sleep(100);
start += 100;
}
List<String> actualKeys = getActualKeysList(deviceId, expectedKeys);
assertNotNull(actualKeys);
Set<String> actualKeySet = new HashSet<>(actualKeys);
@ -147,8 +139,8 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
} else {
getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet);
}
start = System.currentTimeMillis();
end = System.currentTimeMillis() + 5000;
long start = System.currentTimeMillis();
long end = System.currentTimeMillis() + 5000;
Map<String, List<Map<String, Object>>> values = null;
while (start <= end) {
values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() {});
@ -187,9 +179,10 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
}
protected void processGatewayTelemetryTest(String topic, List<String> expectedKeys, byte[] payload, String firstDeviceName, String secondDeviceName) throws Exception {
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
publishMqttMsg(client, payload, topic);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
client.publishAndWait(topic, payload);
client.disconnect();
Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class),
20,
@ -203,12 +196,10 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
assertNotNull(secondDevice);
Thread.sleep(2000);
List<String> firstDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", new TypeReference<>() {});
List<String> firstDeviceActualKeys = getActualKeysList(firstDevice.getId(), expectedKeys);
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);
List<String> secondDeviceActualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + secondDevice.getId() + "/keys/timeseries", new TypeReference<>() {});
List<String> secondDeviceActualKeys = getActualKeysList(secondDevice.getId(), expectedKeys);
Set<String> secondDeviceActualKeySet = new HashSet<>(secondDeviceActualKeys);
Set<String> expectedKeySet = new HashSet<>(expectedKeys);
@ -226,6 +217,22 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
assertGatewayDeviceData(secondDeviceValues, expectedKeys);
}
private List<String> getActualKeysList(DeviceId deviceId, List<String> expectedKeys) throws Exception {
long start = System.currentTimeMillis();
long end = System.currentTimeMillis() + 3000;
List<String> actualKeys = null;
while (start <= end) {
actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", new TypeReference<>() {});
if (actualKeys.size() == expectedKeys.size()) {
break;
}
Thread.sleep(100);
start += 100;
}
return actualKeys;
}
protected String getGatewayTelemetryJsonPayload(String deviceA, String deviceB, String firstTsValue, String secondTsValue) {
String payload = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}, " +
"{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]";
@ -312,93 +319,20 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
// @Test - Unstable
public void testMqttQoSLevel() throws Exception {
String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken);
CountDownLatch latch = new CountDownLatch(1);
TestMqttCallback callback = new TestMqttCallback(client, latch);
MqttTestClient client = new MqttTestClient();
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
client.connect(options).waitForCompletion(5000);
client.subscribe("v1/devices/me/attributes", MqttQoS.AT_MOST_ONCE.value());
client.connectAndWait(accessToken);
client.subscribe(DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE);
String payload = "{\"key\":\"uniqueValue\"}";
// TODO 3.1: we need to acknowledge subscription only after it is processed by device actor and not when the message is pushed to queue.
// MqttClient -> SUB REQUEST -> Transport -> Kafka -> Device Actor (subscribed)
// MqttClient <- SUB_ACK <- Transport
Thread.sleep(5000);
doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk());
latch.await(10, TimeUnit.SECONDS);
assertEquals(payload, callback.getPayload());
callback.getSubscribeLatch().await(10, TimeUnit.SECONDS);
assertEquals(payload.getBytes(), callback.getPayloadBytes());
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
}
private static class TestMqttCallback implements MqttCallback {
private final MqttAsyncClient client;
private final CountDownLatch latch;
private volatile Integer qoS;
private volatile String payload;
String getPayload() {
return payload;
}
TestMqttCallback(MqttAsyncClient client, CountDownLatch latch) {
this.client = client;
this.latch = latch;
}
int getQoS() {
return qoS;
}
@Override
public void connectionLost(Throwable throwable) {
log.error("Client connection lost", throwable);
}
@Override
public void messageArrived(String requestTopic, MqttMessage mqttMessage) {
payload = new String(mqttMessage.getPayload());
qoS = mqttMessage.getQos();
latch.countDown();
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
}
public static class TestMqttPublishCallback implements MqttCallback {
private final CountDownLatch latch;
private boolean pubAckReceived;
public boolean isPubAckReceived() {
return pubAckReceived;
}
public TestMqttPublishCallback(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void connectionLost(Throwable throwable) {
latch.countDown();
}
@Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
pubAckReceived = iMqttDeliveryToken.getResponse().getType() == MqttWireMessage.MESSAGE_TYPE_PUBACK;
latch.countDown();
}
}
}

46
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java

@ -16,15 +16,15 @@
package org.thingsboard.server.transport.mqtt.telemetry.timeseries;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertFalse;
@ -121,13 +121,14 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract
.sendAckOnValidationException(true)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_JSON_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_JSON_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertTrue(callback.isPubAckReceived());
client.disconnect();
}
@Test
@ -138,12 +139,12 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract
.telemetryTopicFilter(POST_DATA_TELEMETRY_TOPIC)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_JSON_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_JSON_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertFalse(callback.isPubAckReceived());
}
@ -157,13 +158,14 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract
.sendAckOnValidationException(true)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_JSON_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_JSON_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertTrue(callback.isPubAckReceived());
client.disconnect();
}
@Test
@ -175,12 +177,12 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract
.telemetryTopicFilter(POST_DATA_TELEMETRY_TOPIC)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_JSON_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_JSON_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertFalse(callback.isPubAckReceived());
}

87
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java

@ -20,28 +20,32 @@ import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.mqtt.MqttTestCallback;
import org.thingsboard.server.transport.mqtt.MqttTestClient;
import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_SHORT_PROTO_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_SHORT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_CONNECT_TOPIC;
import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_TELEMETRY_TOPIC;
@Slf4j
public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest {
@ -272,7 +276,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.build();
processBeforeTest(configProperties);
DynamicMessage postTelemetryMsg = getDefaultDynamicMessage();
processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
processTelemetryTest(DEVICE_TELEMETRY_SHORT_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
}
@Test
@ -283,7 +287,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.telemetryTopicFilter(POST_DATA_TELEMETRY_TOPIC)
.build();
processBeforeTest(configProperties);
processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes(), false);
processJsonPayloadTelemetryTest(DEVICE_TELEMETRY_SHORT_JSON_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes(), false);
}
@Test
@ -295,7 +299,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.build();
processBeforeTest(configProperties);
DynamicMessage postTelemetryMsg = getDefaultDynamicMessage();
processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_PROTO_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
processTelemetryTest(DEVICE_TELEMETRY_SHORT_PROTO_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
}
@Test
@ -315,7 +319,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
TransportApiProtos.TelemetryMsg deviceBTelemetryMsgProto = getDeviceTelemetryMsgProto(deviceName2, expectedKeys, 10000, 20000);
gatewayTelemetryMsgProtoBuilder.addAllMsg(Arrays.asList(deviceATelemetryMsgProto, deviceBTelemetryMsgProto));
TransportApiProtos.GatewayTelemetryMsg gatewayTelemetryMsg = gatewayTelemetryMsgProtoBuilder.build();
processGatewayTelemetryTest(MqttTopics.GATEWAY_TELEMETRY_TOPIC, expectedKeys, gatewayTelemetryMsg.toByteArray(), deviceName1, deviceName2);
processGatewayTelemetryTest(GATEWAY_TELEMETRY_TOPIC, expectedKeys, gatewayTelemetryMsg.toByteArray(), deviceName1, deviceName2);
}
@Test
@ -329,14 +333,16 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
processBeforeTest(configProperties);
String deviceName = "Device A";
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
publishMqttMsg(client, connectMsgProto.toByteArray(), MqttTopics.GATEWAY_CONNECT_TOPIC);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
client.publish(GATEWAY_CONNECT_TOPIC, connectMsgProto.toByteArray());
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(device);
client.disconnect();
}
@ -349,13 +355,14 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.sendAckOnValidationException(true)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_PROTO_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_PROTO_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertTrue(callback.isPubAckReceived());
client.disconnect();
}
@Test
@ -366,12 +373,12 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.telemetryTopicFilter(POST_DATA_TELEMETRY_TOPIC)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_PROTO_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_PROTO_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertFalse(callback.isPubAckReceived());
}
@ -385,13 +392,14 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.sendAckOnValidationException(true)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_JSON_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_JSON_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertTrue(callback.isPubAckReceived());
client.disconnect();
}
@Test
@ -403,12 +411,12 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.enableCompatibilityWithJsonPayloadFormat(true)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(accessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(accessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_JSON_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_JSON_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertFalse(callback.isPubAckReceived());
}
@ -422,13 +430,14 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.sendAckOnValidationException(true)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_PROTO_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_PROTO_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertTrue(callback.isPubAckReceived());
client.disconnect();
}
@Test
@ -440,12 +449,12 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
.telemetryTopicFilter(POST_DATA_TELEMETRY_TOPIC)
.build();
processBeforeTest(configProperties);
CountDownLatch latch = new CountDownLatch(1);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
TestMqttPublishCallback callback = new TestMqttPublishCallback(latch);
MqttTestClient client = new MqttTestClient();
client.connectAndWait(gatewayAccessToken);
MqttTestCallback callback = new MqttTestCallback();
client.setCallback(callback);
publishMqttMsg(client, MALFORMED_PROTO_PAYLOAD.getBytes(), POST_DATA_TELEMETRY_TOPIC);
latch.await(3, TimeUnit.SECONDS);
client.publish(POST_DATA_TELEMETRY_TOPIC, MALFORMED_PROTO_PAYLOAD.getBytes());
callback.getDeliveryLatch().await(3, TimeUnit.SECONDS);
assertFalse(callback.isPubAckReceived());
}

5
dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java

@ -141,6 +141,7 @@ public class CachedAttributesService implements AttributesService {
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (wrappedCachedAttributes.size() == attributeKeys.size()) {
log.trace("[{}][{}] Found all attributes from cache: {}", entityId, scope, attributeKeys);
return Futures.immediateFuture(cachedAttributes);
}
@ -152,6 +153,7 @@ public class CachedAttributesService implements AttributesService {
return cacheExecutor.submit(() -> {
var cacheTransaction = cache.newTransactionForKeys(notFoundKeys);
try {
log.trace("[{}][{}] Lookup attributes from db: {}", entityId, scope, notFoundAttributeKeys);
List<AttributeKvEntry> result = attributesDao.find(tenantId, entityId, scope, notFoundAttributeKeys);
for (AttributeKvEntry foundInDbAttribute : result) {
AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, foundInDbAttribute.getKey());
@ -164,6 +166,7 @@ public class CachedAttributesService implements AttributesService {
List<AttributeKvEntry> mergedAttributes = new ArrayList<>(cachedAttributes);
mergedAttributes.addAll(result);
cacheTransaction.commit();
log.trace("[{}][{}] Commit cache transaction: {}", entityId, scope, notFoundAttributeKeys);
return mergedAttributes;
} catch (Throwable e) {
cacheTransaction.rollback();
@ -211,7 +214,9 @@ public class CachedAttributesService implements AttributesService {
for (var attribute : attributes) {
ListenableFuture<String> future = attributesDao.save(tenantId, entityId, scope, attribute);
futures.add(Futures.transform(future, key -> {
log.trace("[{}][{}][{}] Before cache evict: {}", entityId, scope, key, attribute);
cache.evictOrPut(new AttributeCacheKey(scope, entityId, key), attribute);
log.trace("[{}][{}][{}] after cache evict.", entityId, scope, key);
return key;
}, cacheExecutor));
}

3
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java

@ -408,9 +408,8 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
private Device createDeviceThroughGateway(MqttClient mqttClient, Device gatewayDevice) throws Exception {
String deviceName = "mqtt_device";
mqttClient.publish("v1/gateway/connect", Unpooled.wrappedBuffer(createGatewayConnectPayload(deviceName).toString().getBytes())).get();
mqttClient.publish("v1/gateway/connect", Unpooled.wrappedBuffer(createGatewayConnectPayload(deviceName).toString().getBytes()), MqttQoS.AT_LEAST_ONCE).get();
TimeUnit.SECONDS.sleep(3);
List<EntityRelation> relations = restClient.findByFrom(gatewayDevice.getId(), RelationTypeGroup.COMMON);
Assert.assertEquals(1, relations.size());

10
ui-ngx/src/app/core/api/alias-controller.ts

@ -17,14 +17,15 @@
import { AliasInfo, IAliasController, StateControllerHolder, StateEntityInfo } from '@core/api/widget-api.models';
import { forkJoin, Observable, of, ReplaySubject, Subject } from 'rxjs';
import { Datasource, DatasourceType, datasourceTypeTranslationMap } from '@app/shared/models/widget.models';
import { deepClone, isEqual } from '@core/utils';
import { deepClone, isDefinedAndNotNull, isEqual } from '@core/utils';
import { EntityService } from '@core/http/entity.service';
import { UtilsService } from '@core/services/utils.service';
import { AliasFilterType, EntityAliases, SingleEntityFilter } from '@shared/models/alias.models';
import { EntityInfo } from '@shared/models/entity.models';
import { map, mergeMap } from 'rxjs/operators';
import {
defaultEntityDataPageLink, Filter, FilterInfo, filterInfoToKeyFilters, Filters, KeyFilter, singleEntityDataPageLink,
createDefaultEntityDataPageLink,
Filter, FilterInfo, filterInfoToKeyFilters, Filters, KeyFilter, singleEntityDataPageLink,
updateDatasourceFromEntityInfo
} from '@shared/models/query/query.models';
import { TranslateService } from '@ngx-translate/core';
@ -322,7 +323,7 @@ export class AliasController implements IAliasController {
);
}
resolveDatasources(datasources: Array<Datasource>, singleEntity?: boolean): Observable<Array<Datasource>> {
resolveDatasources(datasources: Array<Datasource>, singleEntity?: boolean, pageSize = 1024): Observable<Array<Datasource>> {
if (!datasources || !datasources.length) {
return of([]);
}
@ -360,7 +361,8 @@ export class AliasController implements IAliasController {
if (singleEntity) {
datasource.pageLink = deepClone(singleEntityDataPageLink);
} else if (!datasource.pageLink) {
datasource.pageLink = deepClone(defaultEntityDataPageLink);
pageSize = isDefinedAndNotNull(pageSize) && pageSize > 0 ? pageSize : 1024;
datasource.pageLink = createDefaultEntityDataPageLink(pageSize);
}
}
});

4
ui-ngx/src/app/core/api/widget-api.models.ts

@ -119,7 +119,7 @@ export interface IAliasController {
getEntityAliasId(aliasName: string): string;
getInstantAliasInfo(aliasId: string): AliasInfo;
resolveSingleEntityInfo(aliasId: string): Observable<EntityInfo>;
resolveDatasources(datasources: Array<Datasource>, singleEntity?: boolean): Observable<Array<Datasource>>;
resolveDatasources(datasources: Array<Datasource>, singleEntity?: boolean, pageSize?: number): Observable<Array<Datasource>>;
resolveAlarmSource(alarmSource: Datasource): Observable<Datasource>;
getEntityAliases(): EntityAliases;
getFilters(): Filters;
@ -184,6 +184,7 @@ export interface SubscriptionInfo {
deviceName?: string;
deviceNamePrefix?: string;
deviceIds?: Array<string>;
pageSize?: number;
}
export class WidgetSubscriptionContext {
@ -242,6 +243,7 @@ export interface WidgetSubscriptionOptions {
datasourcesOptional?: boolean;
hasDataPageLink?: boolean;
singleEntity?: boolean;
pageSize?: number;
warnOnPageDataOverflow?: boolean;
ignoreDataUpdateOnIntervalTick?: boolean;
targetDeviceAliasIds?: Array<string>;

8
ui-ngx/src/app/core/api/widget-subscription.ts

@ -97,6 +97,7 @@ export class WidgetSubscription implements IWidgetSubscription {
hasDataPageLink: boolean;
singleEntity: boolean;
pageSize: number;
warnOnPageDataOverflow: boolean;
ignoreDataUpdateOnIntervalTick: boolean;
@ -229,6 +230,7 @@ export class WidgetSubscription implements IWidgetSubscription {
this.entityDataListeners = [];
this.hasDataPageLink = options.hasDataPageLink;
this.singleEntity = options.singleEntity;
this.pageSize = options.pageSize;
this.warnOnPageDataOverflow = options.warnOnPageDataOverflow;
this.ignoreDataUpdateOnIntervalTick = options.ignoreDataUpdateOnIntervalTick;
this.datasourcePages = [];
@ -387,7 +389,7 @@ export class WidgetSubscription implements IWidgetSubscription {
}
);
} else {
this.ctx.aliasController.resolveDatasources(this.configuredDatasources, this.singleEntity).subscribe(
this.ctx.aliasController.resolveDatasources(this.configuredDatasources, this.singleEntity, this.pageSize).subscribe(
(datasources) => {
this.configuredDatasources = datasources;
this.prepareDataSubscriptions().subscribe(
@ -1132,7 +1134,7 @@ export class WidgetSubscription implements IWidgetSubscription {
}
);
} else {
this.ctx.aliasController.resolveDatasources(this.configuredDatasources, this.singleEntity).subscribe(
this.ctx.aliasController.resolveDatasources(this.configuredDatasources, this.singleEntity, this.pageSize).subscribe(
(datasources) => {
this.configuredDatasources = datasources;
this.prepareDataSubscriptions().subscribe(
@ -1271,7 +1273,7 @@ export class WidgetSubscription implements IWidgetSubscription {
totalPages: pageData.totalPages
};
if (datasource.type === DatasourceType.entity &&
pageData.hasNext && pageLink.pageSize > 1) {
pageData.hasNext && !this.singleEntity) {
if (this.warnOnPageDataOverflow) {
const message = this.ctx.translate.instant('widget.data-overflow',
{count: pageData.data.length, total: pageData.totalElements});

6
ui-ngx/src/app/core/http/entity.service.ts

@ -1319,7 +1319,8 @@ export class EntityService {
pageLink = deepClone(singleEntityDataPageLink);
} else {
nameFilter = subscriptionInfo.entityNamePrefix;
pageLink = deepClone(defaultEntityDataPageLink);
const pageSize = isDefinedAndNotNull(subscriptionInfo.pageSize) && subscriptionInfo.pageSize > 0 ? subscriptionInfo.pageSize : 1024;
pageLink = createDefaultEntityDataPageLink(pageSize);
}
datasource.entityFilter = {
type: AliasFilterType.entityName,
@ -1333,7 +1334,8 @@ export class EntityService {
entityType: subscriptionInfo.entityType,
entityList: subscriptionInfo.entityIds
};
datasource.pageLink = deepClone(defaultEntityDataPageLink);
const pageSize = isDefinedAndNotNull(subscriptionInfo.pageSize) && subscriptionInfo.pageSize > 0 ? subscriptionInfo.pageSize : 1024;
datasource.pageLink = createDefaultEntityDataPageLink(pageSize);
}
}

87
ui-ngx/src/app/core/schema-utils.ts

@ -1,87 +0,0 @@
///
/// Copyright © 2016-2022 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { JsonSettingsSchema } from '@shared/models/widget.models';
export function initSchema(): JsonSettingsSchema {
return {
schema: {
type: 'object',
properties: {},
required: []
},
form: [],
groupInfoes: []
};
}
export function addGroupInfo(schema: JsonSettingsSchema, title: string) {
schema.groupInfoes.push({
formIndex: schema.groupInfoes?.length || 0,
GroupTitle: title
});
}
export function addToSchema(schema: JsonSettingsSchema, newSchema: JsonSettingsSchema) {
Object.assign(schema.schema.properties, newSchema.schema.properties);
schema.schema.required = schema.schema.required.concat(newSchema.schema.required);
schema.form.push(newSchema.form);
}
export function mergeSchemes(schemes: JsonSettingsSchema[]): JsonSettingsSchema {
return schemes.reduce((finalSchema: JsonSettingsSchema, schema: JsonSettingsSchema) => {
return {
schema: {
properties: {
...finalSchema.schema.properties,
...schema.schema.properties
},
required: [
...finalSchema.schema.required,
...schema.schema.required
]
},
form: [
...finalSchema.form,
...schema.form
]
} as JsonSettingsSchema;
}, initSchema());
}
export function addCondition(schema: JsonSettingsSchema, condition: string, exclude: string[] = []): JsonSettingsSchema {
schema.form = schema.form.map(element => {
if (!exclude.includes(element) && !exclude.includes(element.key)) {
if (typeof element === 'string') {
return {
key: element,
condition
};
}
if (typeof element === 'object') {
if (element.condition) {
element.condition += ' && ' + condition;
}
else {
element.condition = condition;
}
}
}
return element;
});
return schema;
}

19
ui-ngx/src/app/modules/home/components/widget/lib/maps/common-maps-utils.ts

@ -28,27 +28,8 @@ import {
import { Observable, Observer, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { FormattedData } from '@shared/models/widget.models';
import _ from 'lodash';
import { mapProviderSchema, providerSets } from '@home/components/widget/lib/maps/schemes';
import { addCondition, mergeSchemes } from '@core/schema-utils';
import L from 'leaflet';
export function getProviderSchema(mapProvider: MapProviders, ignoreImageMap = false) {
const providerSchema = _.cloneDeep(mapProviderSchema);
if (mapProvider) {
providerSchema.schema.properties.provider.default = mapProvider;
}
if (ignoreImageMap) {
providerSchema.form[0].items = providerSchema.form[0]?.items.filter(item => item.value !== 'image-map');
}
return mergeSchemes([providerSchema,
...Object.keys(providerSets)?.map(
(key: string) => {
const setting = providerSets[key];
return addCondition(setting?.schema, `model.provider === '${setting.name}'`);
})]);
}
export function getRatio(firsMoment: number, secondMoment: number, intermediateMoment: number): number {
return (intermediateMoment - firsMoment) / (secondMoment - firsMoment);
}

5
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget.interface.ts

@ -14,8 +14,6 @@
/// limitations under the License.
///
import { JsonSettingsSchema } from '@shared/models/widget.models';
import { MapProviders } from '@home/components/widget/lib/maps/map-models';
import LeafletMap from '@home/components/widget/lib/maps/leaflet-map';
export interface MapWidgetInterface {
@ -26,8 +24,5 @@ export interface MapWidgetInterface {
}
export interface MapWidgetStaticInterface {
settingsSchema(mapProvider?: MapProviders, drawRoutes?: boolean): JsonSettingsSchema;
getProvidersSchema(mapProvider?: MapProviders, ignoreImageMap?: boolean): JsonSettingsSchema;
dataKeySettingsSchema(): object;
actionSources(): object;
}

60
ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts

@ -14,26 +14,11 @@
/// limitations under the License.
///
import {
defaultMapSettings,
MapProviders,
UnitedMapSettings,
WidgetUnitedMapSettings
} from './map-models';
import { defaultMapSettings, MapProviders, UnitedMapSettings, WidgetUnitedMapSettings } from './map-models';
import LeafletMap from './leaflet-map';
import {
commonMapSettingsSchema,
editorSettingSchema,
mapCircleSchema,
mapPolygonSchema,
markerClusteringSettingsSchema,
markerClusteringSettingsSchemaLeaflet,
routeMapSettingsSchema
} from './schemes';
import { MapWidgetInterface, MapWidgetStaticInterface } from './map-widget.interface';
import { addCondition, addGroupInfo, addToSchema, initSchema, mergeSchemes } from '@core/schema-utils';
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import { getDefCenterPosition, getProviderSchema, parseWithTranslation } from './common-maps-utils';
import { getDefCenterPosition, parseWithTranslation } from './common-maps-utils';
import {
Datasource,
DatasourceData,
@ -45,7 +30,7 @@ import { TranslateService } from '@ngx-translate/core';
import { UtilsService } from '@core/services/utils.service';
import { EntityDataPageLink } from '@shared/models/query/query.models';
import { providerClass } from '@home/components/widget/lib/maps/providers';
import { isDefined, parseFunction } from '@core/utils';
import { isDefined, isDefinedAndNotNull, parseFunction } from '@core/utils';
import L from 'leaflet';
import { forkJoin, Observable, of } from 'rxjs';
import { AttributeService } from '@core/http/attribute.service';
@ -87,9 +72,13 @@ export class MapWidgetController implements MapWidgetInterface {
this.map.saveMarkerLocation = this.setMarkerLocation.bind(this);
this.map.savePolygonLocation = this.savePolygonLocation.bind(this);
this.map.saveLocation = this.saveLocation.bind(this);
let pageSize = this.settings.mapPageSize;
if (isDefinedAndNotNull(this.ctx.widgetConfig.pageSize)) {
pageSize = Math.max(pageSize, this.ctx.widgetConfig.pageSize);
}
this.pageLink = {
page: 0,
pageSize: this.settings.mapPageSize,
pageSize,
textSearch: null,
dynamic: true
};
@ -104,39 +93,6 @@ export class MapWidgetController implements MapWidgetInterface {
settings: WidgetUnitedMapSettings;
pageLink: EntityDataPageLink;
public static dataKeySettingsSchema(): object {
return {};
}
public static getProvidersSchema(mapProvider: MapProviders, ignoreImageMap = false) {
return getProviderSchema(mapProvider, ignoreImageMap);
}
public static settingsSchema(mapProvider: MapProviders, drawRoutes: boolean): JsonSettingsSchema {
const schema = initSchema();
addToSchema(schema, this.getProvidersSchema(mapProvider));
addGroupInfo(schema, 'Map Provider Settings');
addToSchema(schema, commonMapSettingsSchema);
addGroupInfo(schema, 'Common Map Settings');
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings');
addToSchema(schema, addCondition(mapCircleSchema, 'model.showCircle === true', ['showCircle']));
addGroupInfo(schema, 'Circle Settings');
if (drawRoutes) {
addToSchema(schema, routeMapSettingsSchema);
addGroupInfo(schema, 'Route Map Settings');
} else {
const clusteringSchema = mergeSchemes([markerClusteringSettingsSchema,
addCondition(markerClusteringSettingsSchemaLeaflet,
`model.useClusterMarkers === true && model.provider !== "image-map"`)]);
addToSchema(schema, clusteringSchema);
addGroupInfo(schema, 'Markers Clustering Settings');
addToSchema(schema, addCondition(editorSettingSchema, '(model.editablePolygon === true || model.draggableMarker === true)'));
addGroupInfo(schema, 'Editor settings');
}
return schema;
}
public static actionSources(): object {
return {
markerClick: {

1
ui-ngx/src/app/modules/home/components/widget/lib/maps/providers/image-map.ts

@ -88,6 +88,7 @@ export class ImageMap extends LeafletMap {
const imageUrlSubscriptionOptions: WidgetSubscriptionOptions = {
datasources,
hasDataPageLink: true,
singleEntity: true,
useDashboardTimewindow: false,
type: widgetType.latest,
callbacks: {

1603
ui-ngx/src/app/modules/home/components/widget/lib/maps/schemes.ts

File diff suppressed because it is too large

6
ui-ngx/src/app/modules/home/components/widget/lib/markdown-widget.component.ts

@ -26,7 +26,7 @@ import {
fillDataPattern,
flatFormattedData,
formattedDataFormDatasourceData,
hashCode,
hashCode, isDefinedAndNotNull,
isNotEmptyStr,
parseFunction, processDataPattern,
safeExecute
@ -83,9 +83,11 @@ export class MarkdownWidgetComponent extends PageComponent implements OnInit {
cssParser.cssPreviewNamespace = this.markdownClass;
cssParser.createStyleElement(this.markdownClass, cssString);
}
const pageSize = isDefinedAndNotNull(this.ctx.widgetConfig.pageSize) &&
this.ctx.widgetConfig.pageSize > 0 ? this.ctx.widgetConfig.pageSize : 16384;
const pageLink: EntityDataPageLink = {
page: 0,
pageSize: 16384,
pageSize,
textSearch: null,
dynamic: true
};

45
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts

@ -32,30 +32,22 @@ import {
MapProviders,
WidgetUnitedTripAnimationSettings
} from '@home/components/widget/lib/maps/map-models';
import { addCondition, addGroupInfo, addToSchema, initSchema } from '@app/core/schema-utils';
import {
mapCircleSchema,
mapPolygonSchema,
pathSchema,
pointSchema,
tripAnimationSchema
} from '@home/components/widget/lib/maps/schemes';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { WidgetContext } from '@app/modules/home/models/widget-component.models';
import {
findAngle,
getProviderSchema,
getRatio,
interpolateOnLineSegment,
parseWithTranslation
} from '@home/components/widget/lib/maps/common-maps-utils';
import { FormattedData, JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models';
import { FormattedData, WidgetConfig } from '@shared/models/widget.models';
import moment from 'moment';
import {
deepClone,
formattedDataArrayFromDatasourceData, formattedDataFormDatasourceData,
formattedDataArrayFromDatasourceData,
formattedDataFormDatasourceData,
isDefined,
isUndefined, mergeFormattedData,
isUndefined,
mergeFormattedData,
parseFunction,
safeExecute
} from '@core/utils';
@ -101,23 +93,6 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
useAnchors: boolean;
currentTime: number;
static getSettingsSchema(): JsonSettingsSchema {
const schema = initSchema();
addToSchema(schema, getProviderSchema(null, true));
addGroupInfo(schema, 'Map Provider Settings');
addToSchema(schema, tripAnimationSchema);
addGroupInfo(schema, 'Trip Animation Settings');
addToSchema(schema, pathSchema);
addGroupInfo(schema, 'Path Settings');
addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints']));
addGroupInfo(schema, 'Path Points Settings');
addToSchema(schema, addCondition(mapPolygonSchema, 'model.showPolygon === true', ['showPolygon']));
addGroupInfo(schema, 'Polygon Settings');
addToSchema(schema, addCondition(mapCircleSchema, 'model.showCircle === true', ['showCircle']));
addGroupInfo(schema, 'Circle Settings');
return schema;
}
ngOnInit(): void {
this.widgetConfig = this.ctx.widgetConfig;
this.settings = {
@ -286,10 +261,12 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
}
calcLabel(points: FormattedData[]) {
const data = points[this.activeTrip.dsIndex];
const labelText: string = this.settings.useLabelFunction ?
safeExecute(this.settings.parsedLabelFunction, [data, points, data.dsIndex]) : this.settings.label;
this.label = this.sanitizer.bypassSecurityTrustHtml(parseWithTranslation.parseTemplate(labelText, data, true));
if (this.activeTrip) {
const data = points[this.activeTrip.dsIndex];
const labelText: string = this.settings.useLabelFunction ?
safeExecute(this.settings.parsedLabelFunction, [data, points, data.dsIndex]) : this.settings.label;
this.label = this.sanitizer.bypassSecurityTrustHtml(parseWithTranslation.parseTemplate(labelText, data, true));
}
}
private interpolateArray(originData: FormattedData[]): {[time: number]: FormattedData} {

8
ui-ngx/src/app/modules/home/components/widget/widget-config.component.html

@ -317,6 +317,14 @@
<mat-panel-title translate>widget-config.data-settings</mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div fxLayout="row" *ngIf="widgetType !== widgetTypes.rpc &&
widgetType !== widgetTypes.alarm &&
modelValue?.isDataEnabled && !modelValue?.typeParameters?.singleEntity">
<mat-form-field fxFlex>
<mat-label translate>widget-config.data-page-size</mat-label>
<input matInput formControlName="pageSize" type="number" min="1" step="1">
</mat-form-field>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px">
<mat-form-field fxFlex>
<mat-label translate>widget-config.units</mat-label>

2
ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts

@ -213,6 +213,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
widgetStyle: [null, []],
widgetCss: [null, []],
titleStyle: [null, []],
pageSize: [1024, [Validators.min(1), Validators.pattern(/^\d*$/)]],
units: [null, []],
decimals: [null, [Validators.min(0), Validators.max(15), Validators.pattern(/^\d*$/)]],
noDataDisplayMessage: [null, []],
@ -420,6 +421,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, Cont
fontSize: '16px',
fontWeight: 400
},
pageSize: isDefined(config.pageSize) ? config.pageSize : 1024,
units: config.units,
decimals: config.decimals,
noDataDisplayMessage: isDefined(config.noDataDisplayMessage) ? config.noDataDisplayMessage : '',

3
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -977,7 +977,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
ignoreDataUpdateOnIntervalTick: this.typeParameters.ignoreDataUpdateOnIntervalTick,
comparisonEnabled: comparisonSettings.comparisonEnabled,
timeForComparison: comparisonSettings.timeForComparison,
comparisonCustomIntervalValue: comparisonSettings.comparisonCustomIntervalValue
comparisonCustomIntervalValue: comparisonSettings.comparisonCustomIntervalValue,
pageSize: this.widget.config.pageSize
};
if (this.widget.type === widgetType.alarm) {
options.alarmSource = deepClone(this.widget.config.alarmSource);

1
ui-ngx/src/app/shared/models/widget.models.ts

@ -554,6 +554,7 @@ export interface WidgetConfig {
units?: string;
decimals?: number;
noDataDisplayMessage?: string;
pageSize?: number;
actions?: {[actionSourceId: string]: Array<WidgetActionDescriptor>};
settings?: WidgetSettings;
alarmSource?: Datasource;

1
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -3305,6 +3305,7 @@
"advanced-settings": "Advanced settings",
"data-settings": "Data settings",
"no-data-display-message": "\"No data to display\" alternative message",
"data-page-size": "Maximum entities per datasource",
"settings-component-not-found": "Settings form component not found for selector '{{selector}}'"
},
"widget-type": {

Loading…
Cancel
Save