Browse Source

Ability to show SCADA widget bundles/widgets first when adding widget to SCADA layout.

pull/11391/head
Igor Kulikov 2 years ago
parent
commit
81fa9a6ff2
  1. 1
      application/src/main/data/json/system/widget_bundles/scada_symbols.json
  2. 1
      application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json
  3. 1
      application/src/main/data/json/system/widget_types/scada_symbol.json
  4. 21
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  5. 1
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  6. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java
  8. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java
  9. 1
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  10. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java
  11. 34
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeFilter.java
  12. 44
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java
  13. 12
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java
  14. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java
  15. 7
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java
  16. 33
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java
  17. 77
      dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java
  18. 65
      dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java
  19. 4
      ui-ngx/src/app/core/http/entity.service.ts
  20. 11
      ui-ngx/src/app/core/http/widget.service.ts
  21. 1
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html
  22. 2
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  23. 10
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts
  24. 1
      ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.html
  25. 13
      ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.scss
  26. 8
      ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts
  27. 1
      ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
  28. 5
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  29. 17
      ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html
  30. 11
      ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html
  31. 2
      ui-ngx/src/app/modules/home/pages/widget/widget-type.component.ts
  32. 3
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html
  33. 2
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts
  34. 3
      ui-ngx/src/app/shared/models/widget.models.ts
  35. 1
      ui-ngx/src/app/shared/models/widgets-bundle.model.ts
  36. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

1
application/src/main/data/json/system/widget_bundles/scada_symbols.json

@ -2,6 +2,7 @@
"widgetsBundle": {
"alias": "scada_symbols",
"title": "SCADA symbols",
"scada": true,
"image": null,
"description": "Bundle with SCADA symbols",
"order": 9200,

1
application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json

@ -2,6 +2,7 @@
"widgetsBundle": {
"alias": "scada_water_system_symbols",
"title": "SCADA water system symbols",
"scada": true,
"image": null,
"description": "Bundle with SCADA symbols for water system",
"order": 9300,

1
application/src/main/data/json/system/widget_types/scada_symbol.json

@ -2,6 +2,7 @@
"fqn": "scada_symbol",
"name": "SCADA symbol",
"deprecated": false,
"scada": true,
"image": null,
"description": "",
"descriptor": {

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

@ -42,8 +42,10 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.common.data.widget.WidgetsBundleFilter;
import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.resource.ImageService;
@ -84,6 +86,7 @@ public class WidgetTypeController extends AutoCommitController {
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";
private static final String FULL_SEARCH_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether search widgets by description not only by name";
private static final String SCADA_FIRST_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether to fetch SCADA symbol widgets first";
private static final String DEPRECATED_FILTER_PARAM_DESCRIPTION = "Optional string parameter indicating whether to include deprecated widgets";
private static final String UPDATE_EXISTING_BY_FQN_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether to update existing widget type by FQN if present instead of creating new one";
private static final String WIDGET_TYPE_ARRAY_DESCRIPTION = "A list of string values separated by comma ',' representing one of the widget type value";
@ -187,18 +190,26 @@ public class WidgetTypeController extends AutoCommitController {
@Parameter(description = DEPRECATED_FILTER_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"ALL", "ACTUAL", "DEPRECATED"}))
@RequestParam(required = false) String deprecatedFilter,
@Parameter(description = WIDGET_TYPE_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"timeseries", "latest", "control", "alarm", "static"})))
@RequestParam(required = false) String[] widgetTypeList) throws ThingsboardException {
@RequestParam(required = false) String[] widgetTypeList,
@Parameter(description = SCADA_FIRST_PARAM_DESCRIPTION)
@RequestParam(required = false) Boolean scadaFirst) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
List<String> widgetTypes = widgetTypeList != null ? Arrays.asList(widgetTypeList) : Collections.emptyList();
boolean fullSearchBool = fullSearch != null && fullSearch;
DeprecatedFilter widgetTypeDeprecatedFilter = StringUtils.isNotEmpty(deprecatedFilter) ? DeprecatedFilter.valueOf(deprecatedFilter) : DeprecatedFilter.ALL;
WidgetTypeFilter widgetTypeFilter = WidgetTypeFilter.builder()
.tenantId(getTenantId())
.widgetTypes(widgetTypes)
.deprecatedFilter(widgetTypeDeprecatedFilter)
.fullSearch(fullSearch != null && fullSearch)
.scadaFirst(scadaFirst != null && scadaFirst)
.build();
if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
return checkNotNull(widgetTypeService.findSystemWidgetTypesByPageLink(getTenantId(), fullSearchBool, widgetTypeDeprecatedFilter, widgetTypes, pageLink));
return checkNotNull(widgetTypeService.findSystemWidgetTypesByPageLink(widgetTypeFilter, pageLink));
} else {
if (tenantOnly != null && tenantOnly) {
return checkNotNull(widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(getTenantId(), fullSearchBool, widgetTypeDeprecatedFilter, widgetTypes, pageLink));
return checkNotNull(widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(widgetTypeFilter, pageLink));
} else {
return checkNotNull(widgetTypeService.findAllTenantWidgetTypesByTenantIdAndPageLink(getTenantId(), fullSearchBool, widgetTypeDeprecatedFilter, widgetTypes, pageLink));
return checkNotNull(widgetTypeService.findAllTenantWidgetTypesByTenantIdAndPageLink(widgetTypeFilter, pageLink));
}
}
}

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

@ -212,7 +212,6 @@ public class WidgetsBundleController extends BaseController {
if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(widgetsBundleFilter, pageLink));
} else {
TenantId tenantId = getCurrentUser().getTenantId();
if (tenantOnly != null && tenantOnly) {
return checkNotNull(widgetsBundleService.findTenantWidgetsBundlesByTenantIdAndPageLink(widgetsBundleFilter, pageLink));
} else {

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

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.dao.widget.WidgetTypeService;
@ -32,6 +33,12 @@ public class SystemWidgetTypesEdgeEventFetcher extends BaseWidgetTypesEdgeEventF
@Override
protected PageData<WidgetTypeInfo> findWidgetTypes(TenantId tenantId, PageLink pageLink) {
return widgetTypeService.findSystemWidgetTypesByPageLink(tenantId, false, DeprecatedFilter.ALL, null, pageLink);
return widgetTypeService.findSystemWidgetTypesByPageLink(
WidgetTypeFilter.builder()
.tenantId(tenantId)
.fullSearch(false)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
pageLink);
}
}

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.common.data.widget.WidgetsBundleFilter;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
@Slf4j
@ -31,6 +32,6 @@ public class SystemWidgetsBundlesEdgeEventFetcher extends BaseWidgetsBundlesEdge
@Override
protected PageData<WidgetsBundle> findWidgetsBundles(TenantId tenantId, PageLink pageLink) {
return widgetsBundleService.findSystemWidgetsBundlesByPageLink(tenantId, false, pageLink);
return widgetsBundleService.findSystemWidgetsBundlesByPageLink(WidgetsBundleFilter.fromTenantId(tenantId), pageLink);
}
}

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

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.dao.widget.WidgetTypeService;
@ -31,6 +32,12 @@ public class TenantWidgetTypesEdgeEventFetcher extends BaseWidgetTypesEdgeEventF
}
@Override
protected PageData<WidgetTypeInfo> findWidgetTypes(TenantId tenantId, PageLink pageLink) {
return widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(tenantId, false, DeprecatedFilter.ALL, null, pageLink);
return widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(
WidgetTypeFilter.builder()
.tenantId(tenantId)
.fullSearch(false)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
pageLink);
}
}

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

@ -364,6 +364,7 @@ public class InstallScripts {
scadaSymbolWidget.setImage(symbolUrl);
scadaSymbolWidget.setDescription(metadata.getDescription());
scadaSymbolWidget.setTags(metadata.getSearchTags());
scadaSymbolWidget.setScada(true);
ObjectNode defaultConfig = null;
if (descriptor.has("defaultConfig")) {
defaultConfig = JacksonUtil.fromString(descriptor.get("defaultConfig").asText(), ObjectNode.class);

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

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.dao.entity.EntityDaoService;
@ -40,11 +41,11 @@ public interface WidgetTypeService extends EntityDaoService {
void deleteWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId);
PageData<WidgetTypeInfo> findSystemWidgetTypesByPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink);
PageData<WidgetTypeInfo> findSystemWidgetTypesByPageLink(WidgetTypeFilter widgetTypeFilter, PageLink pageLink);
PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink);
PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantIdAndPageLink(WidgetTypeFilter widgetTypeFilter, PageLink pageLink);
PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink);
PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantIdAndPageLink(WidgetTypeFilter widgetTypeFilter, PageLink pageLink);
List<WidgetType> findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId);

34
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeFilter.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2024 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.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
@Data
@Builder
public class WidgetTypeFilter {
private TenantId tenantId;
private boolean fullSearch;
private boolean scadaFirst;
DeprecatedFilter deprecatedFilter;
List<String> widgetTypes;
}

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

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.DaoUtil;
@ -84,57 +85,60 @@ public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeDetailsEntity, Wi
}
@Override
public PageData<WidgetTypeInfo> findSystemWidgetTypes(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink) {
boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter);
boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter);
boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty();
public PageData<WidgetTypeInfo> findSystemWidgetTypes(WidgetTypeFilter widgetTypeFilter, PageLink pageLink) {
boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(widgetTypeFilter.getDeprecatedFilter());
boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(widgetTypeFilter.getDeprecatedFilter());
boolean widgetTypesEmpty = widgetTypeFilter.getWidgetTypes() == null || widgetTypeFilter.getWidgetTypes().isEmpty();
return DaoUtil.toPageData(
widgetTypeInfoRepository
.findSystemWidgetTypes(
NULL_UUID,
pageLink.getTextSearch(),
fullSearch,
widgetTypeFilter.isFullSearch(),
deprecatedFilterEnabled,
deprecatedFilterBool,
widgetTypesEmpty,
widgetTypes == null ? Collections.emptyList() : widgetTypes,
widgetTypeFilter.getWidgetTypes() == null ? Collections.emptyList() : widgetTypeFilter.getWidgetTypes(),
widgetTypeFilter.isScadaFirst(),
DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP)));
}
@Override
public PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink) {
boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter);
boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter);
boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty();
public PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantId(WidgetTypeFilter widgetTypeFilter, PageLink pageLink) {
boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(widgetTypeFilter.getDeprecatedFilter());
boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(widgetTypeFilter.getDeprecatedFilter());
boolean widgetTypesEmpty = widgetTypeFilter.getWidgetTypes() == null || widgetTypeFilter.getWidgetTypes().isEmpty();
return DaoUtil.toPageData(
widgetTypeInfoRepository
.findAllTenantWidgetTypesByTenantId(
tenantId,
widgetTypeFilter.getTenantId().getId(),
NULL_UUID,
pageLink.getTextSearch(),
fullSearch,
widgetTypeFilter.isFullSearch(),
deprecatedFilterEnabled,
deprecatedFilterBool,
widgetTypesEmpty,
widgetTypes == null ? Collections.emptyList() : widgetTypes,
widgetTypeFilter.getWidgetTypes() == null ? Collections.emptyList() : widgetTypeFilter.getWidgetTypes(),
widgetTypeFilter.isScadaFirst(),
DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP)));
}
@Override
public PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink) {
boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter);
boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter);
boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty();
public PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantId(WidgetTypeFilter widgetTypeFilter, PageLink pageLink) {
boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(widgetTypeFilter.getDeprecatedFilter());
boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(widgetTypeFilter.getDeprecatedFilter());
boolean widgetTypesEmpty = widgetTypeFilter.getWidgetTypes() == null || widgetTypeFilter.getWidgetTypes().isEmpty();
return DaoUtil.toPageData(
widgetTypeInfoRepository
.findTenantWidgetTypesByTenantId(
tenantId,
widgetTypeFilter.getTenantId().getId(),
pageLink.getTextSearch(),
fullSearch,
widgetTypeFilter.isFullSearch(),
deprecatedFilterEnabled,
deprecatedFilterBool,
widgetTypesEmpty,
widgetTypes == null ? Collections.emptyList() : widgetTypes,
widgetTypeFilter.getWidgetTypes() == null ? Collections.emptyList() : widgetTypeFilter.getWidgetTypes(),
widgetTypeFilter.isScadaFirst(),
DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP)));
}

12
dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java

@ -41,7 +41,8 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
"OR :searchText ILIKE currentTag || ' %' " +
"OR :searchText ILIKE '% ' || currentTag " +
"OR :searchText ILIKE '% ' || currentTag || ' %')" +
"))))",
")))) " +
"ORDER BY CASE WHEN :scadaFirst then wti.scada END DESC",
countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id = :systemTenantId " +
"AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " +
"AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " +
@ -64,6 +65,7 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
@Param("deprecatedFilter") boolean deprecatedFilter,
@Param("widgetTypesEmpty") boolean widgetTypesEmpty,
@Param("widgetTypes") List<String> widgetTypes,
@Param("scadaFirst") boolean scadaFirst,
Pageable pageable);
@Query(nativeQuery = true,
@ -80,7 +82,8 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
"OR :searchText ILIKE currentTag || ' %' " +
"OR :searchText ILIKE '% ' || currentTag " +
"OR :searchText ILIKE '% ' || currentTag || ' %')" +
"))))",
")))) " +
"ORDER BY CASE WHEN :scadaFirst then wti.scada END DESC",
countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id IN (:tenantId, :nullTenantId) " +
"AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " +
"AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " +
@ -104,6 +107,7 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
@Param("deprecatedFilter") boolean deprecatedFilter,
@Param("widgetTypesEmpty") boolean widgetTypesEmpty,
@Param("widgetTypes") List<String> widgetTypes,
@Param("scadaFirst") boolean scadaFirst,
Pageable pageable);
@Query(nativeQuery = true,
@ -120,7 +124,8 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
"OR :searchText ILIKE currentTag || ' %' " +
"OR :searchText ILIKE '% ' || currentTag " +
"OR :searchText ILIKE '% ' || currentTag || ' %')" +
"))))",
")))) " +
"ORDER BY CASE WHEN :scadaFirst then wti.scada END DESC",
countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id = :tenantId " +
"AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " +
"AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " +
@ -143,6 +148,7 @@ public interface WidgetTypeInfoRepository extends JpaRepository<WidgetTypeInfoEn
@Param("deprecatedFilter") boolean deprecatedFilter,
@Param("widgetTypesEmpty") boolean widgetTypesEmpty,
@Param("widgetTypes") List<String> widgetTypes,
@Param("scadaFirst") boolean scadaFirst,
Pageable pageable);
@Query("SELECT wti FROM WidgetTypeInfoEntity wti, WidgetsBundleWidgetEntity wbw " +

2
dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java

@ -107,7 +107,7 @@ public interface WidgetsBundleRepository extends JpaRepository<WidgetsBundleEnti
"OR :textSearch ILIKE '% ' || currentTag " +
"OR :textSearch ILIKE '% ' || currentTag || ' %')" +
")))) " +
"ORDER BY CASE WHEN :scadaFirst then wb.scada END ASC, wb.widgets_bundle_order ASC NULLS LAST",
"ORDER BY CASE WHEN :scadaFirst then wb.scada END DESC, wb.widgets_bundle_order ASC NULLS LAST",
countQuery = "SELECT count(*) FROM widgets_bundle wb WHERE wb.tenant_id IN (:tenantIds) " +
"AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " +
"OR wb.description ILIKE CONCAT('%', :textSearch, '%') " +

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

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.Dao;
@ -55,11 +56,11 @@ public interface WidgetTypeDao extends Dao<WidgetTypeDetails>, ExportableEntityD
boolean existsByTenantIdAndId(TenantId tenantId, UUID widgetTypeId);
PageData<WidgetTypeInfo> findSystemWidgetTypes(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink);
PageData<WidgetTypeInfo> findSystemWidgetTypes(WidgetTypeFilter widgetTypeFilter, PageLink pageLink);
PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink);
PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantId(WidgetTypeFilter widgetTypeFilter, PageLink pageLink);
PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink);
PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantId(WidgetTypeFilter widgetTypeFilter, PageLink pageLink);
/**
* Find widget types by widgetsBundleId.

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

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
@ -121,28 +122,30 @@ public class WidgetTypeServiceImpl implements WidgetTypeService {
}
@Override
public PageData<WidgetTypeInfo> findSystemWidgetTypesByPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink) {
log.trace("Executing findSystemWidgetTypesByPageLink, fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", fullSearch, deprecatedFilter, widgetTypes, pageLink);
public PageData<WidgetTypeInfo> findSystemWidgetTypesByPageLink(WidgetTypeFilter widgetTypeFilter, PageLink pageLink) {
log.trace("Executing findSystemWidgetTypesByPageLink, pageLink [{}]", pageLink);
Validator.validatePageLink(pageLink);
return widgetTypeDao.findSystemWidgetTypes(tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink);
return widgetTypeDao.findSystemWidgetTypes(widgetTypeFilter, pageLink);
}
@Override
public PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink) {
log.trace("Executing findAllTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]",
tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink);
public PageData<WidgetTypeInfo> findAllTenantWidgetTypesByTenantIdAndPageLink(WidgetTypeFilter widgetTypeFilter, PageLink pageLink) {
TenantId tenantId = widgetTypeFilter.getTenantId();
log.trace("Executing findAllTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], pageLink [{}]",
tenantId, pageLink);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return widgetTypeDao.findAllTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink);
return widgetTypeDao.findAllTenantWidgetTypesByTenantId(widgetTypeFilter, pageLink);
}
@Override
public PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List<String> widgetTypes, PageLink pageLink) {
log.trace("Executing findTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]",
tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink);
public PageData<WidgetTypeInfo> findTenantWidgetTypesByTenantIdAndPageLink(WidgetTypeFilter widgetTypeFilter, PageLink pageLink) {
TenantId tenantId = widgetTypeFilter.getTenantId();
log.trace("Executing findTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], pageLink [{}]",
tenantId, pageLink);
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
Validator.validatePageLink(pageLink);
return widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink);
return widgetTypeDao.findTenantWidgetTypesByTenantId(widgetTypeFilter, pageLink);
}
@Override
@ -260,7 +263,13 @@ public class WidgetTypeServiceImpl implements WidgetTypeService {
@Override
protected PageData<WidgetTypeInfo> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
return widgetTypeDao.findTenantWidgetTypesByTenantId(id.getId(), false, DeprecatedFilter.ALL, null, pageLink);
return widgetTypeDao.findTenantWidgetTypesByTenantId(
WidgetTypeFilter.builder()
.tenantId(id)
.fullSearch(false)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
pageLink);
}
@Override

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

@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.widget.BaseWidgetType;
import org.thingsboard.server.common.data.widget.DeprecatedFilter;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetTypeFilter;
import org.thingsboard.server.common.data.widget.WidgetTypeInfo;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.common.data.widget.WidgetsBundleWidget;
@ -151,12 +152,22 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
@Test
public void testFindSystemWidgetTypes() {
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"),
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findSystemWidgetTypes(
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(Collections.singletonList("static")).build(),
new PageLink(1024, 0, "TYPE_DESCRIPTION", new SortOrder("createdTime")));
assertEquals(1, widgetTypes.getData().size());
assertEquals(new WidgetTypeInfo(widgetTypeList.get(1)), widgetTypes.getData().get(0));
widgetTypes = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.emptyList(),
widgetTypes = widgetTypeDao.findSystemWidgetTypes(
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(Collections.emptyList()).build(),
new PageLink(1024, 0, "hfgfd tag2_2 ghg", new SortOrder("createdTime")));
assertEquals(1, widgetTypes.getData().size());
assertEquals(new WidgetTypeInfo(widgetTypeList.get(2)), widgetTypes.getData().get(0));
@ -175,12 +186,21 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
sameNameList.sort(Comparator.comparing(BaseWidgetType::getName).thenComparing((BaseWidgetType baseWidgetType) -> baseWidgetType.getId().getId()));
List<WidgetTypeInfo> expected = sameNameList.stream().map(WidgetTypeInfo::new).collect(Collectors.toList());
PageData<WidgetTypeInfo> widgetTypesFirstPage = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"),
PageData<WidgetTypeInfo> widgetTypesFirstPage = widgetTypeDao.findSystemWidgetTypes(
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(Collections.singletonList("static")).build(),
new PageLink(10, 0, null, new SortOrder("name")));
assertEquals(10, widgetTypesFirstPage.getData().size());
assertThat(widgetTypesFirstPage.getData()).containsExactlyElementsOf(expected.subList(0, 10));
PageData<WidgetTypeInfo> widgetTypesSecondPage = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"),
PageData<WidgetTypeInfo> widgetTypesSecondPage = widgetTypeDao.findSystemWidgetTypes(WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(Collections.singletonList("static")).build(),
new PageLink(10, 1, null, new SortOrder("name")));
assertEquals(10, widgetTypesSecondPage.getData().size());
assertThat(widgetTypesSecondPage.getData()).containsExactlyElementsOf(expected.subList(10, 20));
@ -195,7 +215,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags);
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findSystemWidgetTypes(
TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText)
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, searchText)
);
assertThat(widgetTypes.getData()).hasSize(1);
@ -211,7 +236,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags);
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findSystemWidgetTypes(
TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText)
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, searchText)
);
assertThat(widgetTypes.getData()).hasSize(0);
@ -227,7 +257,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
var widgetType = createAndSaveWidgetType(new TenantId(tenantId), i);
widgetTypeList.add(widgetType);
}
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId, true, DeprecatedFilter.ALL, null,
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId(
WidgetTypeFilter.builder()
.tenantId(new TenantId(tenantId))
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, "", new SortOrder("createdTime")));
assertEquals(WIDGET_TYPE_COUNT, widgetTypes.getData().size());
assertEquals(new WidgetTypeInfo(widgetTypeList.get(3)), widgetTypes.getData().get(0));
@ -244,7 +279,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags);
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId(
TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText)
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, searchText)
);
assertThat(widgetTypes.getData()).hasSize(1);
@ -260,7 +300,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags);
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId(
TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText)
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, searchText)
);
assertThat(widgetTypes.getData()).hasSize(0);
@ -278,7 +323,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags);
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findAllTenantWidgetTypesByTenantId(
TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText)
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, searchText)
);
assertThat(widgetTypes.getData()).hasSize(1);
@ -294,7 +344,12 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest {
WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags);
PageData<WidgetTypeInfo> widgetTypes = widgetTypeDao.findAllTenantWidgetTypesByTenantId(
TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText)
WidgetTypeFilter.builder()
.tenantId(TenantId.SYS_TENANT_ID)
.fullSearch(true)
.deprecatedFilter(DeprecatedFilter.ALL)
.widgetTypes(null).build(),
new PageLink(10, 0, searchText)
);
assertThat(widgetTypes.getData()).hasSize(0);

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

@ -43,6 +43,8 @@ import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
@ -311,6 +313,52 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
assertEquals(widgetsBundle3, widgetsBundles5.getData().get(0));
}
@Test
public void testFindAllWidgetsBundlesByTenantIdFullSearchScadaFirst() {
UUID tenantId1 = Uuids.timeBased();
UUID tenantId2 = Uuids.timeBased();
for (int i = 0; i < 10; i++) {
createWidgetBundles(5, tenantId1, "WB1_" + i + "_");
createWidgetBundles(2, tenantId1, "WB1_SCADA_" + i + "_", true);
createWidgetBundles(3, tenantId2, "WB2_" + i + "_");
createWidgetBundles(3, tenantId2, "WB2_SCADA_" + i + "_", true);
createSystemWidgetBundles(2, "WB_SYS_" + i + "_");
createSystemWidgetBundles(1, "WB_SYS_SCADA_" + i + "_", true);
}
widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted(Comparator.comparing(WidgetsBundle::getTitle)).collect(Collectors.toList());;
assertEquals(160, widgetsBundles.size());
PageLink pageLink = new PageLink(50, 0, "WB", new SortOrder("title"));
PageData<WidgetsBundle> widgetsBundles1 =
widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(
WidgetsBundleFilter.builder().tenantId(TenantId.fromUUID(tenantId1)).fullSearch(true).scadaFirst(true).build(), pageLink);
for (int i =0; i < 30; i++) {
var widgetsBundle = widgetsBundles1.getData().get(i);
assertTrue(widgetsBundle.isScada());
}
for (int i = 30; i < 50; i++) {
var widgetsBundle = widgetsBundles1.getData().get(i);
assertFalse(widgetsBundle.isScada());
}
pageLink = new PageLink(50, 0, "WB", new SortOrder("title"));
PageData<WidgetsBundle> widgetsBundles2 =
widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(
WidgetsBundleFilter.builder().tenantId(TenantId.fromUUID(tenantId2)).fullSearch(true).scadaFirst(true).build(), pageLink);
for (int i =0; i < 40; i++) {
var widgetsBundle = widgetsBundles2.getData().get(i);
assertTrue(widgetsBundle.isScada());
}
for (int i = 40; i < 50; i++) {
var widgetsBundle = widgetsBundles2.getData().get(i);
assertFalse(widgetsBundle.isScada());
}
}
@Test
public void testTagsSearchInFindAllWidgetsBundlesByTenantId() {
for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) {
@ -397,14 +445,22 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
}
private void createWidgetBundles(int count, UUID tenantId, String prefix) {
createWidgetBundles(count, tenantId, prefix, false);
}
private void createWidgetBundles(int count, UUID tenantId, String prefix, boolean scada) {
for (int i = 0; i < count; i++) {
createWidgetsBundle(TenantId.fromUUID(tenantId), prefix + i, prefix + i, null);
createWidgetsBundle(TenantId.fromUUID(tenantId), prefix + i, prefix + i, null, scada);
}
}
private void createSystemWidgetBundles(int count, String prefix) {
createSystemWidgetBundles(count, prefix, false);
}
private void createSystemWidgetBundles(int count, String prefix, boolean scada) {
for (int i = 0; i < count; i++) {
createWidgetsBundle(TenantId.SYS_TENANT_ID, prefix + i, prefix + i, null);
createWidgetsBundle(TenantId.SYS_TENANT_ID, prefix + i, prefix + i, null, scada);
}
}
@ -413,12 +469,17 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest {
}
private WidgetsBundle createWidgetsBundle(TenantId tenantId, String alias, String title, Integer order) {
return createWidgetsBundle(tenantId, alias, title, order, false);
}
private WidgetsBundle createWidgetsBundle(TenantId tenantId, String alias, String title, Integer order, boolean scada) {
WidgetsBundle widgetsBundle = new WidgetsBundle();
widgetsBundle.setAlias(alias);
widgetsBundle.setTitle(title);
widgetsBundle.setTenantId(tenantId);
widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased()));
widgetsBundle.setOrder(order);
widgetsBundle.setScada(scada);
return widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle);
}

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

@ -427,11 +427,11 @@ export class EntityService {
break;
case EntityType.WIDGETS_BUNDLE:
pageLink.sortOrder.property = 'title';
entitiesObservable = this.widgetService.getWidgetBundles(pageLink, false, true, config);
entitiesObservable = this.widgetService.getWidgetBundles(pageLink, false, true, false, config);
break;
case EntityType.WIDGET_TYPE:
pageLink.sortOrder.property = 'name';
entitiesObservable = this.widgetService.getWidgetTypes(pageLink, true, false, DeprecatedFilter.ALL, null, config);
entitiesObservable = this.widgetService.getWidgetTypes(pageLink, true, false, false, DeprecatedFilter.ALL, null, config);
break;
case EntityType.NOTIFICATION_TARGET:
pageLink.sortOrder.property = 'name';

11
ui-ngx/src/app/core/http/widget.service.ts

@ -86,9 +86,9 @@ export class WidgetService {
}
public getWidgetBundles(pageLink: PageLink, fullSearch = false,
tenantOnly = false, config?: RequestConfig): Observable<PageData<WidgetsBundle>> {
tenantOnly = false, scadaFirst = false, config?: RequestConfig): Observable<PageData<WidgetsBundle>> {
return this.http.get<PageData<WidgetsBundle>>(
`/api/widgetsBundles${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}`,
`/api/widgetsBundles${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}&scadaFirst=${scadaFirst}`,
defaultHttpOptionsFromConfig(config)
);
}
@ -240,10 +240,13 @@ export class WidgetService {
}
public getWidgetTypes(pageLink: PageLink, tenantOnly = false,
fullSearch = false, deprecatedFilter = DeprecatedFilter.ALL, widgetTypes: Array<widgetType> = null,
fullSearch = false, scadaFirst = false,
deprecatedFilter = DeprecatedFilter.ALL,
widgetTypes: Array<widgetType> = null,
config?: RequestConfig): Observable<PageData<WidgetTypeInfo>> {
let url =
`/api/widgetTypes${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}&deprecatedFilter=${deprecatedFilter}`;
`/api/widgetTypes${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}
&scadaFirst=${scadaFirst}&deprecatedFilter=${deprecatedFilter}`;
if (widgetTypes && widgetTypes.length) {
url += `&widgetTypeList=${widgetTypes.join(',')}`;
}

1
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html

@ -442,6 +442,7 @@
*ngIf="isAddingWidget"
[aliasController]="dashboardCtx.aliasController"
[filterWidgetTypes]="filterWidgetTypes"
[scadaFirst]="layouts.main.layoutCtx.gridSettings.layoutType === LayoutType.scada"
(widgetSelected)="addWidgetFromType($event)">
</tb-dashboard-widget-select>
</tb-details-panel>

2
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -166,6 +166,8 @@ import {
})
export class DashboardPageComponent extends PageComponent implements IDashboardController, HasDirtyFlag, OnInit, AfterViewInit, OnDestroy {
LayoutType = LayoutType;
private forcePristine = false;
get isDirty(): boolean {

10
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts

@ -33,6 +33,7 @@ import { PageLink } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order';
import { GridEntitiesFetchFunction, ScrollGridColumns } from '@shared/components/grid/scroll-grid-datasource';
import { ItemSizeStrategy } from '@shared/components/grid/scroll-grid.component';
import { coerceBoolean } from '@shared/decorators/coercion';
type selectWidgetMode = 'bundles' | 'allWidgets';
@ -69,6 +70,10 @@ export class DashboardWidgetSelectComponent implements OnInit {
@Input()
aliasController: IAliasController;
@Input()
@coerceBoolean()
scadaFirst = false;
@Input()
set search(search: string) {
this.searchSubject.next(search);
@ -170,7 +175,7 @@ export class DashboardWidgetSelectComponent implements OnInit {
property: 'title',
direction: Direction.ASC
});
return this.widgetsService.getWidgetBundles(pageLink, true);
return this.widgetsService.getWidgetBundles(pageLink, true, false, this.scadaFirst);
};
this.allWidgetsFetchFunction = (pageSize, page, filter) => {
@ -178,7 +183,8 @@ export class DashboardWidgetSelectComponent implements OnInit {
property: 'name',
direction: Direction.ASC
});
return this.widgetsService.getWidgetTypes(pageLink, false, true, filter.deprecatedFilter, filter.filter);
return this.widgetsService.getWidgetTypes(pageLink, false, true, this.scadaFirst,
filter.deprecatedFilter, filter.filter);
};
this.widgetsFetchFunction = (pageSize, page, filter) => {

1
ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.html

@ -23,6 +23,7 @@
<div class="tb-scada-symbol-content">
<div #scadaSymbolShape class="tb-scada-symbol-shape">
</div>
<div *ngIf="noScadaSymbol" class="tb-no-scada-symbol" translate>scada.no-symbol-selected</div>
</div>
<mat-progress-bar class="tb-action-widget-progress" style="height: 4px;" color="accent" mode="indeterminate" *ngIf="loading$ | async"></mat-progress-bar>
</div>

13
ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.scss

@ -46,5 +46,18 @@
align-items: center;
justify-content: center;
}
.tb-no-scada-symbol {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
color: rgba(0, 0, 0, 0.87);
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 16px;
}
}
}

8
ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts

@ -69,6 +69,7 @@ export class ScadaSymbolWidgetComponent implements OnInit, AfterViewInit, OnDest
loading$ = this.loadingSubject.asObservable().pipe(share());
scadaSymbolObject: ScadaSymbolObject;
noScadaSymbol = false;
constructor(public widgetComponent: WidgetComponent,
protected imagePipe: ImagePipe,
@ -89,9 +90,9 @@ export class ScadaSymbolWidgetComponent implements OnInit, AfterViewInit, OnDest
this.scadaSymbolContent$ = of(this.settings.scadaSymbolContent);
} else if (this.settings.scadaSymbolUrl) {
this.scadaSymbolContent$ = this.imageService.getImageString(this.settings.scadaSymbolUrl)
.pipe(catchError(() => of('<svg></svg>')));
.pipe(catchError(() => of('empty')));
} else {
this.scadaSymbolContent$ = of('<svg></svg>');
this.scadaSymbolContent$ = of('empty');
}
}
@ -133,6 +134,9 @@ export class ScadaSymbolWidgetComponent implements OnInit, AfterViewInit, OnDest
this.ctx.isPreview || (isDefinedAndNotNull(this.settings.simulated) ? this.settings.simulated : false);
if (content.startsWith('<parsererror')) {
rootElement.innerHTML = content;
} else if (content === 'empty') {
this.noScadaSymbol = true;
this.cd.markForCheck();
} else {
this.scadaSymbolObject = new ScadaSymbolObject(rootElement, this.ctx, content,
this.settings.scadaSymbolObjectSettings, this, simulated);

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

@ -96,6 +96,7 @@ export class WidgetComponentService {
widgetName: this.utils.editWidgetInfo.widgetName,
fullFqn: 'system.customWidget',
deprecated: false,
scada: false,
type: this.utils.editWidgetInfo.type,
sizeX: this.utils.editWidgetInfo.sizeX,
sizeY: this.utils.editWidgetInfo.sizeY,

5
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -531,6 +531,7 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri
widgetName: string;
fullFqn: string;
deprecated: boolean;
scada: boolean;
typeSettingsSchema?: string | any;
typeDataKeySettingsSchema?: string | any;
typeLatestDataKeySettingsSchema?: string | any;
@ -565,6 +566,7 @@ export const MissingWidgetType: WidgetInfo = {
widgetName: 'Widget type not found',
fullFqn: 'undefined',
deprecated: false,
scada: false,
sizeX: 8,
sizeY: 6,
resources: [],
@ -590,6 +592,7 @@ export const ErrorWidgetType: WidgetInfo = {
widgetName: 'Error loading widget',
fullFqn: 'error',
deprecated: false,
scada: false,
sizeX: 8,
sizeY: 6,
resources: [],
@ -632,6 +635,7 @@ export const toWidgetInfo = (widgetTypeEntity: WidgetType): WidgetInfo => ({
widgetName: widgetTypeEntity.name,
fullFqn: fullWidgetTypeFqn(widgetTypeEntity),
deprecated: widgetTypeEntity.deprecated,
scada: widgetTypeEntity.scada,
type: widgetTypeEntity.descriptor.type,
sizeX: widgetTypeEntity.descriptor.sizeX,
sizeY: widgetTypeEntity.descriptor.sizeY,
@ -685,6 +689,7 @@ export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId:
fqn: widgetTypeFqn(widgetInfo.fullFqn),
name: widgetInfo.widgetName,
deprecated: widgetInfo.deprecated,
scada: widgetInfo.scada,
descriptor
};
};

17
ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html

@ -257,11 +257,18 @@
[(ngModel)]="widget.tags"
(ngModelChange)="isDirty = true">
</tb-string-items-list>
<mat-slide-toggle class="mat-block" style="padding-bottom: 16px;"
[(ngModel)]="widget.deprecated"
(ngModelChange)="isDirty = true">
{{ 'widget.deprecated' | translate }}
</mat-slide-toggle>
<div fxLayout="column" fxLayoutGap="16px" style="padding-bottom: 16px;">
<mat-slide-toggle class="mat-block"
[(ngModel)]="widget.scada"
(ngModelChange)="isDirty = true">
{{ 'widget.scada' | translate }}
</mat-slide-toggle>
<mat-slide-toggle class="mat-block"
[(ngModel)]="widget.deprecated"
(ngModelChange)="isDirty = true">
{{ 'widget.deprecated' | translate }}
</mat-slide-toggle>
</div>
<mat-form-field class="mat-block">
<mat-label translate>widget.settings-form-selector</mat-label>
<input matInput

11
ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html

@ -67,9 +67,14 @@
label="{{ 'widget.tags' | translate }}"
formControlName="tags">
</tb-string-items-list>
<mat-slide-toggle formControlName="deprecated">
{{ 'widget.deprecated' | translate }}
</mat-slide-toggle>
<div fxLayout="column" fxLayoutGap="16px">
<mat-slide-toggle formControlName="scada">
{{ 'widget.scada' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="deprecated">
{{ 'widget.deprecated' | translate }}
</mat-slide-toggle>
</div>
</fieldset>
</form>
</div>

2
ui-ngx/src/app/modules/home/pages/widget/widget-type.component.ts

@ -53,6 +53,7 @@ export class WidgetTypeComponent extends EntityComponent<WidgetTypeDetails> {
image: [entity ? entity.image : ''],
description: [entity ? entity.description : '', Validators.maxLength(1024)],
tags: [entity ? entity.tags : []],
scada: [entity ? entity.scada : false],
deprecated: [entity ? entity.deprecated : false]
}
);
@ -64,6 +65,7 @@ export class WidgetTypeComponent extends EntityComponent<WidgetTypeDetails> {
image: entity.image,
description: entity.description,
tags: entity.tags,
scada: entity.scada,
deprecated: entity.deprecated
});
}

3
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html

@ -63,6 +63,9 @@
<textarea matInput formControlName="description" rows="2" maxlength="1024" #descriptionInput></textarea>
<mat-hint align="end">{{descriptionInput.value?.length || 0}}/1024</mat-hint>
</mat-form-field>
<mat-slide-toggle formControlName="scada" style="padding-bottom: 16px;">
{{ 'widgets-bundle.scada' | translate }}
</mat-slide-toggle>
<mat-form-field class="mat-block">
<mat-label translate>widgets-bundle.order</mat-label>
<input matInput type="number" step="1" formControlName="order">

2
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts

@ -51,6 +51,7 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
image: [entity ? entity.image : ''],
description: [entity ? entity.description : '', Validators.maxLength(1024)],
scada: [entity ? entity.scada : false],
order: [entity ? entity.order : null]
}
);
@ -61,6 +62,7 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
title: entity.title,
image: entity.image,
description: entity.description,
scada: entity.scada,
order: entity.order
});
}

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

@ -204,6 +204,7 @@ export interface BaseWidgetType extends BaseData<WidgetTypeId>, HasTenantId {
fqn: string;
name: string;
deprecated: boolean;
scada: boolean;
}
export const fullWidgetTypeFqn = (type: BaseWidgetType): string =>
@ -361,7 +362,7 @@ export interface DataKey extends KeyInfo {
_hash?: number;
}
export type CellClickColumnInfo = Pick<DataKey, 'name' | 'label'>
export type CellClickColumnInfo = Pick<DataKey, 'name' | 'label'>;
export enum DataKeyConfigMode {
general = 'general',

1
ui-ngx/src/app/shared/models/widgets-bundle.model.ts

@ -24,6 +24,7 @@ export interface WidgetsBundle extends BaseData<WidgetsBundleId>, HasTenantId, E
alias: string;
title: string;
image: string;
scada: boolean;
description: string;
order: number;
}

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

@ -5493,6 +5493,7 @@
"latest-data-key-settings-form-selector": "Latest data key settings form selector",
"all": "All",
"actual": "Actual",
"scada": "SCADA symbol",
"deprecated": "Deprecated",
"has-basic-mode": "Has basic mode",
"basic-mode-form-selector": "Basic mode form selector",
@ -5601,6 +5602,7 @@
"title-max-length": "Title should be less than 256",
"description": "Description",
"image-preview": "Image preview",
"scada": "SCADA widgets bundle",
"order": "Order",
"add-widgets-bundle-text": "Add new widgets bundle",
"no-widgets-bundles-text": "No widgets bundles found",

Loading…
Cancel
Save