Browse Source

Widget -> Widget Bundle Many to Many support.

feature/widget-bundles
Igor Kulikov 3 years ago
parent
commit
1fb9ba622e
  1. 39
      application/src/main/data/upgrade/3.5.1/schema_update.sql
  2. 2
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  3. 187
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  4. 30
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  5. 4
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  6. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java
  8. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java
  9. 52
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java
  10. 36
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java
  11. 35
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java
  12. 5
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java
  13. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  14. 12
      application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java
  15. 8
      application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java
  16. 64
      application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java
  17. 22
      application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java
  18. 2
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  19. 2
      application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java
  20. 2
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java
  21. 27
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java
  22. 1
      application/src/main/resources/thingsboard.yml
  23. 5
      application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java
  24. 128
      application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java
  25. 1
      application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java
  26. 28
      common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java
  27. 9
      common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java
  28. 5
      common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java
  29. 14
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java
  30. 33
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java
  31. 14
      common/edge-api/src/main/proto/edge.proto
  32. 8
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  33. 6
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java
  34. 12
      dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java
  35. 37
      dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java
  36. 75
      dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java
  37. 13
      dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java
  38. 110
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java
  39. 66
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java
  40. 29
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java
  41. 5
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  42. 44
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java
  43. 155
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java
  44. 5
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java
  45. 15
      dao/src/main/resources/sql/schema-entities.sql
  46. 152
      dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java
  47. 29
      dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java
  48. 12
      dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java
  49. 1
      dao/src/test/resources/sql/psql/drop-all-tables.sql

39
application/src/main/data/upgrade/3.5.1/schema_update.sql

@ -141,3 +141,42 @@ $$
END IF;
END;
$$;
ALTER TABLE widget_type
ADD COLUMN IF NOT EXISTS external_id UUID;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widget_type_external_id_unq_key') THEN
ALTER TABLE widget_type ADD CONSTRAINT widget_type_external_id_unq_key UNIQUE (tenant_id, external_id);
END IF;
END;
$$;
DO
$$
BEGIN
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_widgets_bundle_alias') THEN
ALTER TABLE widgets_bundle ADD CONSTRAINT uq_widgets_bundle_alias UNIQUE (tenant_id, alias);
END IF;
END;
$$;
CREATE TABLE IF NOT EXISTS widgets_bundle_widget (
widgets_bundle_id uuid NOT NULL,
widget_type_id uuid NOT NULL,
widget_type_order int NOT NULL DEFAULT 0,
CONSTRAINT widgets_bundle_widget_pkey PRIMARY KEY (widgets_bundle_id, widget_type_id),
CONSTRAINT fk_widgets_bundle FOREIGN KEY (widgets_bundle_id) REFERENCES widgets_bundle(id) ON DELETE CASCADE,
CONSTRAINT fk_widget_type FOREIGN KEY (widget_type_id) REFERENCES widget_type(id) ON DELETE CASCADE
);
DO
$$
BEGIN
IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'widget_type' and column_name='bundle_alias') THEN
INSERT INTO widgets_bundle_widget SELECT wb.id as widgets_bundle_id, wt.id as widget_type_id from widget_type wt left join widgets_bundle wb ON wt.bundle_alias = wb.alias ON CONFLICT (widgets_bundle_id, widget_type_id) DO NOTHING;
ALTER TABLE widget_type DROP COLUMN IF EXISTS bundle_alias;
END IF;
END;
$$;

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

@ -76,6 +76,7 @@ public class ControllerConstants {
protected static final String ASSET_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the asset name.";
protected static final String DASHBOARD_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the dashboard title.";
protected static final String WIDGET_BUNDLE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the widget bundle title.";
protected static final String WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the widget type name.";
protected static final String RPC_TEXT_SEARCH_DESCRIPTION = "Not implemented. Leave empty.";
protected static final String DEVICE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the device name.";
protected static final String ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the entity view name.";
@ -112,6 +113,7 @@ public class ControllerConstants {
protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root";
protected static final String WIDGET_BUNDLE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, tenantId";
protected static final String WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, deprecated, tenantId";
protected static final String AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, entityType, entityName, userName, actionType, actionStatus";
protected static final String SORT_ORDER_DESCRIPTION = "Sort order. ASC (ASCENDING) or DESC (DESCENDING)";
protected static final String SORT_ORDER_ALLOWABLE_VALUES = "ASC, DESC";

187
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -17,7 +17,7 @@ package org.thingsboard.server.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -32,6 +32,9 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
@ -39,27 +42,38 @@ import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.widgets.type.TbWidgetTypeService;
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.AVAILABLE_FOR_ANY_AUTHORIZED_USER;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
import static org.thingsboard.server.controller.ControllerConstants.WIDGET_TYPE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION;
@Slf4j
@RestController
@TbCoreComponent
@RequestMapping("/api")
@RequiredArgsConstructor
public class WidgetTypeController extends AutoCommitController {
private final TbWidgetTypeService tbWidgetTypeService;
private static final String WIDGET_TYPE_DESCRIPTION = "Widget Type represents the template for widget creation. Widget Type and Widget are similar to class and object in OOP theory.";
private static final String WIDGET_TYPE_DETAILS_DESCRIPTION = "Widget Type Details extend Widget Type and add image and description properties. " +
"Those properties are useful to edit the Widget Type but they are not required for Dashboard rendering. ";
private static final String WIDGET_TYPE_INFO_DESCRIPTION = "Widget Type Info is a lightweight object that represents Widget Type but does not contain the heavyweight widget descriptor JSON";
private static final String TENANT_ONLY_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether only tenant widget types should be returned";
@ApiOperation(value = "Get Widget Type Details (getWidgetTypeById)",
notes = "Get the Widget Type Details based on the provided Widget Type Id. " + WIDGET_TYPE_DETAILS_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@ -98,16 +112,7 @@ public class WidgetTypeController extends AutoCommitController {
}
checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE);
WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails);
if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) {
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId(), widgetTypeDetails.getBundleAlias());
if (widgetsBundle != null) {
autoCommit(currentUser, widgetsBundle.getId());
}
}
return checkNotNull(savedWidgetTypeDetails);
return tbWidgetTypeService.save(widgetTypeDetails, currentUser);
}
@ApiOperation(value = "Delete widget type (deleteWidgetType)",
@ -119,25 +124,48 @@ public class WidgetTypeController extends AutoCommitController {
@ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("widgetTypeId") String strWidgetTypeId) throws Exception {
checkParameter("widgetTypeId", strWidgetTypeId);
var currentUser = getCurrentUser();
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.DELETE);
widgetTypeService.deleteWidgetType(currentUser.getTenantId(), widgetTypeId);
tbWidgetTypeService.delete(wtd, getCurrentUser());
}
if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) {
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias());
if (widgetsBundle != null) {
autoCommit(currentUser, widgetsBundle.getId());
@ApiOperation(value = "Get Widget Types (getWidgetTypes)",
notes = "Returns a page of Widget Type objects available for current user. " + WIDGET_TYPE_DESCRIPTION + " " +
PAGE_DATA_PARAMETERS + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetTypes", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<WidgetTypeInfo> getWidgetTypes(
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortProperty,
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortOrder,
@ApiParam(value = TENANT_ONLY_PARAM_DESCRIPTION)
@RequestParam(required = false) Boolean tenantOnly) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
return checkNotNull(widgetTypeService.findSystemWidgetTypesByPageLink(getTenantId(), pageLink));
} else {
if (tenantOnly != null && tenantOnly) {
return checkNotNull(widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(getTenantId(), pageLink));
} else {
return checkNotNull(widgetTypeService.findAllTenantWidgetTypesByTenantIdAndPageLink(getTenantId(), pageLink));
}
}
}
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)",
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypesByBundleAlias) (Deprecated)",
notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetType> getBundleWidgetTypes(
public List<WidgetType> getBundleWidgetTypesByBundleAlias(
@ApiParam(value = "System or Tenant", required = true)
@RequestParam boolean isSystem,
@ApiParam(value = "Widget Bundle alias", required = true)
@ -148,15 +176,28 @@ public class WidgetTypeController extends AutoCommitController {
} else {
tenantId = getCurrentUser().getTenantId();
}
return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias));
WidgetsBundle widgetsBundle = checkNotNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(tenantId, bundleAlias));
return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundle.getId()));
}
@ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)",
@ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)",
notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetTypes", params = {"widgetsBundleId"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetType> getBundleWidgetTypes(
@ApiParam(value = "Widget Bundle Id", required = true)
@RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundleId));
}
@ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypesDetailsByBundleAlias) (Deprecated)",
notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetTypeDetails> getBundleWidgetTypesDetails(
public List<WidgetTypeDetails> getBundleWidgetTypesDetailsByBundleAlias(
@ApiParam(value = "System or Tenant", required = true)
@RequestParam boolean isSystem,
@ApiParam(value = "Widget Bundle alias", required = true)
@ -167,15 +208,28 @@ public class WidgetTypeController extends AutoCommitController {
} else {
tenantId = getCurrentUser().getTenantId();
}
return checkNotNull(widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId, bundleAlias));
WidgetsBundle widgetsBundle = checkNotNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(tenantId, bundleAlias));
return checkNotNull(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(getTenantId(), widgetsBundle.getId()));
}
@ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfos)",
notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)",
notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetTypeDetails> getBundleWidgetTypesDetails(
@ApiParam(value = "Widget Bundle Id", required = true)
@RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
return checkNotNull(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(getTenantId(), widgetsBundleId));
}
@ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfosByBundleAlias) (Deprecated)",
notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetTypesInfos", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetTypeInfo> getBundleWidgetTypesInfos(
public List<WidgetTypeInfo> getBundleWidgetTypesInfosByBundleAlias(
@ApiParam(value = "System or Tenant", required = true)
@RequestParam boolean isSystem,
@ApiParam(value = "Widget Bundle alias", required = true)
@ -186,15 +240,28 @@ public class WidgetTypeController extends AutoCommitController {
} else {
tenantId = getCurrentUser().getTenantId();
}
return checkNotNull(widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias));
WidgetsBundle widgetsBundle = checkNotNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(tenantId, bundleAlias));
return checkNotNull(widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(getTenantId(), widgetsBundle.getId()));
}
@ApiOperation(value = "Get Widget Type (getWidgetType)",
@ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfos)",
notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetTypesInfos", params = {"widgetsBundleId"}, method = RequestMethod.GET)
@ResponseBody
public List<WidgetTypeInfo> getBundleWidgetTypesInfos(
@ApiParam(value = "Widget Bundle Id", required = true)
@RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
return checkNotNull(widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(getTenantId(), widgetsBundleId));
}
@ApiOperation(value = "Get Widget Type (getWidgetTypeByBundleAliasAndTypeAlias) (Deprecated)",
notes = "Get the Widget Type based on the provided parameters. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}, method = RequestMethod.GET)
@ResponseBody
public WidgetType getWidgetType(
public WidgetType getWidgetTypeByBundleAliasAndTypeAlias(
@ApiParam(value = "System or Tenant", required = true)
@RequestParam boolean isSystem,
@ApiParam(value = "Widget Bundle alias", required = true)
@ -213,12 +280,12 @@ public class WidgetTypeController extends AutoCommitController {
return widgetType;
}
@ApiOperation(value = "Get Widget Type by fqn (getWidgetTypeByFqn)",
@ApiOperation(value = "Get Widget Type (getWidgetType)",
notes = "Get the Widget Type by FQN. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetType", params = {"fqn"}, method = RequestMethod.GET)
@ResponseBody
public WidgetType getWidgetTypeByFqn(
public WidgetType getWidgetType(
@ApiParam(value = "Widget Type fqn", required = true)
@RequestParam String fqn) throws ThingsboardException {
String[] parts = fqn.split("\\.");
@ -239,58 +306,4 @@ public class WidgetTypeController extends AutoCommitController {
return widgetType;
}
@ApiOperation(value = "Set widget type deprecated (setWidgetTypeDeprecated)",
notes = "Set Widget Type deprecated flag. Referencing non-existing Widget Type Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType/{widgetTypeId}/deprecate/{deprecated}", method = RequestMethod.POST)
@ResponseBody
public WidgetTypeDetails setWidgetTypeDeprecated(
@ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("widgetTypeId") String strWidgetTypeId,
@PathVariable("deprecated") boolean deprecated) throws Exception {
checkParameter("widgetTypeId", strWidgetTypeId);
var currentUser = getCurrentUser();
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.WRITE);
WidgetTypeDetails updated = widgetTypeService.setWidgetTypeDeprecated(currentUser.getTenantId(), widgetTypeId, deprecated);
if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) {
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias());
if (widgetsBundle != null) {
autoCommit(currentUser, widgetsBundle.getId());
}
}
return updated;
}
@ApiOperation(value = "Move widget type to target widgets bundle (moveWidgetType)",
notes = "Move Widget Type to target Widgets Bundle. Referencing non-existing Widget Type Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetType/{widgetTypeId}/move", params = {"targetBundleAlias"}, method = RequestMethod.POST)
@ResponseBody
public WidgetTypeDetails moveWidgetType(
@ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("widgetTypeId") String strWidgetTypeId,
@ApiParam(value = "Target Widget Bundle alias", required = true)
@RequestParam String targetBundleAlias) throws Exception {
checkParameter("widgetTypeId", strWidgetTypeId);
checkParameter("targetBundleAlias", targetBundleAlias);
var currentUser = getCurrentUser();
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.WRITE);
if (!wtd.getBundleAlias().equals(targetBundleAlias)) {
wtd = widgetTypeService.moveWidgetType(currentUser.getTenantId(), widgetTypeId, targetBundleAlias);
if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) {
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias());
if (widgetsBundle != null) {
autoCommit(currentUser, widgetsBundle.getId());
}
WidgetsBundle targetWidgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(currentUser.getTenantId(), targetBundleAlias);
if (targetWidgetsBundle != null) {
autoCommit(currentUser, targetWidgetsBundle.getId());
}
}
}
return wtd;
}
}

30
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -30,6 +30,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -40,7 +41,10 @@ import org.thingsboard.server.service.entitiy.widgets.bundle.TbWidgetsBundleServ
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
@ -106,6 +110,32 @@ public class WidgetsBundleController extends BaseController {
return tbWidgetsBundleService.save(widgetsBundle, currentUser);
}
@ApiOperation(value = "Update widgets bundle widgets types list (updateWidgetsBundleWidgetTypes)",
notes = "Updates widgets bundle widgets list." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/widgetsBundle/{widgetsBundleId}/widgetTypes", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void updateWidgetsBundleWidgetTypes(
@ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("widgetsBundleId") String strWidgetsBundleId,
@ApiParam(value = "Ordered list of widget type Ids to be included by widgets bundle")
@RequestBody List<String> strWidgetTypeIds) throws Exception {
checkParameter("widgetsBundleId", strWidgetsBundleId);
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
checkNotNull(strWidgetTypeIds);
Set<WidgetTypeId> widgetTypeIds = new LinkedHashSet<>();
var currentUser = getCurrentUser();
TenantId tenantId = currentUser.getTenantId();
for (String strWidgetTypeId : strWidgetTypeIds) {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
if (!widgetTypeIds.contains(widgetTypeId) &&
widgetTypeService.widgetTypeExistsByTenantIdAndWidgetTypeId(tenantId, widgetTypeId)) {
widgetTypeIds.add(widgetTypeId);
}
}
tbWidgetsBundleService.updateWidgetsBundleWidgetTypes(widgetsBundleId, new ArrayList<>(widgetTypeIds), currentUser);
}
@ApiOperation(value = "Delete widgets bundle (deleteWidgetsBundle)",
notes = "Deletes the widget bundle. Referencing non-existing Widget Bundle Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")

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

@ -38,6 +38,7 @@ import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
@ -117,6 +118,9 @@ public class EdgeContextComponent {
@Autowired
private CustomerService customerService;
@Autowired
private WidgetTypeService widgetTypeService;
@Autowired
private WidgetsBundleService widgetsBundleService;

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java

@ -32,9 +32,11 @@ import org.thingsboard.server.service.edge.rpc.fetch.EntityViewsEdgeEventFetcher
import org.thingsboard.server.service.edge.rpc.fetch.OtaPackagesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.QueuesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetTypesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetTypesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher;
import java.util.LinkedList;
@ -68,6 +70,8 @@ public class EdgeSyncCursor {
fetchers.add(new EntityViewsEdgeEventFetcher(ctx.getEntityViewService()));
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
if (fullSync) {
fetchers.add(new SystemWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService()));
fetchers.add(new TenantWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService()));
fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService()));

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java

@ -33,9 +33,6 @@ public class WidgetTypeMsgConstructor {
.setMsgType(msgType)
.setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits())
.setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits());
if (widgetTypeDetails.getBundleAlias() != null) {
builder.setBundleAlias(widgetTypeDetails.getBundleAlias());
}
if (widgetTypeDetails.getFqn() != null) {
builder.setFqn(widgetTypeDetails.getFqn());
}

5
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.constructor;
import com.google.protobuf.ByteString;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
@ -25,12 +26,13 @@ import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Component
@TbCoreComponent
public class WidgetsBundleMsgConstructor {
public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle) {
public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List<String> widgets) {
WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits())
@ -46,6 +48,7 @@ public class WidgetsBundleMsgConstructor {
if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true);
}
builder.setWidgets(JacksonUtil.toString(widgets));
return builder.build();
}

52
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java

@ -0,0 +1,52 @@
/**
* Copyright © 2016-2023 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.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
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.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
@Slf4j
@AllArgsConstructor
public abstract class BaseWidgetTypesEdgeEventFetcher extends BasePageableEdgeEventFetcher<WidgetTypeInfo> {
protected final WidgetTypeService widgetTypeService;
@Override
PageData<WidgetTypeInfo> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
return findWidgetTypes(tenantId, pageLink);
}
@Override
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, WidgetTypeInfo widgetTypeInfo) {
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE,
EdgeEventActionType.ADDED, widgetTypeInfo.getId(), null);
}
protected abstract PageData<WidgetTypeInfo> findWidgetTypes(TenantId tenantId, PageLink pageLink);
}

36
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2023 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.edge.rpc.fetch;
import lombok.extern.slf4j.Slf4j;
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.widget.WidgetTypeInfo;
import org.thingsboard.server.dao.widget.WidgetTypeService;
@Slf4j
public class SystemWidgetTypesEdgeEventFetcher extends BaseWidgetTypesEdgeEventFetcher {
public SystemWidgetTypesEdgeEventFetcher(WidgetTypeService widgetTypeService) {
super(widgetTypeService);
}
@Override
protected PageData<WidgetTypeInfo> findWidgetTypes(TenantId tenantId, PageLink pageLink) {
return widgetTypeService.findSystemWidgetTypesByPageLink(tenantId, pageLink);
}
}

35
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2023 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.edge.rpc.fetch;
import lombok.extern.slf4j.Slf4j;
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.widget.WidgetTypeInfo;
import org.thingsboard.server.dao.widget.WidgetTypeService;
@Slf4j
public class TenantWidgetTypesEdgeEventFetcher extends BaseWidgetTypesEdgeEventFetcher {
public TenantWidgetTypesEdgeEventFetcher(WidgetTypeService widgetTypeService) {
super(widgetTypeService);
}
@Override
protected PageData<WidgetTypeInfo> findWidgetTypes(TenantId tenantId, PageLink pageLink) {
return widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(tenantId, pageLink);
}
}

5
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java

@ -27,6 +27,8 @@ import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.List;
@Component
@Slf4j
@TbCoreComponent
@ -40,9 +42,10 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor {
case UPDATED:
WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(edgeEvent.getTenantId(), widgetsBundleId);
if (widgetsBundle != null) {
List<String> widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(edgeEvent.getTenantId(), widgetsBundleId);
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle);
widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg)

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -330,7 +330,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
WidgetsBundle widgetsBundleById = widgetsBundleService.findWidgetsBundleById(tenantId, widgetsBundleId);
if (widgetsBundleById != null) {
List<WidgetType> widgetTypesToPush =
widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundleById.getTenantId(), widgetsBundleById.getAlias());
widgetTypeService.findWidgetTypesByWidgetsBundleId(widgetsBundleById.getTenantId(), widgetsBundleId);
for (WidgetType widgetType : widgetTypesToPush) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null));
}

12
application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java

@ -21,17 +21,23 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.List;
@Service
@TbCoreComponent
@AllArgsConstructor
public class DefaultWidgetsBundleService extends AbstractTbEntityService implements TbWidgetsBundleService {
private final WidgetsBundleService widgetsBundleService;
private final WidgetTypeService widgetTypeService;
@Override
public WidgetsBundle save(WidgetsBundle widgetsBundle, User user) throws Exception {
@ -61,4 +67,10 @@ public class DefaultWidgetsBundleService extends AbstractTbEntityService impleme
throw e;
}
}
@Override
public void updateWidgetsBundleWidgetTypes(WidgetsBundleId widgetsBundleId, List<WidgetTypeId> widgetTypeIds, User user) throws Exception {
widgetTypeService.updateWidgetsBundleWidgetTypes(user.getTenantId(), widgetsBundleId, widgetTypeIds);
autoCommit(user, widgetsBundleId);
}
}

8
application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java

@ -15,8 +15,16 @@
*/
package org.thingsboard.server.service.entitiy.widgets.bundle;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
import java.util.List;
public interface TbWidgetsBundleService extends SimpleTbEntityService<WidgetsBundle> {
void updateWidgetsBundleWidgetTypes(WidgetsBundleId widgetsBundleId, List<WidgetTypeId> widgetTypeIds, User user) throws Exception;
}

64
application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java

@ -0,0 +1,64 @@
/**
* Copyright © 2016-2023 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.widgets.type;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
@Service
@TbCoreComponent
@AllArgsConstructor
public class DefaultWidgetTypeService extends AbstractTbEntityService implements TbWidgetTypeService {
private final WidgetTypeService widgetTypeService;
@Override
public WidgetTypeDetails save(WidgetTypeDetails widgetTypeDetails, User user) throws Exception {
ActionType actionType = widgetTypeDetails.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = widgetTypeDetails.getTenantId();
try {
WidgetTypeDetails savedWidgetTypeDetails = checkNotNull(widgetTypeService.saveWidgetType(widgetTypeDetails));
autoCommit(user, savedWidgetTypeDetails.getId());
notificationEntityService.logEntityAction(tenantId, savedWidgetTypeDetails.getId(), savedWidgetTypeDetails,
null, actionType, user);
return savedWidgetTypeDetails;
} catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGET_TYPE), widgetTypeDetails, actionType, user, e);
throw e;
}
}
@Override
public void delete(WidgetTypeDetails widgetTypeDetails, User user) {
ActionType actionType = ActionType.DELETED;
TenantId tenantId = widgetTypeDetails.getTenantId();
try {
widgetTypeService.deleteWidgetType(widgetTypeDetails.getTenantId(), widgetTypeDetails.getId());
notificationEntityService.logEntityAction(tenantId, widgetTypeDetails.getId(), widgetTypeDetails, null, actionType, user);
} catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGET_TYPE), actionType, user, e, widgetTypeDetails.getId());
throw e;
}
}
}

22
application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java

@ -0,0 +1,22 @@
/**
* Copyright © 2016-2023 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.widgets.type;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.service.entitiy.SimpleTbEntityService;
public interface TbWidgetTypeService extends SimpleTbEntityService<WidgetTypeDetails> {
}

2
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -194,7 +194,7 @@ public class InstallScripts {
widgetTypeJson -> {
try {
WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class);
widgetTypeDetails.setBundleAlias(savedWidgetsBundle.getAlias());
// widgetTypeDetails.setBundleAlias(savedWidgetsBundle.getAlias()); // TODO:
widgetTypeService.saveWidgetType(widgetTypeDetails);
} catch (Exception e) {
log.error("Unable to load widget type from json: [{}]", path.toString());

2
application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java

@ -68,7 +68,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
EntityType.CUSTOMER, EntityType.RULE_CHAIN, EntityType.DASHBOARD,
EntityType.ASSET_PROFILE, EntityType.ASSET,
EntityType.DEVICE_PROFILE, EntityType.DEVICE,
EntityType.ENTITY_VIEW, EntityType.WIDGETS_BUNDLE,
EntityType.ENTITY_VIEW, EntityType.WIDGET_TYPE, EntityType.WIDGETS_BUNDLE,
EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE
);

2
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java

@ -42,7 +42,7 @@ public class WidgetsBundleExportService extends BaseEntityExportService<WidgetsB
throw new IllegalArgumentException("Export of system Widget Bundles is not allowed");
}
List<WidgetTypeDetails> widgets = widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(ctx.getTenantId(), widgetsBundle.getAlias());
List<String> widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId());
exportData.setWidgets(widgets);
}

27
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java

@ -54,32 +54,7 @@ public class WidgetsBundleImportService extends BaseEntityImportService<WidgetsB
@Override
protected WidgetsBundle saveOrUpdate(EntitiesImportCtx ctx, WidgetsBundle widgetsBundle, WidgetsBundleExportData exportData, IdProvider idProvider) {
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
if (widgetsBundle.getId() == null) {
for (WidgetTypeDetails widget : exportData.getWidgets()) {
widget.setId(null);
widget.setTenantId(ctx.getTenantId());
widget.setBundleAlias(savedWidgetsBundle.getAlias());
widgetTypeService.saveWidgetType(widget);
}
} else {
Map<String, WidgetTypeInfo> existingWidgets = widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(ctx.getTenantId(), savedWidgetsBundle.getAlias()).stream()
.collect(Collectors.toMap(BaseWidgetType::getFqn, w -> w));
for (WidgetTypeDetails widget : exportData.getWidgets()) {
WidgetTypeInfo existingWidget;
if ((existingWidget = existingWidgets.remove(widget.getFqn())) != null) {
widget.setId(existingWidget.getId());
widget.setCreatedTime(existingWidget.getCreatedTime());
} else {
widget.setId(null);
}
widget.setTenantId(ctx.getTenantId());
widget.setBundleAlias(savedWidgetsBundle.getAlias());
widgetTypeService.saveWidgetType(widget);
}
existingWidgets.values().stream()
.map(BaseWidgetType::getId)
.forEach(widgetTypeId -> widgetTypeService.deleteWidgetType(ctx.getTenantId(), widgetTypeId));
}
widgetTypeService.updateWidgetsBundleWidgetFqns(ctx.getTenantId(), savedWidgetsBundle.getId(), exportData.getWidgets());
return savedWidgetsBundle;
}

1
application/src/main/resources/thingsboard.yml

@ -650,6 +650,7 @@ audit-log:
"device": "${AUDIT_LOG_MASK_DEVICE:W}"
"asset": "${AUDIT_LOG_MASK_ASSET:W}"
"dashboard": "${AUDIT_LOG_MASK_DASHBOARD:W}"
"widget_type": "${AUDIT_LOG_MASK_WIDGET_TYPE:W}"
"widgets_bundle": "${AUDIT_LOG_MASK_WIDGETS_BUNDLE:W}"
"customer": "${AUDIT_LOG_MASK_CUSTOMER:W}"
"user": "${AUDIT_LOG_MASK_USER:W}"

5
application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java

@ -232,12 +232,7 @@ public class TbResourceControllerTest extends AbstractControllerTest {
String resourceIdStr = savedResource.getId().getId().toString();
//create widget type
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle");
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString(String.format("{ \"resources\": [{\"url\":{\"entityType\":\"TB_RESOURCE\",\"id\":\"%s\"},\"isModule\":true}]}", savedResource.getId()), JsonNode.class));
doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);

128
application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java

@ -24,6 +24,7 @@ import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
@ -33,6 +34,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -43,7 +45,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
private IdComparator<WidgetType> idComparator = new IdComparator<>();
private Tenant savedTenant;
private WidgetsBundle savedWidgetsBundle;
private User tenantAdmin;
@Before
@ -63,10 +64,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle");
savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
}
@After
@ -80,7 +77,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
@Test
public void testSaveWidgetType() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
@ -92,7 +88,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
Assert.assertEquals(savedTenant.getId(), savedWidgetType.getTenantId());
Assert.assertEquals(widgetType.getName(), savedWidgetType.getName());
Assert.assertEquals(widgetType.getDescriptor(), savedWidgetType.getDescriptor());
Assert.assertEquals(savedWidgetsBundle.getAlias(), savedWidgetType.getBundleAlias());
savedWidgetType.setName("New Widget Type");
@ -105,7 +100,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
@Test
public void testUpdateWidgetTypeFromDifferentTenant() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
@ -118,7 +112,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
@Test
public void testFindWidgetTypeById() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
@ -130,7 +123,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
@Test
public void testDeleteWidgetType() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
@ -145,27 +137,15 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
@Test
public void testSaveWidgetTypeWithEmptyName() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
doPost("/api/widgetType", widgetType)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widgets type name should be specified")));
}
@Test
public void testSaveWidgetTypeWithEmptyBundleAlias() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
doPost("/api/widgetType", widgetType)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widgets type bundle alias should be specified")));
}
@Test
public void testSaveWidgetTypeWithEmptyDescriptor() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{}", JsonNode.class));
doPost("/api/widgetType", widgetType)
@ -173,55 +153,9 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
.andExpect(statusReason(containsString("Widgets type descriptor can't be empty")));
}
@Test
public void testSaveWidgetTypeWithInvalidBundleAlias() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias("some_alias");
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
doPost("/api/widgetType", widgetType)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widget type is referencing to non-existent widgets bundle")));
}
@Test
public void testUpdateWidgetTypeBundleAlias() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
WidgetsBundle widgetsBundle2 = new WidgetsBundle();
widgetsBundle2.setTitle("My widgets bundle 2");
WidgetsBundle savedWidgetsBundle2 = doPost("/api/widgetsBundle", widgetsBundle2, WidgetsBundle.class);
savedWidgetType.setBundleAlias(savedWidgetsBundle2.getAlias());
doPost("/api/widgetType", savedWidgetType);
WidgetTypeDetails foundWidgetType = doGet("/api/widgetType/" + savedWidgetType.getId().getId().toString(), WidgetTypeDetails.class);
Assert.assertEquals(savedWidgetsBundle2.getAlias(), foundWidgetType.getBundleAlias());
}
@Test
public void testUpdateWidgetTypeBundleAliasToNonExistent() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
savedWidgetType.setBundleAlias("some_alias");
doPost("/api/widgetType", savedWidgetType)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widget type is referencing to non-existent widgets bundle")));
}
@Test
public void testUpdateWidgetTypeFqn() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
@ -232,64 +166,25 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
}
@Test
public void testDeprecateWidgetType() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
doPost("/api/widgetType/"+savedWidgetType.getId().getId().toString() + "/deprecate/true")
.andExpect(status().isOk());
WidgetTypeDetails foundWidgetType = doGet("/api/widgetType/" + savedWidgetType.getId().getId().toString(), WidgetTypeDetails.class);
Assert.assertTrue(foundWidgetType.isDeprecated());
doPost("/api/widgetType/"+savedWidgetType.getId().getId().toString() + "/deprecate/false")
.andExpect(status().isOk());
foundWidgetType = doGet("/api/widgetType/" + savedWidgetType.getId().getId().toString(), WidgetTypeDetails.class);
Assert.assertFalse(foundWidgetType.isDeprecated());
}
@Test
public void testMoveWidgetType() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
WidgetsBundle widgetsBundle2 = new WidgetsBundle();
widgetsBundle2.setTitle("My widgets bundle 2");
WidgetsBundle savedWidgetsBundle2 = doPost("/api/widgetsBundle", widgetsBundle2, WidgetsBundle.class);
doPost("/api/widgetType/"+savedWidgetType.getId().getId().toString() + "/move?targetBundleAlias=" + savedWidgetsBundle2.getAlias())
.andExpect(status().isOk());
WidgetTypeDetails foundWidgetType = doGet("/api/widgetType/" + savedWidgetType.getId().getId().toString(), WidgetTypeDetails.class);
Assert.assertEquals(savedWidgetsBundle2.getAlias(), foundWidgetType.getBundleAlias());
}
@Test
public void testMoveWidgetTypeToNonExistentBundle() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);
doPost("/api/widgetType/"+savedWidgetType.getId().getId().toString() + "/move?targetBundleAlias=some_alias")
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Widget type is referencing to non-existent widgets bundle")));
}
@Test
public void testGetBundleWidgetTypes() throws Exception {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTitle("My widgets bundle");
widgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
List<WidgetType> widgetTypes = new ArrayList<>();
for (int i=0;i<89;i++) {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type " + i);
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
widgetTypes.add(new WidgetType(doPost("/api/widgetType", widgetType, WidgetTypeDetails.class)));
}
List<WidgetType> loadedWidgetTypes = doGetTyped("/api/widgetTypes?isSystem={isSystem}&bundleAlias={bundleAlias}",
new TypeReference<>(){}, false, savedWidgetsBundle.getAlias());
List<String> widgetTypeIds = widgetTypes.stream().map(type -> type.getId().getId().toString()).collect(Collectors.toList());
doPost("/api/widgetsBundle/" + widgetsBundle.getId().getId().toString() + "/widgetTypes", widgetTypeIds);
List<WidgetType> loadedWidgetTypes = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}",
new TypeReference<>(){}, widgetsBundle.getId().getId().toString());
Collections.sort(widgetTypes, idComparator);
Collections.sort(loadedWidgetTypes, idComparator);
@ -300,7 +195,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest {
@Test
public void testGetWidgetType() throws Exception {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class);

1
application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java

@ -54,7 +54,6 @@ public class WidgetEdgeTest extends AbstractEdgeTest {
edgeImitator.expectMessageAmount(1);
WidgetType widgetType = new WidgetType();
widgetType.setName("Test Widget Type");
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
ObjectNode descriptor = JacksonUtil.newObjectNode();
descriptor.put("key", "value");
widgetType.setDescriptor(descriptor);

28
common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java

@ -15,9 +15,11 @@
*/
package org.thingsboard.server.dao.widget;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
@ -31,24 +33,32 @@ public interface WidgetTypeService extends EntityDaoService {
WidgetTypeDetails findWidgetTypeDetailsById(TenantId tenantId, WidgetTypeId widgetTypeId);
WidgetTypeDetails saveWidgetType(WidgetTypeDetails widgetType);
boolean widgetTypeExistsByTenantIdAndWidgetTypeId(TenantId tenantId, WidgetTypeId widgetTypeId);
WidgetTypeDetails moveWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId, String targetBundleAlias);
WidgetTypeDetails saveWidgetType(WidgetTypeDetails widgetType);
void deleteWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId);
WidgetTypeDetails setWidgetTypeDeprecated(TenantId tenantId, WidgetTypeId widgetTypeId, boolean deprecated);
PageData<WidgetTypeInfo> findSystemWidgetTypesByPageLink(TenantId tenantId, PageLink pageLink);
PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink);
List<WidgetType> findWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias);
PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink);
List<WidgetTypeDetails> findWidgetTypesDetailsByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias);
List<WidgetType> findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId);
List<WidgetTypeInfo> findWidgetTypesInfosByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias);
List<WidgetTypeDetails> findWidgetTypesDetailsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId);
List<WidgetTypeDetails> findWidgetTypesInfosByTenantIdAndResourceId(TenantId tenantId, TbResourceId tbResourceId);
List<WidgetTypeInfo> findWidgetTypesInfosByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId);
List<String> findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId);
WidgetType findWidgetTypeByTenantIdAndFqn(TenantId tenantId, String fqn);
void deleteWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias);
void updateWidgetsBundleWidgetTypes(TenantId tenantId, WidgetsBundleId widgetsBundleId, List<WidgetTypeId> widgetTypeIds);
void updateWidgetsBundleWidgetFqns(TenantId tenantId, WidgetsBundleId widgetsBundleId, List<String> widgetFqns);
void deleteWidgetTypesByTenantId(TenantId tenantId);
}

9
common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java

@ -30,13 +30,6 @@ import java.util.List;
public class WidgetsBundleExportData extends EntityExportData<WidgetsBundle> {
@JsonProperty(index = 3)
private List<WidgetTypeDetails> widgets;
@Override
public EntityExportData<WidgetsBundle> sort() {
super.sort();
widgets.sort(Comparator.comparing(BaseWidgetType::getFqn));
return this;
}
private List<String> widgets;
}

5
common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java

@ -33,10 +33,6 @@ public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, H
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private TenantId tenantId;
@NoXss
@Length(fieldName = "bundleAlias")
@ApiModelProperty(position = 4, value = "Reference to widget bundle", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String bundleAlias;
@NoXss
@Length(fieldName = "fqn")
@ApiModelProperty(position = 5, value = "Unique FQN that is used in dashboards as a reference widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String fqn;
@ -59,7 +55,6 @@ public class BaseWidgetType extends BaseData<WidgetTypeId> implements HasName, H
public BaseWidgetType(BaseWidgetType widgetType) {
super(widgetType);
this.tenantId = widgetType.getTenantId();
this.bundleAlias = widgetType.getBundleAlias();
this.fqn = widgetType.getFqn();
this.name = widgetType.getName();
this.deprecated = widgetType.isDeprecated();

14
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java

@ -18,13 +18,18 @@ package org.thingsboard.server.common.data.widget;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@Data
@JsonPropertyOrder({ "alias", "name", "image", "description", "descriptor" })
public class WidgetTypeDetails extends WidgetType {
@JsonPropertyOrder({ "fqn", "name", "deprecated", "image", "description", "descriptor", "externalId" })
public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantId, ExportableEntity<WidgetTypeId> {
@Length(fieldName = "image", max = 1000000)
@ApiModelProperty(position = 9, value = "Base64 encoded thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@ -34,6 +39,10 @@ public class WidgetTypeDetails extends WidgetType {
@ApiModelProperty(position = 10, value = "Description of the widget", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String description;
@Getter
@Setter
private WidgetTypeId externalId;
public WidgetTypeDetails() {
super();
}
@ -50,5 +59,6 @@ public class WidgetTypeDetails extends WidgetType {
super(widgetTypeDetails);
this.image = widgetTypeDetails.getImage();
this.description = widgetTypeDetails.getDescription();
this.externalId = widgetTypeDetails.getExternalId();
}
}

33
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2023 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.common.data.widget;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WidgetsBundleWidget {
private WidgetsBundleId widgetsBundleId;
private WidgetTypeId widgetTypeId;
private int widgetTypeOrder;
}

14
common/edge-api/src/main/proto/edge.proto

@ -355,19 +355,19 @@ message WidgetsBundleUpdateMsg {
optional bytes image = 6;
bool isSystem = 7;
optional string description = 8;
optional string widgets = 9;
}
message WidgetTypeUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
optional string bundleAlias = 4;
optional string fqn = 5;
optional string name = 6;
optional string descriptorJson = 7;
bool isSystem = 8;
optional string image = 9;
optional string description = 10;
optional string fqn = 4;
optional string name = 5;
optional string descriptorJson = 6;
bool isSystem = 7;
optional string image = 8;
optional string description = 9;
}
message AdminSettingsUpdateMsg {

8
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -308,7 +308,6 @@ public class ModelConstants {
*/
public static final String WIDGET_TYPE_TABLE_NAME = "widget_type";
public static final String WIDGET_TYPE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY = "bundle_alias";
public static final String WIDGET_TYPE_FQN_PROPERTY = "fqn";
public static final String WIDGET_TYPE_NAME_PROPERTY = "name";
@ -318,6 +317,13 @@ public class ModelConstants {
public static final String WIDGET_TYPE_DEPRECATED_PROPERTY = "deprecated";
/**
* Widgets bundle widget constants.
*/
public static final String WIDGETS_BUNDLE_WIDGET_TABLE_NAME = "widgets_bundle_widget";
public static final String WIDGET_TYPE_ORDER_PROPERTY = "widget_type_order";
/**
* Dashboard constants.
*/

6
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java

@ -36,9 +36,6 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
@Column(name = ModelConstants.WIDGET_TYPE_TENANT_ID_PROPERTY)
private UUID tenantId;
@Column(name = ModelConstants.WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY)
private String bundleAlias;
@Column(name = ModelConstants.WIDGET_TYPE_FQN_PROPERTY)
private String fqn;
@ -60,7 +57,6 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
if (widgetType.getTenantId() != null) {
this.tenantId = widgetType.getTenantId().getId();
}
this.bundleAlias = widgetType.getBundleAlias();
this.fqn = widgetType.getFqn();
this.name = widgetType.getName();
this.deprecated = widgetType.isDeprecated();
@ -70,7 +66,6 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
this.setId(widgetTypeEntity.getId());
this.setCreatedTime(widgetTypeEntity.getCreatedTime());
this.tenantId = widgetTypeEntity.getTenantId();
this.bundleAlias = widgetTypeEntity.getBundleAlias();
this.fqn = widgetTypeEntity.getFqn();
this.name = widgetTypeEntity.getName();
this.deprecated = widgetTypeEntity.isDeprecated();
@ -82,7 +77,6 @@ public abstract class AbstractWidgetTypeEntity<T extends BaseWidgetType> extends
if (tenantId != null) {
widgetType.setTenantId(TenantId.fromUUID(tenantId));
}
widgetType.setBundleAlias(bundleAlias);
widgetType.setFqn(fqn);
widgetType.setName(name);
widgetType.setDeprecated(deprecated);

12
dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java

@ -20,6 +20,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.BaseWidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.dao.model.ModelConstants;
@ -28,6 +30,7 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@ -46,6 +49,9 @@ public class WidgetTypeDetailsEntity extends AbstractWidgetTypeEntity<WidgetType
@Column(name = ModelConstants.WIDGET_TYPE_DESCRIPTOR_PROPERTY)
private JsonNode descriptor;
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY)
private UUID externalId;
public WidgetTypeDetailsEntity() {
super();
}
@ -55,6 +61,9 @@ public class WidgetTypeDetailsEntity extends AbstractWidgetTypeEntity<WidgetType
this.image = widgetTypeDetails.getImage();
this.description = widgetTypeDetails.getDescription();
this.descriptor = widgetTypeDetails.getDescriptor();
if (widgetTypeDetails.getExternalId() != null) {
this.externalId = widgetTypeDetails.getExternalId().getId();
}
}
@Override
@ -64,6 +73,9 @@ public class WidgetTypeDetailsEntity extends AbstractWidgetTypeEntity<WidgetType
widgetTypeDetails.setImage(image);
widgetTypeDetails.setDescription(description);
widgetTypeDetails.setDescriptor(descriptor);
if (externalId != null) {
widgetTypeDetails.setExternalId(new WidgetTypeId(externalId));
}
return widgetTypeDetails;
}
}

37
dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java

@ -0,0 +1,37 @@
/**
* Copyright © 2016-2023 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.dao.model.sql;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Transient;
import java.io.Serializable;
import java.util.UUID;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class WidgetsBundleWidgetCompositeKey implements Serializable {
@Transient
private static final long serialVersionUID = -245388185894468455L;
private UUID widgetsBundleId;
private UUID widgetTypeId;
}

75
dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java

@ -0,0 +1,75 @@
/**
* Copyright © 2016-2023 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.dao.model.sql;
import lombok.Data;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.model.ToData;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_WIDGET_TABLE_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_ORDER_PROPERTY;
@Data
@Entity
@Table(name = WIDGETS_BUNDLE_WIDGET_TABLE_NAME)
@IdClass(WidgetsBundleWidgetCompositeKey.class)
public final class WidgetsBundleWidgetEntity implements ToData<WidgetsBundleWidget> {
@Id
@Column(name = "widgets_bundle_id", columnDefinition = "uuid")
private UUID widgetsBundleId;
@Id
@Column(name = "widget_type_id", columnDefinition = "uuid")
private UUID widgetTypeId;
@Column(name = WIDGET_TYPE_ORDER_PROPERTY)
private int widgetTypeOrder;
public WidgetsBundleWidgetEntity() {
super();
}
public WidgetsBundleWidgetEntity(WidgetsBundleWidget widgetsBundleWidget) {
widgetsBundleId = widgetsBundleWidget.getWidgetsBundleId().getId();
widgetTypeId = widgetsBundleWidget.getWidgetTypeId().getId();
widgetTypeOrder = widgetsBundleWidget.getWidgetTypeOrder();
}
public WidgetsBundleWidgetEntity(UUID widgetsBundleId, UUID widgetTypeId, int widgetTypeOrder) {
this.widgetsBundleId = widgetsBundleId;
this.widgetTypeId = widgetTypeId;
this.widgetTypeOrder = widgetTypeOrder;
}
@Override
public WidgetsBundleWidget toData() {
WidgetsBundleWidget result = new WidgetsBundleWidget();
result.setWidgetsBundleId(new WidgetsBundleId(widgetsBundleId));
result.setWidgetTypeId(new WidgetTypeId(widgetTypeId));
result.setWidgetTypeOrder(widgetTypeOrder);
return result;
}
}

13
dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java

@ -42,9 +42,6 @@ public class WidgetTypeDataValidator extends DataValidator<WidgetTypeDetails> {
if (StringUtils.isEmpty(widgetTypeDetails.getName())) {
throw new DataValidationException("Widgets type name should be specified!");
}
if (StringUtils.isEmpty(widgetTypeDetails.getBundleAlias())) {
throw new DataValidationException("Widgets type bundle alias should be specified!");
}
if (widgetTypeDetails.getDescriptor() == null || widgetTypeDetails.getDescriptor().size() == 0) {
throw new DataValidationException("Widgets type descriptor can't be empty!");
}
@ -60,10 +57,6 @@ public class WidgetTypeDataValidator extends DataValidator<WidgetTypeDetails> {
@Override
protected void validateCreate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) {
WidgetsBundle widgetsBundle = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias());
if (widgetsBundle == null) {
throw new DataValidationException("Widget type is referencing to non-existent widgets bundle!");
}
String fqn = widgetTypeDetails.getFqn();
if (fqn == null || fqn.trim().isEmpty()) {
fqn = widgetTypeDetails.getName().toLowerCase().replaceAll("\\W+", "_");
@ -86,12 +79,6 @@ public class WidgetTypeDataValidator extends DataValidator<WidgetTypeDetails> {
if (!storedWidgetType.getTenantId().getId().equals(widgetTypeDetails.getTenantId().getId())) {
throw new DataValidationException("Can't move existing widget type to different tenant!");
}
if (!storedWidgetType.getBundleAlias().equals(widgetTypeDetails.getBundleAlias())) {
WidgetsBundle widgetsBundle = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias());
if (widgetsBundle == null) {
throw new DataValidationException("Widget type is referencing to non-existent widgets bundle!");
}
}
if (!storedWidgetType.getFqn().equals(widgetTypeDetails.getFqn())) {
throw new DataValidationException("Update of widget type fqn is prohibited!");
}

110
dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java

@ -20,17 +20,28 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.WidgetTypeDetailsEntity;
import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetCompositeKey;
import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetEntity;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import org.thingsboard.server.dao.widget.WidgetTypeDao;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
/**
* Created by Valerii Sosliuk on 4/29/2017.
@ -42,6 +53,9 @@ public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeDetailsEntity, Wi
@Autowired
private WidgetTypeRepository widgetTypeRepository;
@Autowired
private WidgetsBundleWidgetRepository widgetsBundleWidgetRepository;
@Override
protected Class<WidgetTypeDetailsEntity> getEntityClass() {
return WidgetTypeDetailsEntity.class;
@ -58,18 +72,59 @@ public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeDetailsEntity, Wi
}
@Override
public List<WidgetType> findWidgetTypesByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) {
return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias));
public boolean existsByTenantIdAndId(TenantId tenantId, UUID widgetTypeId) {
return widgetTypeRepository.existsByTenantIdAndId(tenantId.getId(), widgetTypeId);
}
@Override
public PageData<WidgetTypeInfo> findSystemWidgetTypes(TenantId tenantId, PageLink pageLink) {
return DaoUtil.toPageData(
widgetTypeRepository
.findSystemWidgetTypes(
NULL_UUID,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink) {
return DaoUtil.toPageData(
widgetTypeRepository
.findAllTenantWidgetTypesByTenantId(
tenantId,
NULL_UUID,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink) {
return DaoUtil.toPageData(
widgetTypeRepository
.findTenantWidgetTypesByTenantId(
tenantId,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public List<WidgetType> findWidgetTypesByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) {
return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesByWidgetsBundleId(widgetsBundleId));
}
@Override
public List<WidgetTypeDetails> findWidgetTypesDetailsByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) {
return DaoUtil.convertDataList(widgetTypeRepository.findByTenantIdAndBundleAlias(tenantId, bundleAlias));
public List<WidgetTypeDetails> findWidgetTypesDetailsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) {
return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesDetailsByWidgetsBundleId(widgetsBundleId));
}
@Override
public List<WidgetTypeInfo> findWidgetTypesInfosByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) {
return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias));
public List<WidgetTypeInfo> findWidgetTypesInfosByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) {
return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByWidgetsBundleId(widgetsBundleId));
}
@Override
public List<String> findWidgetFqnsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) {
return widgetTypeRepository.findWidgetFqnsByWidgetsBundleId(widgetsBundleId);
}
@Override
@ -82,9 +137,52 @@ public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeDetailsEntity, Wi
return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByTenantIdAndResourceId(tenantId, tbResourceId));
}
@Override
public List<WidgetTypeId> findWidgetTypeIdsByTenantIdAndFqns(UUID tenantId, List<String> widgetFqns) {
return widgetTypeRepository.findWidgetTypeIdsByTenantIdAndFqns(tenantId, widgetFqns).stream()
.map(WidgetTypeId::new).collect(Collectors.toList());
}
@Override
public WidgetTypeDetails findByTenantIdAndExternalId(UUID tenantId, UUID externalId) {
return DaoUtil.getData(widgetTypeRepository.findByTenantIdAndExternalId(tenantId, externalId));
}
@Override
public PageData<WidgetTypeDetails> findByTenantId(UUID tenantId, PageLink pageLink) {
return DaoUtil.toPageData(
widgetTypeRepository
.findTenantWidgetTypeDetailsByTenantId(
tenantId,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public WidgetTypeId getExternalIdByInternal(WidgetTypeId internalId) {
return Optional.ofNullable(widgetTypeRepository.getExternalIdById(internalId.getId()))
.map(WidgetTypeId::new).orElse(null);
}
@Override
public List<WidgetsBundleWidget> findWidgetsBundleWidgetsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) {
return DaoUtil.convertDataList(widgetsBundleWidgetRepository.findAllByWidgetsBundleId(widgetsBundleId));
}
@Override
public void saveWidgetsBundleWidget(WidgetsBundleWidget widgetsBundleWidget) {
widgetsBundleWidgetRepository.save(new WidgetsBundleWidgetEntity(widgetsBundleWidget));
}
@Override
public void removeWidgetTypeFromWidgetsBundle(UUID widgetsBundleId, UUID widgetTypeId) {
widgetsBundleWidgetRepository.deleteById(new WidgetsBundleWidgetCompositeKey(widgetsBundleId, widgetTypeId));
}
@Override
public EntityType getEntityType() {
return EntityType.WIDGET_TYPE;
}
}

66
dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java

@ -15,31 +15,76 @@
*/
package org.thingsboard.server.dao.sql.widget;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.ExportableEntityRepository;
import org.thingsboard.server.dao.model.sql.WidgetTypeDetailsEntity;
import org.thingsboard.server.dao.model.sql.WidgetTypeEntity;
import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity;
import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity;
import java.util.List;
import java.util.UUID;
public interface WidgetTypeRepository extends JpaRepository<WidgetTypeDetailsEntity, UUID> {
public interface WidgetTypeRepository extends JpaRepository<WidgetTypeDetailsEntity, UUID>, ExportableEntityRepository<WidgetTypeDetailsEntity> {
@Query("SELECT wt FROM WidgetTypeEntity wt WHERE wt.id = :widgetTypeId")
WidgetTypeEntity findWidgetTypeById(@Param("widgetTypeId") UUID widgetTypeId);
@Query("SELECT wt FROM WidgetTypeEntity wt WHERE wt.tenantId = :tenantId AND wt.bundleAlias = :bundleAlias")
List<WidgetTypeEntity> findWidgetTypesByTenantIdAndBundleAlias(@Param("tenantId") UUID tenantId,
@Param("bundleAlias") String bundleAlias);
boolean existsByTenantIdAndId(UUID tenantId, UUID id);
@Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity(wtd) FROM WidgetTypeDetailsEntity wtd " +
"WHERE wtd.tenantId = :tenantId AND wtd.bundleAlias = :bundleAlias")
List<WidgetTypeInfoEntity> findWidgetTypesInfosByTenantIdAndBundleAlias(@Param("tenantId") UUID tenantId,
@Param("bundleAlias") String bundleAlias);
@Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity(wtd) FROM WidgetTypeDetailsEntity wtd WHERE wtd.tenantId = :systemTenantId " +
"AND LOWER(wtd.name) LIKE LOWER(CONCAT('%', :searchText, '%'))")
Page<WidgetTypeInfoEntity> findSystemWidgetTypes(@Param("systemTenantId") UUID systemTenantId,
@Param("searchText") String searchText,
Pageable pageable);
List<WidgetTypeDetailsEntity> findByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias);
@Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity(wtd) FROM WidgetTypeDetailsEntity wtd WHERE wtd.tenantId IN (:tenantId, :nullTenantId) " +
"AND LOWER(wtd.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))")
Page<WidgetTypeInfoEntity> findAllTenantWidgetTypesByTenantId(@Param("tenantId") UUID tenantId,
@Param("nullTenantId") UUID nullTenantId,
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity(wtd) FROM WidgetTypeDetailsEntity wtd WHERE wtd.tenantId = :tenantId " +
"AND LOWER(wtd.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))")
Page<WidgetTypeInfoEntity> findTenantWidgetTypesByTenantId(@Param("tenantId") UUID tenantId,
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT wtd FROM WidgetTypeDetailsEntity wtd WHERE wtd.tenantId = :tenantId " +
"AND LOWER(wtd.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))")
Page<WidgetTypeDetailsEntity> findTenantWidgetTypeDetailsByTenantId(@Param("tenantId") UUID tenantId,
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT wt FROM WidgetTypeEntity wt, WidgetsBundleWidgetEntity wbw " +
"WHERE wbw.widgetsBundleId = :widgetsBundleId " +
"AND wbw.widgetTypeId = wt.id ORDER BY wbw.widgetTypeOrder")
List<WidgetTypeEntity> findWidgetTypesByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId);
@Query("SELECT wtd FROM WidgetTypeDetailsEntity wtd, WidgetsBundleWidgetEntity wbw " +
"WHERE wbw.widgetsBundleId = :widgetsBundleId " +
"AND wbw.widgetTypeId = wtd.id ORDER BY wbw.widgetTypeOrder")
List<WidgetTypeDetailsEntity> findWidgetTypesDetailsByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId);
@Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity(wtd) FROM WidgetTypeDetailsEntity wtd, WidgetsBundleWidgetEntity wbw " +
"WHERE wbw.widgetsBundleId = :widgetsBundleId " +
"AND wbw.widgetTypeId = wtd.id ORDER BY wbw.widgetTypeOrder")
List<WidgetTypeInfoEntity> findWidgetTypesInfosByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId);
@Query("SELECT wtd.fqn FROM WidgetTypeDetailsEntity wtd, WidgetsBundleWidgetEntity wbw " +
"WHERE wbw.widgetsBundleId = :widgetsBundleId " +
"AND wbw.widgetTypeId = wtd.id ORDER BY wbw.widgetTypeOrder")
List<String> findWidgetFqnsByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId);
@Query("SELECT wtd.id FROM WidgetTypeDetailsEntity wtd " +
"WHERE wtd.tenantId = :tenantId " +
"AND wtd.fqn IN (:widgetFqns)")
List<UUID> findWidgetTypeIdsByTenantIdAndFqns(@Param("tenantId") UUID tenantId, @Param("widgetFqns") List<String> widgetFqns);
@Query("SELECT wt FROM WidgetTypeEntity wt " +
"WHERE wt.tenantId = :tenantId AND wt.fqn = :fqn")
@ -52,4 +97,7 @@ public interface WidgetTypeRepository extends JpaRepository<WidgetTypeDetailsEnt
List<WidgetTypeDetailsEntity> findWidgetTypesInfosByTenantIdAndResourceId(@Param("tenantId") UUID tenantId,
@Param("resourceId") UUID resourceId);
@Query("SELECT externalId FROM WidgetTypeDetailsEntity WHERE id = :id")
UUID getExternalIdById(@Param("id") UUID id);
}

29
dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2023 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.dao.sql.widget;
import org.springframework.data.jpa.repository.JpaRepository;
import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetCompositeKey;
import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetEntity;
import java.util.List;
import java.util.UUID;
public interface WidgetsBundleWidgetRepository extends JpaRepository<WidgetsBundleWidgetEntity, WidgetsBundleWidgetCompositeKey> {
List<WidgetsBundleWidgetEntity> findAllByWidgetsBundleId(UUID widgetsBundleId);
}

5
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -58,6 +58,7 @@ import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import java.util.List;
@ -104,6 +105,9 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
@Autowired
private WidgetsBundleService widgetsBundleService;
@Autowired
private WidgetTypeService widgetTypeService;
@Autowired
private DashboardService dashboardService;
@ -218,6 +222,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
entityViewService.deleteEntityViewsByTenantId(tenantId);
widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId);
widgetTypeService.deleteWidgetTypesByTenantId(tenantId);
assetService.deleteAssetsByTenantId(tenantId);
assetProfileService.deleteAssetProfilesByTenantId(tenantId);
deviceService.deleteDevicesByTenantId(tenantId);

44
dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java

@ -16,10 +16,15 @@
package org.thingsboard.server.dao.widget;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.ExportableEntityDao;
import java.util.List;
import java.util.UUID;
@ -27,7 +32,7 @@ import java.util.UUID;
/**
* The Interface WidgetTypeDao.
*/
public interface WidgetTypeDao extends Dao<WidgetTypeDetails> {
public interface WidgetTypeDao extends Dao<WidgetTypeDetails>, ExportableEntityDao<WidgetTypeId, WidgetTypeDetails> {
/**
* Save or update widget type object
@ -46,32 +51,42 @@ public interface WidgetTypeDao extends Dao<WidgetTypeDetails> {
*/
WidgetType findWidgetTypeById(TenantId tenantId, UUID widgetTypeId);
boolean existsByTenantIdAndId(TenantId tenantId, UUID widgetTypeId);
PageData<WidgetTypeInfo> findSystemWidgetTypes(TenantId tenantId, PageLink pageLink);
PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink);
PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink);
/**
* Find widget types by tenantId and bundleAlias.
* Find widget types by widgetsBundleId.
*
* @param tenantId the tenantId
* @param bundleAlias the bundle alias
* @param widgetsBundleId the widgets bundle id
* @return the list of widget types objects
*/
List<WidgetType> findWidgetTypesByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias);
List<WidgetType> findWidgetTypesByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId);
/**
* Find widget types details by tenantId and bundleAlias.
* Find widget types details by widgetsBundleId.
*
* @param tenantId the tenantId
* @param bundleAlias the bundle alias
* @param widgetsBundleId the widgets bundle id
* @return the list of widget types details objects
*/
List<WidgetTypeDetails> findWidgetTypesDetailsByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias);
List<WidgetTypeDetails> findWidgetTypesDetailsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId);
/**
* Find widget types infos by tenantId and bundleAlias.
* Find widget types infos by widgetsBundleId.
*
* @param tenantId the tenantId
* @param bundleAlias the bundle alias
* @param widgetsBundleId the widgets bundle id
* @return the list of widget types infos objects
*/
List<WidgetTypeInfo> findWidgetTypesInfosByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias);
List<WidgetTypeInfo> findWidgetTypesInfosByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId);
List<String> findWidgetFqnsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId);
/**
* Find widget type by tenantId and FQN.
@ -90,4 +105,13 @@ public interface WidgetTypeDao extends Dao<WidgetTypeDetails> {
* @return the list of widget types infos objects
*/
List<WidgetTypeDetails> findWidgetTypesInfosByTenantIdAndResourceId(UUID tenantId, UUID tbResourceId);
List<WidgetTypeId> findWidgetTypeIdsByTenantIdAndFqns(UUID tenantId, List<String> widgetFqns);
List<WidgetsBundleWidget> findWidgetsBundleWidgetsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId);
void saveWidgetsBundleWidget(WidgetsBundleWidget widgetsBundleWidget);
void removeWidgetTypeFromWidgetsBundle(UUID widgetsBundleId, UUID widgetTypeId);
}

155
dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java

@ -22,28 +22,37 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.service.Validator.validateIds;
@Service("WidgetTypeDaoService")
@Slf4j
public class WidgetTypeServiceImpl extends AbstractEntityService implements WidgetTypeService {
public class WidgetTypeServiceImpl implements WidgetTypeService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId ";
public static final String INCORRECT_BUNDLE_ALIAS = "Incorrect bundleAlias ";
public static final String INCORRECT_WIDGETS_BUNDLE_ID = "Incorrect widgetsBundleId ";
@Autowired
private WidgetTypeDao widgetTypeDao;
@ -68,6 +77,13 @@ public class WidgetTypeServiceImpl extends AbstractEntityService implements Widg
return widgetTypeDao.findById(tenantId, widgetTypeId.getId());
}
@Override
public boolean widgetTypeExistsByTenantIdAndWidgetTypeId(TenantId tenantId, WidgetTypeId widgetTypeId) {
log.trace("Executing widgetTypeExistsByTenantIdAndWidgetTypeId, tenantId [{}], widgetTypeId [{}]", tenantId, widgetTypeId);
Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId);
return widgetTypeDao.existsByTenantIdAndId(tenantId, widgetTypeId.getId());
}
@Override
public WidgetTypeDetails saveWidgetType(WidgetTypeDetails widgetTypeDetails) {
log.trace("Executing saveWidgetType [{}]", widgetTypeDetails);
@ -78,27 +94,13 @@ public class WidgetTypeServiceImpl extends AbstractEntityService implements Widg
.entityId(result.getId()).added(widgetTypeDetails.getId() == null).build());
return result;
} catch (Exception t) {
checkConstraintViolation(t,
AbstractCachedEntityService.checkConstraintViolation(t,
"uq_widget_type_fqn", "Widget type with such fqn already exists!");
AbstractCachedEntityService.checkConstraintViolation(t, "widget_type_external_id_unq_key", "Widget type with such external id already exists!");
throw t;
}
}
@Override
public WidgetTypeDetails moveWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId, String targetBundleAlias) {
log.trace("Executing moveWidgetType, widgetTypeId [{}], targetBundleAlias [{}]", widgetTypeId, targetBundleAlias);
Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId);
WidgetTypeDetails widgetTypeDetails = widgetTypeDao.findById(tenantId, widgetTypeId.getId());
if (widgetTypeDetails != null && !widgetTypeDetails.getBundleAlias().equals(targetBundleAlias)) {
widgetTypeDetails.setBundleAlias(targetBundleAlias);
widgetTypeValidator.validate(widgetTypeDetails, WidgetType::getTenantId);
widgetTypeDetails = widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(widgetTypeDetails.getTenantId())
.entityId(widgetTypeDetails.getId()).added(widgetTypeDetails.getId() == null).build());
}
return widgetTypeDetails;
}
@Override
public void deleteWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId) {
log.trace("Executing deleteWidgetType [{}]", widgetTypeId);
@ -108,48 +110,59 @@ public class WidgetTypeServiceImpl extends AbstractEntityService implements Widg
}
@Override
public WidgetTypeDetails setWidgetTypeDeprecated(TenantId tenantId, WidgetTypeId widgetTypeId, boolean deprecated) {
log.trace("Executing setWidgetTypeDeprecated, widgetTypeId [{}], deprecated [{}]", widgetTypeId, deprecated);
Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId);
WidgetTypeDetails widgetTypeDetails = widgetTypeDao.findById(tenantId, widgetTypeId.getId());
if (widgetTypeDetails.isDeprecated() != deprecated) {
widgetTypeDetails.setDeprecated(deprecated);
widgetTypeDetails = widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails);
}
return widgetTypeDetails;
public PageData<WidgetTypeInfo> findSystemWidgetTypesByPageLink(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findSystemWidgetTypesByPageLink, pageLink [{}]", pageLink);
Validator.validatePageLink(pageLink);
return widgetTypeDao.findSystemWidgetTypes(tenantId, pageLink);
}
@Override
public PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAllTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validatePageLink(pageLink);
return widgetTypeDao.findAllTenantWidgetTypesByTenantId(tenantId.getId(), pageLink);
}
@Override
public List<WidgetType> findWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) {
log.trace("Executing findWidgetTypesByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias);
public PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], pageLink [{}]", tenantId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias);
return widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias);
Validator.validatePageLink(pageLink);
return widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId.getId(), pageLink);
}
@Override
public List<WidgetTypeDetails> findWidgetTypesDetailsByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) {
log.trace("Executing findWidgetTypesDetailsByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias);
public List<WidgetType> findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) {
log.trace("Executing findWidgetTypesByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias);
return widgetTypeDao.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias);
Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId);
return widgetTypeDao.findWidgetTypesByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId());
}
@Override
public List<WidgetTypeDetails> findWidgetTypesDetailsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) {
log.trace("Executing findWidgetTypesDetailsByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId);
return widgetTypeDao.findWidgetTypesDetailsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId());
}
@Override
public List<WidgetTypeInfo> findWidgetTypesInfosByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) {
log.trace("Executing findWidgetTypesInfosByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias);
public List<WidgetTypeInfo> findWidgetTypesInfosByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) {
log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias);
return widgetTypeDao.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias);
Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId);
return widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId());
}
@Override
public List<WidgetTypeDetails> findWidgetTypesInfosByTenantIdAndResourceId(TenantId tenantId, TbResourceId tbResourceId) {
log.trace("Executing findWidgetTypesInfosByTenantIdAndResourceId, tenantId [{}], tbResourceId [{}]", tenantId, tbResourceId);
public List<String> findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) {
log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(tbResourceId, INCORRECT_RESOURCE_ID + tbResourceId);
return widgetTypeDao.findWidgetTypesInfosByTenantIdAndResourceId(tenantId.getId(), tbResourceId.getId());
Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId);
return widgetTypeDao.findWidgetFqnsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId());
}
@Override
@ -161,14 +174,42 @@ public class WidgetTypeServiceImpl extends AbstractEntityService implements Widg
}
@Override
public void deleteWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) {
log.trace("Executing deleteWidgetTypesByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias);
public void updateWidgetsBundleWidgetTypes(TenantId tenantId, WidgetsBundleId widgetsBundleId, List<WidgetTypeId> widgetTypeIds) {
log.trace("Executing updateWidgetsBundleWidgetTypes, tenantId [{}], widgetsBundleId [{}], widgetTypeIds [{}]", tenantId, widgetsBundleId, widgetTypeIds);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias);
List<WidgetType> widgetTypes = widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias);
for (WidgetType widgetType : widgetTypes) {
deleteWidgetType(tenantId, new WidgetTypeId(widgetType.getUuidId()));
Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId);
validateIds(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds);
List<WidgetsBundleWidget> bundleWidgets = new ArrayList<>();
for (int index = 0; index < widgetTypeIds.size(); index++) {
bundleWidgets.add(new WidgetsBundleWidget(widgetsBundleId, widgetTypeIds.get(index), index));
}
List<WidgetsBundleWidget> existingBundleWidgets = widgetTypeDao.findWidgetsBundleWidgetsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId());
List<WidgetTypeId> toRemove = existingBundleWidgets.stream()
.map(WidgetsBundleWidget::getWidgetTypeId)
.filter(widgetTypeId -> bundleWidgets.stream().noneMatch(newBundleWidget ->
newBundleWidget.getWidgetTypeId().equals(widgetTypeId))).collect(Collectors.toList());
for (WidgetTypeId widgetTypeId : toRemove) {
widgetTypeDao.removeWidgetTypeFromWidgetsBundle(widgetsBundleId.getId(), widgetTypeId.getId());
}
for (WidgetsBundleWidget widgetsBundleWidget : bundleWidgets) {
widgetTypeDao.saveWidgetsBundleWidget(widgetsBundleWidget);
}
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(widgetsBundleId).added(false).build());
}
@Override
public void updateWidgetsBundleWidgetFqns(TenantId tenantId, WidgetsBundleId widgetsBundleId, List<String> widgetFqns) {
log.trace("Executing updateWidgetsBundleWidgetFqns, tenantId [{}], widgetsBundleId [{}], widgetFqns [{}]", tenantId, widgetsBundleId, widgetFqns);
List<WidgetTypeId> widgetTypeIds = widgetTypeDao.findWidgetTypeIdsByTenantIdAndFqns(tenantId.getId(), widgetFqns);
this.updateWidgetsBundleWidgetTypes(tenantId, widgetsBundleId, widgetTypeIds);
}
@Override
public void deleteWidgetTypesByTenantId(TenantId tenantId) {
log.trace("Executing deleteWidgetTypesByTenantId, tenantId [{}]", tenantId);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantWidgetTypeRemover.removeEntities(tenantId, tenantId);
}
@Override
@ -181,4 +222,18 @@ public class WidgetTypeServiceImpl extends AbstractEntityService implements Widg
return EntityType.WIDGET_TYPE;
}
private PaginatedRemover<TenantId, WidgetTypeInfo> tenantWidgetTypeRemover =
new PaginatedRemover<>() {
@Override
protected PageData<WidgetTypeInfo> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
return widgetTypeDao.findTenantWidgetTypesByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, WidgetTypeInfo entity) {
deleteWidgetType(tenantId, new WidgetTypeId(entity.getUuidId()));
}
};
}

5
dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java

@ -76,7 +76,9 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService {
.entityId(result.getId()).added(widgetsBundle.getId() == null).build());
return result;
} catch (Exception e) {
AbstractCachedEntityService.checkConstraintViolation(e, "widgets_bundle_external_id_unq_key", "Widget Bundle with such external id already exists!");
AbstractCachedEntityService.checkConstraintViolation(e,
"uq_widgets_bundle_alias", "Widgets Bundle with such alias already exists!");
AbstractCachedEntityService.checkConstraintViolation(e, "widgets_bundle_external_id_unq_key", "Widgets Bundle with such external id already exists!");
throw e;
}
}
@ -89,7 +91,6 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService {
if (widgetsBundle == null) {
throw new IncorrectParameterException("Unable to delete non-existent widgets bundle.");
}
widgetTypeService.deleteWidgetTypesByTenantIdAndBundleAlias(widgetsBundle.getTenantId(), widgetsBundle.getAlias());
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(widgetsBundleId).build());
widgetsBundleDao.removeById(tenantId, widgetsBundleId.getId());
}

15
dao/src/main/resources/sql/schema-entities.sql

@ -483,14 +483,15 @@ CREATE TABLE IF NOT EXISTS widget_type (
id uuid NOT NULL CONSTRAINT widget_type_pkey PRIMARY KEY,
created_time bigint NOT NULL,
fqn varchar(512),
bundle_alias varchar(255),
descriptor varchar(1000000),
name varchar(255),
tenant_id uuid,
image varchar(1000000),
deprecated boolean NOT NULL DEFAULT false,
description varchar(255),
CONSTRAINT uq_widget_type_fqn UNIQUE (tenant_id, fqn)
external_id uuid,
CONSTRAINT uq_widget_type_fqn UNIQUE (tenant_id, fqn),
CONSTRAINT widget_type_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS widgets_bundle (
@ -502,9 +503,19 @@ CREATE TABLE IF NOT EXISTS widgets_bundle (
image varchar(1000000),
description varchar(255),
external_id uuid,
CONSTRAINT uq_widgets_bundle_alias UNIQUE (tenant_id, alias),
CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id)
);
CREATE TABLE IF NOT EXISTS widgets_bundle_widget (
widgets_bundle_id uuid NOT NULL,
widget_type_id uuid NOT NULL,
widget_type_order int NOT NULL DEFAULT 0,
CONSTRAINT widgets_bundle_widget_pkey PRIMARY KEY (widgets_bundle_id, widget_type_id),
CONSTRAINT fk_widgets_bundle FOREIGN KEY (widgets_bundle_id) REFERENCES widgets_bundle(id) ON DELETE CASCADE,
CONSTRAINT fk_widget_type FOREIGN KEY (widget_type_id) REFERENCES widget_type(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS entity_view (
id uuid NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY,
created_time bigint NOT NULL,

152
dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java

@ -23,6 +23,7 @@ import org.junit.jupiter.api.Assertions;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
@ -35,12 +36,14 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@DaoSqlTest
public class WidgetTypeServiceTest extends AbstractServiceTest {
@Autowired
WidgetsBundleService widgetsBundleService;
@Autowired
WidgetTypeService widgetTypeService;
@ -48,15 +51,8 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
@Test
public void testSaveWidgetType() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
@ -68,7 +64,6 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
Assert.assertEquals(widgetType.getTenantId(), savedWidgetType.getTenantId());
Assert.assertEquals(widgetType.getName(), savedWidgetType.getName());
Assert.assertEquals(widgetType.getDescriptor(), savedWidgetType.getDescriptor());
Assert.assertEquals(savedWidgetsBundle.getAlias(), savedWidgetType.getBundleAlias());
savedWidgetType.setName("New Widget Type");
@ -76,34 +71,13 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
WidgetType foundWidgetType = widgetTypeService.findWidgetTypeById(tenantId, savedWidgetType.getId());
Assert.assertEquals(foundWidgetType.getName(), savedWidgetType.getName());
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId());
}
@Test
public void testSaveWidgetTypeWithEmptyName() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
try {
Assertions.assertThrows(DataValidationException.class, () -> {
widgetTypeService.saveWidgetType(widgetType);
});
} finally {
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
}
}
@Test
public void testSaveWidgetTypeWithEmptyBundleAlias() throws IOException {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
Assertions.assertThrows(DataValidationException.class, () -> {
widgetTypeService.saveWidgetType(widgetType);
@ -112,51 +86,19 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
@Test
public void testSaveWidgetTypeWithEmptyDescriptor() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setName("Widget Type");
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setDescriptor(JacksonUtil.fromString("{}", JsonNode.class));
try {
Assertions.assertThrows(DataValidationException.class, () -> {
widgetTypeService.saveWidgetType(widgetType);
});
} finally {
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
}
Assertions.assertThrows(DataValidationException.class, () -> {
widgetTypeService.saveWidgetType(widgetType);
});
}
@Test
public void testSaveWidgetTypeWithInvalidTenant() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(TenantId.fromUUID(Uuids.timeBased()));
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
try {
Assertions.assertThrows(DataValidationException.class, () -> {
widgetTypeService.saveWidgetType(widgetType);
});
} finally {
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
}
}
@Test
public void testSaveWidgetTypeWithInvalidBundleAlias() throws IOException {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias("some_alias");
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
Assertions.assertThrows(DataValidationException.class, () -> {
@ -166,14 +108,8 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
@Test
public void testUpdateWidgetTypeTenant() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
@ -183,43 +119,14 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
widgetTypeService.saveWidgetType(savedWidgetType);
});
} finally {
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
}
}
@Test
public void testUpdateWidgetTypeBundleAlias() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
savedWidgetType.setBundleAlias("some_alias");
try {
Assertions.assertThrows(DataValidationException.class, () -> {
widgetTypeService.saveWidgetType(savedWidgetType);
});
} finally {
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId());
}
}
@Test
public void testUpdateWidgetTypeFqn() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
@ -229,60 +136,40 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
widgetTypeService.saveWidgetType(savedWidgetType);
});
} finally {
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId());
}
}
@Test
public void testFindWidgetTypeById() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
WidgetTypeDetails foundWidgetType = widgetTypeService.findWidgetTypeDetailsById(tenantId, savedWidgetType.getId());
Assert.assertNotNull(foundWidgetType);
Assert.assertEquals(savedWidgetType, foundWidgetType);
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId());
}
@Test
public void testFindWidgetTypeByTenantIdAndFqn() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetType savedWidgetType = new WidgetType(widgetTypeService.saveWidgetType(widgetType));
WidgetType foundWidgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, savedWidgetType.getFqn());
Assert.assertNotNull(foundWidgetType);
Assert.assertEquals(savedWidgetType, foundWidgetType);
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId());
}
@Test
public void testDeleteWidgetType() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type");
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
WidgetType savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
@ -291,12 +178,10 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId());
foundWidgetType = widgetTypeService.findWidgetTypeById(tenantId, savedWidgetType.getId());
Assert.assertNull(foundWidgetType);
widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId());
}
@Test
public void testFindWidgetTypesByTenantIdAndBundleAlias() throws IOException {
public void testFindWidgetTypesByTenantIdAndWidgetsBundleId() throws IOException {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setTitle("Widgets bundle");
@ -306,22 +191,27 @@ public class WidgetTypeServiceTest extends AbstractServiceTest {
for (int i=0;i<121;i++) {
WidgetTypeDetails widgetType = new WidgetTypeDetails();
widgetType.setTenantId(tenantId);
widgetType.setBundleAlias(savedWidgetsBundle.getAlias());
widgetType.setName("Widget Type " + i);
widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class));
widgetTypes.add(new WidgetType(widgetTypeService.saveWidgetType(widgetType)));
}
List<WidgetType> loadedWidgetTypes = widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias());
List<WidgetTypeId> widgetTypeIds = widgetTypes.stream().map(WidgetType::getId).collect(Collectors.toList());
widgetTypeService.updateWidgetsBundleWidgetTypes(tenantId, savedWidgetsBundle.getId(), widgetTypeIds);
List<WidgetType> loadedWidgetTypes = widgetTypeService.findWidgetTypesByWidgetsBundleId(tenantId, savedWidgetsBundle.getId());
Collections.sort(widgetTypes, idComparator);
Collections.sort(loadedWidgetTypes, idComparator);
Assert.assertEquals(widgetTypes, loadedWidgetTypes);
widgetTypeService.deleteWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias());
for (WidgetTypeId id : widgetTypeIds) {
widgetTypeService.deleteWidgetType(tenantId, id);
}
loadedWidgetTypes = widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias());
loadedWidgetTypes = widgetTypeService.findWidgetTypesByWidgetsBundleId(tenantId, savedWidgetsBundle.getId());
Assert.assertTrue(loadedWidgetTypes.isEmpty());

29
dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java

@ -15,15 +15,20 @@
*/
package org.thingsboard.server.dao.sql.widget;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.AbstractJpaDaoTest;
import org.thingsboard.server.dao.widget.WidgetTypeDao;
import org.thingsboard.server.dao.widget.WidgetsBundleDao;
import java.util.ArrayList;
import java.util.List;
@ -39,15 +44,29 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
final String BUNDLE_ALIAS = "BUNDLE_ALIAS";
final int WIDGET_TYPE_COUNT = 3;
List<WidgetType> widgetTypeList;
WidgetsBundle widgetsBundle;
@Autowired
private WidgetTypeDao widgetTypeDao;
@Autowired
private WidgetsBundleDao widgetsBundleDao;
@Before
public void setUp() {
widgetTypeList = new ArrayList<>();
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setAlias(BUNDLE_ALIAS);
widgetsBundle.setTitle(BUNDLE_ALIAS);
widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased()));
widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID);
this.widgetsBundle = widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle);
for (int i = 0; i < WIDGET_TYPE_COUNT; i++) {
widgetTypeList.add(createAndSaveWidgetType(i));
var widgetType = createAndSaveWidgetType(i);
widgetTypeList.add(widgetType);
widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(this.widgetsBundle.getId(), widgetType.getId(), i));
}
}
@ -56,20 +75,20 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
widgetType.setTenantId(TenantId.SYS_TENANT_ID);
widgetType.setName("WIDGET_TYPE_" + number);
widgetType.setFqn("FQN_" + number);
widgetType.setBundleAlias(BUNDLE_ALIAS);
return widgetTypeDao.save(TenantId.SYS_TENANT_ID, widgetType);
}
@After
public void deleteAllWidgetType() {
public void tearDown() {
widgetsBundleDao.removeById(TenantId.SYS_TENANT_ID, widgetsBundle.getUuidId());
for (WidgetType widgetType : widgetTypeList) {
widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId());
}
}
@Test
public void testFindByTenantIdAndBundleAlias() {
List<WidgetType> widgetTypes = widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(TenantId.SYS_TENANT_ID.getId(), BUNDLE_ALIAS);
public void testFindByWidgetsBundleId() {
List<WidgetType> widgetTypes = widgetTypeDao.findWidgetTypesByWidgetsBundleId(TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId());
assertEquals(WIDGET_TYPE_COUNT, widgetTypes.size());
}

12
dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java

@ -86,9 +86,9 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
UUID tenantId2 = Uuids.timeBased();
// Create a bunch of widgetBundles
for (int i = 0; i < 10; i++) {
createWidgetBundles(3, tenantId1, "WB1_");
createWidgetBundles(5, tenantId2, "WB2_");
createSystemWidgetBundles(10, "WB_SYS_");
createWidgetBundles(3, tenantId1, "WB1_" + i + "_");
createWidgetBundles(5, tenantId2, "WB2_" + i + "_");
createSystemWidgetBundles(10, "WB_SYS_" + i + "_");
}
widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID);
assertEquals(180, widgetsBundleDao.find(TenantId.SYS_TENANT_ID).size());
@ -112,9 +112,9 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
UUID tenantId2 = Uuids.timeBased();
// Create a bunch of widgetBundles
for (int i = 0; i < 10; i++) {
createWidgetBundles(5, tenantId1, "WB1_");
createWidgetBundles(3, tenantId2, "WB2_");
createSystemWidgetBundles(2, "WB_SYS_");
createWidgetBundles(5, tenantId1, "WB1_" + i + "_");
createWidgetBundles(3, tenantId2, "WB2_" + i + "_");
createSystemWidgetBundles(2, "WB_SYS_" + i + "_");
}
widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID);
assertEquals(100, widgetsBundleDao.find(TenantId.SYS_TENANT_ID).size());

1
dao/src/test/resources/sql/psql/drop-all-tables.sql

@ -37,6 +37,7 @@ DROP TABLE IF EXISTS ts_kv;
DROP TABLE IF EXISTS ts_kv_latest;
DROP TABLE IF EXISTS ts_kv_dictionary;
DROP TABLE IF EXISTS user_credentials;
DROP TABLE IF EXISTS widgets_bundle_widget;
DROP TABLE IF EXISTS widget_type;
DROP TABLE IF EXISTS widgets_bundle;
DROP TABLE IF EXISTS entity_view;

Loading…
Cancel
Save