From 1fb9ba622eabbb72952f4b8bce9caa91bf4c61db Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 31 Aug 2023 12:09:42 +0300 Subject: [PATCH] Widget -> Widget Bundle Many to Many support. --- .../main/data/upgrade/3.5.1/schema_update.sql | 39 ++++ .../controller/ControllerConstants.java | 2 + .../controller/WidgetTypeController.java | 187 ++++++++++-------- .../controller/WidgetsBundleController.java | 30 +++ .../service/edge/EdgeContextComponent.java | 4 + .../service/edge/rpc/EdgeSyncCursor.java | 4 + .../constructor/WidgetTypeMsgConstructor.java | 3 - .../WidgetsBundleMsgConstructor.java | 5 +- .../BaseWidgetTypesEdgeEventFetcher.java | 52 +++++ .../SystemWidgetTypesEdgeEventFetcher.java | 36 ++++ .../TenantWidgetTypesEdgeEventFetcher.java | 35 ++++ .../widget/WidgetBundleEdgeProcessor.java | 5 +- .../rpc/sync/DefaultEdgeRequestsService.java | 2 +- .../bundle/DefaultWidgetsBundleService.java | 12 ++ .../bundle/TbWidgetsBundleService.java | 8 + .../type/DefaultWidgetTypeService.java | 64 ++++++ .../widgets/type/TbWidgetTypeService.java | 22 +++ .../service/install/InstallScripts.java | 2 +- .../DefaultEntitiesExportImportService.java | 2 +- .../impl/WidgetsBundleExportService.java | 2 +- .../impl/WidgetsBundleImportService.java | 27 +-- .../src/main/resources/thingsboard.yml | 1 + .../controller/TbResourceControllerTest.java | 5 - .../controller/WidgetTypeControllerTest.java | 128 ++---------- .../server/edge/WidgetEdgeTest.java | 1 - .../server/dao/widget/WidgetTypeService.java | 28 ++- .../data/sync/ie/WidgetsBundleExportData.java | 9 +- .../common/data/widget/BaseWidgetType.java | 5 - .../common/data/widget/WidgetTypeDetails.java | 14 +- .../data/widget/WidgetsBundleWidget.java | 33 ++++ common/edge-api/src/main/proto/edge.proto | 14 +- .../server/dao/model/ModelConstants.java | 8 +- .../model/sql/AbstractWidgetTypeEntity.java | 6 - .../model/sql/WidgetTypeDetailsEntity.java | 12 ++ .../sql/WidgetsBundleWidgetCompositeKey.java | 37 ++++ .../model/sql/WidgetsBundleWidgetEntity.java | 75 +++++++ .../validator/WidgetTypeDataValidator.java | 13 -- .../dao/sql/widget/JpaWidgetTypeDao.java | 110 ++++++++++- .../dao/sql/widget/WidgetTypeRepository.java | 66 ++++++- .../widget/WidgetsBundleWidgetRepository.java | 29 +++ .../server/dao/tenant/TenantServiceImpl.java | 5 + .../server/dao/widget/WidgetTypeDao.java | 44 ++++- .../dao/widget/WidgetTypeServiceImpl.java | 155 ++++++++++----- .../dao/widget/WidgetsBundleServiceImpl.java | 5 +- .../main/resources/sql/schema-entities.sql | 15 +- .../dao/service/WidgetTypeServiceTest.java | 152 ++------------ .../dao/sql/widget/JpaWidgetTypeDaoTest.java | 29 ++- .../sql/widget/JpaWidgetsBundleDaoTest.java | 12 +- .../resources/sql/psql/drop-all-tables.sql | 1 + 49 files changed, 1038 insertions(+), 517 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java diff --git a/application/src/main/data/upgrade/3.5.1/schema_update.sql b/application/src/main/data/upgrade/3.5.1/schema_update.sql index f23cb9f960..ab8e32e320 100644 --- a/application/src/main/data/upgrade/3.5.1/schema_update.sql +++ b/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; +$$; diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java index f31cebd258..e6b4235cf9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/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"; diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index 2b34432e12..469a6c2cb2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/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 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 getBundleWidgetTypes( + public List 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 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 getBundleWidgetTypesDetails( + public List 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 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 getBundleWidgetTypesInfos( + public List 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 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; - } - } diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 737e6d54d5..dff740ed09 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/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 strWidgetTypeIds) throws Exception { + checkParameter("widgetsBundleId", strWidgetsBundleId); + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + checkNotNull(strWidgetTypeIds); + Set 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')") diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index da63cb53d7..dc83e0f2a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/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; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java index a74a0df18c..40acc147ad 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java +++ b/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())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java index b2b82a34d7..300dfa479b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ b/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()); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java index 36e43b9579..bed83c8832 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ b/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 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(); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java new file mode 100644 index 0000000000..05d9071ed3 --- /dev/null +++ b/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 { + + protected final WidgetTypeService widgetTypeService; + + @Override + PageData 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 findWidgetTypes(TenantId tenantId, PageLink pageLink); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java new file mode 100644 index 0000000000..7dca9c9ccc --- /dev/null +++ b/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 findWidgetTypes(TenantId tenantId, PageLink pageLink) { + return widgetTypeService.findSystemWidgetTypesByPageLink(tenantId, pageLink); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java new file mode 100644 index 0000000000..1415056aef --- /dev/null +++ b/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 findWidgetTypes(TenantId tenantId, PageLink pageLink) { + return widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(tenantId, pageLink); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java index d50e752fdf..877ece1b15 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java +++ b/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 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) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java index 86a97f33db..6f90691aa0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java +++ b/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 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)); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java index efc20cd422..2f9333a61e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java +++ b/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 widgetTypeIds, User user) throws Exception { + widgetTypeService.updateWidgetsBundleWidgetTypes(user.getTenantId(), widgetsBundleId, widgetTypeIds); + autoCommit(user, widgetsBundleId); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java index d60bc66d66..9e266d206e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java +++ b/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 { + + void updateWidgetsBundleWidgetTypes(WidgetsBundleId widgetsBundleId, List widgetTypeIds, User user) throws Exception; + } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java new file mode 100644 index 0000000000..7f9ab2bb8c --- /dev/null +++ b/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; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java new file mode 100644 index 0000000000..fa68c8fa8b --- /dev/null +++ b/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 { +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 36ec2b9b78..bab69e0cb9 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/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()); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index e55fdb17c5..1cf0f0fc4d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/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 ); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java index 7024f36623..3fe9cd9e4a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java @@ -42,7 +42,7 @@ public class WidgetsBundleExportService extends BaseEntityExportService widgets = widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(ctx.getTenantId(), widgetsBundle.getAlias()); + List widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId()); exportData.setWidgets(widgets); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java index 4aea4799c7..975eb62f2f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java @@ -54,32 +54,7 @@ public class WidgetsBundleImportService extends BaseEntityImportService 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; } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 0207860e3f..93464478f5 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/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}" diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index bb542e6cf1..8725f43be1 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/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); diff --git a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java index c464649b11..9cc9fd0596 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java +++ b/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 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 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 loadedWidgetTypes = doGetTyped("/api/widgetTypes?isSystem={isSystem}&bundleAlias={bundleAlias}", - new TypeReference<>(){}, false, savedWidgetsBundle.getAlias()); + List widgetTypeIds = widgetTypes.stream().map(type -> type.getId().getId().toString()).collect(Collectors.toList()); + doPost("/api/widgetsBundle/" + widgetsBundle.getId().getId().toString() + "/widgetTypes", widgetTypeIds); + + List 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); diff --git a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java index c4fe89a209..517bb39cb5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java +++ b/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); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java index 51bffeef4f..897775e0c0 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java +++ b/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 findSystemWidgetTypesByPageLink(TenantId tenantId, PageLink pageLink); + + PageData findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); - List findWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + PageData findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); - List findWidgetTypesDetailsByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + List findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); - List findWidgetTypesInfosByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + List findWidgetTypesDetailsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); - List findWidgetTypesInfosByTenantIdAndResourceId(TenantId tenantId, TbResourceId tbResourceId); + List findWidgetTypesInfosByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); + + List findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); WidgetType findWidgetTypeByTenantIdAndFqn(TenantId tenantId, String fqn); - void deleteWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + void updateWidgetsBundleWidgetTypes(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetTypeIds); + + void updateWidgetsBundleWidgetFqns(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetFqns); + + void deleteWidgetTypesByTenantId(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java index 5efac34868..5ee856e312 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java +++ b/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 { @JsonProperty(index = 3) - private List widgets; - - @Override - public EntityExportData sort() { - super.sort(); - widgets.sort(Comparator.comparing(BaseWidgetType::getFqn)); - return this; - } + private List widgets; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java index 62c19663f9..8fd0e58006 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java @@ -33,10 +33,6 @@ public class BaseWidgetType extends BaseData 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 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(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java index 5fe63683eb..9ebc0b6639 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java +++ b/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 { @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(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java new file mode 100644 index 0000000000..1ac3c19132 --- /dev/null +++ b/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; + +} diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index e505eb1140..609e87a69b 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/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 { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index a364d3f393..0e8ea2db15 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/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. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java index acbaaf1a0e..08b2ebed77 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java @@ -36,9 +36,6 @@ public abstract class AbstractWidgetTypeEntity 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 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 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 extends if (tenantId != null) { widgetType.setTenantId(TenantId.fromUUID(tenantId)); } - widgetType.setBundleAlias(bundleAlias); widgetType.setFqn(fqn); widgetType.setName(name); widgetType.setDeprecated(deprecated); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java index c936952b40..be934e9c8f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java +++ b/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 { + + @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; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java index 6f634fac3a..1cec787b0f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java @@ -42,9 +42,6 @@ public class WidgetTypeDataValidator extends DataValidator { 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 { @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 { 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!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java index d17254b0fd..53e921f977 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java +++ b/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 getEntityClass() { return WidgetTypeDetailsEntity.class; @@ -58,18 +72,59 @@ public class JpaWidgetTypeDao extends JpaAbstractDao 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 findSystemWidgetTypes(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData( + widgetTypeRepository + .findSystemWidgetTypes( + NULL_UUID, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findAllTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData( + widgetTypeRepository + .findAllTenantWidgetTypesByTenantId( + tenantId, + NULL_UUID, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData( + widgetTypeRepository + .findTenantWidgetTypesByTenantId( + tenantId, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public List findWidgetTypesByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesByWidgetsBundleId(widgetsBundleId)); } @Override - public List findWidgetTypesDetailsByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) { - return DaoUtil.convertDataList(widgetTypeRepository.findByTenantIdAndBundleAlias(tenantId, bundleAlias)); + public List findWidgetTypesDetailsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesDetailsByWidgetsBundleId(widgetsBundleId)); } @Override - public List findWidgetTypesInfosByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) { - return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); + public List findWidgetTypesInfosByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByWidgetsBundleId(widgetsBundleId)); + } + + @Override + public List findWidgetFqnsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return widgetTypeRepository.findWidgetFqnsByWidgetsBundleId(widgetsBundleId); } @Override @@ -82,9 +137,52 @@ public class JpaWidgetTypeDao extends JpaAbstractDao findWidgetTypeIdsByTenantIdAndFqns(UUID tenantId, List 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 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 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; } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java index fae359ffce..6d991737ef 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java +++ b/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 { +public interface WidgetTypeRepository extends JpaRepository, ExportableEntityRepository { @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 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 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 findSystemWidgetTypes(@Param("systemTenantId") UUID systemTenantId, + @Param("searchText") String searchText, + Pageable pageable); - List 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 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 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 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 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 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 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 findWidgetFqnsByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId); + + @Query("SELECT wtd.id FROM WidgetTypeDetailsEntity wtd " + + "WHERE wtd.tenantId = :tenantId " + + "AND wtd.fqn IN (:widgetFqns)") + List findWidgetTypeIdsByTenantIdAndFqns(@Param("tenantId") UUID tenantId, @Param("widgetFqns") List widgetFqns); @Query("SELECT wt FROM WidgetTypeEntity wt " + "WHERE wt.tenantId = :tenantId AND wt.fqn = :fqn") @@ -52,4 +97,7 @@ public interface WidgetTypeRepository extends JpaRepository findWidgetTypesInfosByTenantIdAndResourceId(@Param("tenantId") UUID tenantId, @Param("resourceId") UUID resourceId); + @Query("SELECT externalId FROM WidgetTypeDetailsEntity WHERE id = :id") + UUID getExternalIdById(@Param("id") UUID id); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java new file mode 100644 index 0000000000..d064fca88e --- /dev/null +++ b/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 { + + List findAllByWidgetsBundleId(UUID widgetsBundleId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index bdce72d894..550c259f38 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/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 { +public interface WidgetTypeDao extends Dao, ExportableEntityDao { /** * Save or update widget type object @@ -46,32 +51,42 @@ public interface WidgetTypeDao extends Dao { */ WidgetType findWidgetTypeById(TenantId tenantId, UUID widgetTypeId); + boolean existsByTenantIdAndId(TenantId tenantId, UUID widgetTypeId); + + PageData findSystemWidgetTypes(TenantId tenantId, PageLink pageLink); + + PageData findAllTenantWidgetTypesByTenantId(UUID tenantId, PageLink pageLink); + + PageData 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 findWidgetTypesByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + List 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 findWidgetTypesDetailsByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + List 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 findWidgetTypesInfosByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + List findWidgetTypesInfosByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); + + List findWidgetFqnsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); /** * Find widget type by tenantId and FQN. @@ -90,4 +105,13 @@ public interface WidgetTypeDao extends Dao { * @return the list of widget types infos objects */ List findWidgetTypesInfosByTenantIdAndResourceId(UUID tenantId, UUID tbResourceId); + + List findWidgetTypeIdsByTenantIdAndFqns(UUID tenantId, List widgetFqns); + + List findWidgetsBundleWidgetsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); + + void saveWidgetsBundleWidget(WidgetsBundleWidget widgetsBundleWidget); + + void removeWidgetTypeFromWidgetsBundle(UUID widgetsBundleId, UUID widgetTypeId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java index 6540a5ea85..19e428f5f2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java +++ b/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 findSystemWidgetTypesByPageLink(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findSystemWidgetTypesByPageLink, pageLink [{}]", pageLink); + Validator.validatePageLink(pageLink); + return widgetTypeDao.findSystemWidgetTypes(tenantId, pageLink); + } + + @Override + public PageData 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 findWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing findWidgetTypesByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public PageData 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 findWidgetTypesDetailsByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing findWidgetTypesDetailsByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public List 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 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 findWidgetTypesInfosByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing findWidgetTypesInfosByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public List 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 findWidgetTypesInfosByTenantIdAndResourceId(TenantId tenantId, TbResourceId tbResourceId) { - log.trace("Executing findWidgetTypesInfosByTenantIdAndResourceId, tenantId [{}], tbResourceId [{}]", tenantId, tbResourceId); + public List 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 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 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 bundleWidgets = new ArrayList<>(); + for (int index = 0; index < widgetTypeIds.size(); index++) { + bundleWidgets.add(new WidgetsBundleWidget(widgetsBundleId, widgetTypeIds.get(index), index)); } + List existingBundleWidgets = widgetTypeDao.findWidgetsBundleWidgetsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); + List 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 widgetFqns) { + log.trace("Executing updateWidgetsBundleWidgetFqns, tenantId [{}], widgetsBundleId [{}], widgetFqns [{}]", tenantId, widgetsBundleId, widgetFqns); + List 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 tenantWidgetTypeRemover = + new PaginatedRemover<>() { + + @Override + protected PageData 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())); + } + }; + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java index 6becfb460f..4f094ca62f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java +++ b/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()); } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index b709c44679..5133049819 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/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, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java index d5dba46c8f..679c288ae7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java +++ b/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 loadedWidgetTypes = widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias()); + List widgetTypeIds = widgetTypes.stream().map(WidgetType::getId).collect(Collectors.toList()); + + widgetTypeService.updateWidgetsBundleWidgetTypes(tenantId, savedWidgetsBundle.getId(), widgetTypeIds); + + List 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()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java index b97f37cd5a..8d289c6e1a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java +++ b/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 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 widgetTypes = widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(TenantId.SYS_TENANT_ID.getId(), BUNDLE_ALIAS); + public void testFindByWidgetsBundleId() { + List widgetTypes = widgetTypeDao.findWidgetTypesByWidgetsBundleId(TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId()); assertEquals(WIDGET_TYPE_COUNT, widgetTypes.size()); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java index 209d4193d3..e1758c4660 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java +++ b/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()); diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 2fca79e01e..75b892452a 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/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;