From c3e8c62ffcc51662f91e67f736e76f537a8270f2 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 23 Jan 2024 12:36:20 +0100 Subject: [PATCH 001/107] RPC deleteOutdatedRpcByTenantId simple native query. the test added --- .../service/ttl/rpc/RpcCleanUpService.java | 2 +- .../thingsboard/server/dao/rpc/RpcDao.java | 3 +- .../server/dao/sql/rpc/JpaRpcDao.java | 4 +- .../server/dao/sql/rpc/RpcRepository.java | 6 +- .../server/dao/sql/rpc/JpaRpcDaoTest.java | 59 +++++++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java index c8683972d9..57ba35628c 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/rpc/RpcCleanUpService.java @@ -68,7 +68,7 @@ public class RpcCleanUpService { long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getRpcTtlDays()); long expirationTime = System.currentTimeMillis() - ttl; - long totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); + int totalRemoved = rpcDao.deleteOutdatedRpcByTenantId(tenantId, expirationTime); if (totalRemoved > 0) { log.info("Removed {} outdated rpc(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java index 79b73c69aa..d2e867e763 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java @@ -30,5 +30,6 @@ public interface RpcDao extends Dao { PageData findAllRpcByTenantId(TenantId tenantId, PageLink pageLink); - Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); + int deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java index 2c16db4d34..e1939a595c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -67,8 +68,9 @@ public class JpaRpcDao extends JpaAbstractDao implements RpcDao return DaoUtil.toPageData(rpcRepository.findAllByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink))); } + @Transactional @Override - public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { + public int deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java index e04d000a65..fe7c95d249 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.rpc; 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.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.rpc.RpcStatus; @@ -32,7 +33,8 @@ public interface RpcRepository extends JpaRepository { Page findAllByTenantId(UUID tenantId, Pageable pageable); - @Query(value = "WITH deleted AS (DELETE FROM rpc WHERE (tenant_id = :tenantId AND created_time < :expirationTime) IS TRUE RETURNING *) SELECT count(*) FROM deleted", + @Modifying + @Query(value = "DELETE FROM rpc WHERE tenant_id = :tenantId AND created_time < :expirationTime", nativeQuery = true) - Long deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); + int deleteOutdatedRpcByTenantId(@Param("tenantId") UUID tenantId, @Param("expirationTime") Long expirationTime); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java new file mode 100644 index 0000000000..d2889dfb3c --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDaoTest.java @@ -0,0 +1,59 @@ +/** + * 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.dao.sql.rpc; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rpc.Rpc; +import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.dao.AbstractJpaDaoTest; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JpaRpcDaoTest extends AbstractJpaDaoTest { + + @Autowired + JpaRpcDao rpcDao; + + @Test + public void deleteOutdated() { + Rpc rpc = new Rpc(); + rpc.setTenantId(TenantId.SYS_TENANT_ID); + rpc.setDeviceId(new DeviceId(UUID.randomUUID())); + rpc.setStatus(RpcStatus.QUEUED); + rpc.setRequest(JacksonUtil.toJsonNode("{}")); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + rpc.setId(null); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("3d193a7a-774b-4c05-84d5-f7fdcf7a37cf")); + rpc.setId(null); + rpc.setTenantId(tenantId); + rpc.setDeviceId(new DeviceId(UUID.randomUUID())); + rpcDao.saveAndFlush(rpc.getTenantId(), rpc); + + assertThat(rpcDao.deleteOutdatedRpcByTenantId(TenantId.SYS_TENANT_ID, 0L)).isEqualTo(0); + assertThat(rpcDao.deleteOutdatedRpcByTenantId(TenantId.SYS_TENANT_ID, Long.MAX_VALUE)).isEqualTo(2); + assertThat(rpcDao.deleteOutdatedRpcByTenantId(tenantId, System.currentTimeMillis() + 1)).isEqualTo(1); + } + +} From 053bcc0abe9b7798111548ea414ba850f2f8a78c Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 23 Jan 2024 12:36:40 +0100 Subject: [PATCH 002/107] Log Hibernate SQL queries in the dao test scope (commented) --- dao/src/test/resources/logback.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dao/src/test/resources/logback.xml b/dao/src/test/resources/logback.xml index 61397ec6f1..5e293b2982 100644 --- a/dao/src/test/resources/logback.xml +++ b/dao/src/test/resources/logback.xml @@ -10,6 +10,9 @@ + + + From 42238950817216d10aff688e3be2807b569e6323 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 7 Feb 2024 11:31:52 +0200 Subject: [PATCH 003/107] migrated assets used for storing queue statistics to queue_stats table --- .../main/data/upgrade/3.6.2/schema_update.sql | 20 +++++ .../server/controller/BaseController.java | 13 ++++ .../processor/asset/AssetEdgeProcessor.java | 3 +- .../entitiy/asset/DefaultTbAssetService.java | 14 ---- .../profile/DefaultTbAssetProfileService.java | 11 --- .../service/security/permission/Resource.java | 3 +- .../DefaultRuleEngineStatisticsService.java | 47 ++++++----- .../controller/AssetControllerTest.java | 3 - .../controller/BaseQueueControllerTest.java | 20 ++--- .../server/dao/queue/QueueStatsService.java | 35 +++++++++ .../server/common/data/EntityType.java | 3 +- .../server/common/data/id/QueueStatsId.java | 43 +++++++++++ .../server/common/data/queue/QueueStats.java | 37 +++++++++ .../server/dao/model/ModelConstants.java | 8 ++ .../dao/model/sql/QueueStatsEntity.java | 69 +++++++++++++++++ .../dao/queue/BaseQueueStatsService.java | 77 +++++++++++++++++++ .../server/dao/queue/QueueStatsDao.java | 30 ++++++++ .../service/validator/AssetDataValidator.java | 4 +- .../server/dao/sql/asset/AssetRepository.java | 2 +- .../server/dao/sql/asset/JpaAssetDao.java | 3 +- .../query/DefaultEntityQueryRepository.java | 2 + .../dao/sql/queue/JpaQueueStatsDao.java | 61 +++++++++++++++ .../dao/sql/queue/QueueStatsRepository.java | 30 ++++++++ .../main/resources/sql/schema-entities.sql | 9 +++ 24 files changed, 475 insertions(+), 72 deletions(-) create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java diff --git a/application/src/main/data/upgrade/3.6.2/schema_update.sql b/application/src/main/data/upgrade/3.6.2/schema_update.sql index 6ae5e45134..1be58e4408 100644 --- a/application/src/main/data/upgrade/3.6.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.2/schema_update.sql @@ -28,3 +28,23 @@ ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS queue_name varchar(255); ALTER TABLE component_descriptor ADD COLUMN IF NOT EXISTS has_queue_name boolean DEFAULT false; -- RULE NODE QUEUE UPDATE END + +-- QUEUE STATS UPDATE START + +CREATE TABLE IF NOT EXISTS queue_stats ( + id uuid NOT NULL CONSTRAINT queue_stats_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + queue_name varchar(255) NOT NULL, + service_id varchar(255) NOT NULL, + CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id)); + +INSERT INTO queue_stats + SELECT id, created_time, tenant_id, split_part(name, '_', 1) AS queue_name, split_part(name, '_', 2) AS service_id + FROM asset + WHERE type = 'TbServiceQueue'; + +DELETE FROM asset WHERE type='TbServiceQueue'; +DELETE FROM asset_profile WHERE name ='TbServiceQueue'; + +-- QUEUE STATS UPDATE END \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index c40760dda3..5f406eed29 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -78,6 +78,7 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -96,6 +97,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.query.EntityDataSortOrder; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -123,6 +125,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; @@ -307,6 +310,9 @@ public abstract class BaseController { @Autowired protected QueueService queueService; + @Autowired + protected QueueStatsService queueStatsService; + @Autowired protected EntitiesVersionControlService vcService; @@ -600,6 +606,9 @@ public abstract class BaseController { case QUEUE: checkQueueId(new QueueId(entityId.getId()), operation); return; + case QUEUE_STATS: + checkQueueStatsId(new QueueStatsId(entityId.getId()), operation); + return; default: checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); } @@ -776,6 +785,10 @@ public abstract class BaseController { return queue; } + protected QueueStats checkQueueStatsId(QueueStatsId queueStatsId, Operation operation) throws ThingsboardException { + return checkEntityId(queueStatsId, queueStatsService::findQueueStatsById, operation); + } + protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 203c6466e0..d8122e1b6c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.dao.asset.BaseAssetService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; @@ -115,7 +114,7 @@ public abstract class AssetEdgeProcessor extends BaseAssetProcessor implements A case ASSIGNED_TO_CUSTOMER: case UNASSIGNED_FROM_CUSTOMER: Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId); - if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { + if (asset != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor) assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetUpdatedMsg(msgType, asset); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java index 58940318be..94d28b50d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java @@ -22,10 +22,8 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; @@ -34,30 +32,18 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import org.thingsboard.server.service.profile.TbAssetProfileCache; - -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; @Service @AllArgsConstructor public class DefaultTbAssetService extends AbstractTbEntityService implements TbAssetService { private final AssetService assetService; - private final TbAssetProfileCache assetProfileCache; @Override public Asset save(Asset asset, User user) throws Exception { ActionType actionType = asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = asset.getTenantId(); try { - if (TB_SERVICE_QUEUE.equals(asset.getType())) { - throw new ThingsboardException("Unable to save asset with type " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (asset.getAssetProfileId() != null) { - AssetProfile assetProfile = assetProfileCache.get(tenantId, asset.getAssetProfileId()); - if (assetProfile != null && TB_SERVICE_QUEUE.equals(assetProfile.getName())) { - throw new ThingsboardException("Unable to save asset with profile " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); autoCommit(user, savedAsset.getId()); notificationEntityService.logEntityAction(tenantId, savedAsset.getId(), savedAsset, asset.getCustomerId(), diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java index 832a9a3003..e5884b5eb5 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.TenantId; @@ -31,8 +30,6 @@ import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; - @Service @TbCoreComponent @AllArgsConstructor @@ -46,14 +43,6 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem ActionType actionType = assetProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = assetProfile.getTenantId(); try { - if (TB_SERVICE_QUEUE.equals(assetProfile.getName())) { - throw new ThingsboardException("Unable to save asset profile with name " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } else if (assetProfile.getId() != null) { - AssetProfile foundAssetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfile.getId()); - if (foundAssetProfile != null && TB_SERVICE_QUEUE.equals(foundAssetProfile.getName())) { - throw new ThingsboardException("Updating asset profile with name " + TB_SERVICE_QUEUE + " is prohibited!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); - } - } AssetProfile savedAssetProfile = checkNotNull(assetProfileService.saveAssetProfile(assetProfile)); autoCommit(user, savedAssetProfile.getId()); tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAssetProfile.getId(), diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 5aa869f2a8..770745f738 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -46,7 +46,8 @@ public enum Resource { QUEUE(EntityType.QUEUE), VERSION_CONTROL, NOTIFICATION(EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_TEMPLATE, - EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE); + EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE), + QUEUE_STATS(EntityType.QUEUE_STATS); private final Set entityTypes; diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index ff69746bcc..fca9fce81a 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -21,15 +21,15 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbRuleEngineComponent; @@ -52,7 +52,6 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService { - public static final String TB_SERVICE_QUEUE = "TbServiceQueue"; public static final String RULE_ENGINE_EXCEPTION = "ruleEngineException"; public static final FutureCallback CALLBACK = new FutureCallback() { @Override @@ -68,10 +67,10 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS private final TbServiceInfoProvider serviceInfoProvider; private final TelemetrySubscriptionService tsService; - private final AssetService assetService; + private final QueueStatsService queueStatsService; private final ApiLimitService apiLimitService; private final Lock lock = new ReentrantLock(); - private final ConcurrentMap tenantQueueAssets = new ConcurrentHashMap<>(); + private final ConcurrentMap tenantQueueStats = new ConcurrentHashMap<>(); @Value("${queue.rule-engine.stats.max-error-message-length:4096}") private int maxErrorMessageLength; @@ -82,7 +81,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS ruleEngineStats.getTenantStats().forEach((id, stats) -> { try { TenantId tenantId = TenantId.fromUUID(id); - AssetId serviceAssetId = getServiceAssetId(tenantId, queueName); + QueueStatsId queueStatsId = getQueueStatsId(tenantId, queueName); if (stats.getTotalMsgCounter().get() > 0) { List tsList = stats.getCounters().entrySet().stream() .map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get()))) @@ -90,7 +89,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS if (!tsList.isEmpty()) { long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, serviceAssetId, tsList, ttl, CALLBACK); + tsService.saveAndNotifyInternal(tenantId, queueStatsId, tsList, ttl, CALLBACK); } } } catch (Exception e) { @@ -104,7 +103,7 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); ttl = TimeUnit.DAYS.toSeconds(ttl); - tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); + tsService.saveAndNotifyInternal(tenantId, getQueueStatsId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); } catch (Exception e2) { if (!"Asset is referencing to non-existent tenant!".equalsIgnoreCase(e2.getMessage())) { log.debug("[{}] Failed to store the statistics", tenantId, e2); @@ -113,30 +112,30 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS }); } - private AssetId getServiceAssetId(TenantId tenantId, String queueName) { + private QueueStatsId getQueueStatsId(TenantId tenantId, String queueName) { TenantQueueKey key = new TenantQueueKey(tenantId, queueName); - AssetId assetId = tenantQueueAssets.get(key); - if (assetId == null) { + QueueStatsId queueStatsId = tenantQueueStats.get(key); + if (queueStatsId == null) { lock.lock(); try { - assetId = tenantQueueAssets.get(key); - if (assetId == null) { - Asset asset = assetService.findAssetByTenantIdAndName(tenantId, queueName + "_" + serviceInfoProvider.getServiceId()); - if (asset == null) { - asset = new Asset(); - asset.setTenantId(tenantId); - asset.setName(queueName + "_" + serviceInfoProvider.getServiceId()); - asset.setType(TB_SERVICE_QUEUE); - asset = assetService.saveAsset(asset); + queueStatsId = tenantQueueStats.get(key); + if (queueStatsId == null) { + QueueStats queueStats = queueStatsService.findByTenantIdAndNameAndServiceId(tenantId, queueName , serviceInfoProvider.getServiceId()); + if (queueStats == null) { + queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(queueName); + queueStats.setServiceId(serviceInfoProvider.getServiceId()); + queueStats = queueStatsService.save(tenantId, queueStats); } - assetId = asset.getId(); - tenantQueueAssets.put(key, assetId); + queueStatsId = queueStats.getId(); + tenantQueueStats.put(key, queueStatsId); } } finally { lock.unlock(); } } - return assetId; + return queueStatsId; } @Data diff --git a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java index b41c8c3e78..a3e1e6cccf 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java @@ -50,7 +50,6 @@ import org.thingsboard.server.dao.asset.AssetDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService; import java.util.ArrayList; import java.util.List; @@ -567,8 +566,6 @@ public class AssetControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, cntEntity, cntEntity, cntEntity); - loadedAssets.removeIf(asset -> asset.getType().equals(DefaultRuleEngineStatisticsService.TB_SERVICE_QUEUE)); - assets.sort(idComparator); loadedAssets.sort(idComparator); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java index 772a3c5ecb..87688a797f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java @@ -25,7 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -34,12 +33,13 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.stats.StatsFactory; -import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.timeseries.TimeseriesDao; import org.thingsboard.server.gen.transport.TransportProtos; @@ -50,6 +50,7 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingRes import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService; import org.thingsboard.server.service.stats.RuleEngineStatisticsService; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -66,7 +67,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; @DaoSqlTest @TestPropertySource(properties = { @@ -81,7 +81,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest { @SpyBean private TimeseriesDao timeseriesDao; @Autowired - private AssetService assetService; + private QueueStatsService queueStatsService; @Test public void testQueueWithServiceTypeRE() throws Exception { @@ -176,16 +176,16 @@ public class BaseQueueControllerTest extends AbstractControllerTest { }); ruleEngineStatisticsService.reportQueueStats(System.currentTimeMillis(), testStats); - Asset serviceAsset = assetService.findAssetsByTenantIdAndType(tenantId, TB_SERVICE_QUEUE, new PageLink(100)).getData() - .stream().filter(asset -> asset.getName().startsWith(queue.getName())) - .findFirst().get(); + List queueStatsList = queueStatsService.findByTenantId(tenantId); + assertThat(queueStatsList).hasSize(1); + QueueStats queueStats = queueStatsList.get(0); ArgumentCaptor ttlCaptor = ArgumentCaptor.forClass(Long.class); - verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.SUCCESSFUL_MSGS) && tsKvEntry.getLongValue().get().equals(5L); }), ttlCaptor.capture()); - verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.FAILED_MSGS) && tsKvEntry.getLongValue().get().equals(5L); }), ttlCaptor.capture()); @@ -193,7 +193,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest { assertThat(usedTtl).isEqualTo(TimeUnit.DAYS.toSeconds(queueStatsTtlDays)); }); - verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { return tsKvEntry.getKey().equals(DefaultRuleEngineStatisticsService.RULE_ENGINE_EXCEPTION) && tsKvEntry.getJsonValue().get().equals(ruleEngineException.toJsonString(0)); }), ttlCaptor.capture()); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java new file mode 100644 index 0000000000..e7166c4fd6 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java @@ -0,0 +1,35 @@ +/** + * 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.dao.queue; + +import org.thingsboard.server.common.data.id.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.entity.EntityDaoService; + +import java.util.List; + +public interface QueueStatsService extends EntityDaoService { + + QueueStats save(TenantId tenantId, QueueStats queueStats); + + QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId); + + QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueStatsName, String serviceId); + + List findByTenantId(TenantId tenantId); + +} \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index fb4fe1011e..bb43bf4d11 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -58,7 +58,8 @@ public enum EntityType { NOTIFICATION_TEMPLATE (30), NOTIFICATION_REQUEST (31), NOTIFICATION (32), - NOTIFICATION_RULE (33); + NOTIFICATION_RULE (33), + QUEUE_STATS(34); @Getter private final int protoNumber; // Corresponds to EntityTypeProto diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java new file mode 100644 index 0000000000..04629007a9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/QueueStatsId.java @@ -0,0 +1,43 @@ +/** + * 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModelProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class QueueStatsId extends UUIDBased implements EntityId { + + private static final long serialVersionUID = 1L; + + @JsonCreator + public QueueStatsId(@JsonProperty("id") UUID id) { + super(id); + } + + public static QueueStatsId fromString(String queueId) { + return new QueueStatsId(UUID.fromString(queueId)); + } + + @ApiModelProperty(position = 2, required = true, value = "string", example = "QUEUE_STATS", allowableValues = "QUEUE_STATS") + @Override + public EntityType getEntityType() { + return EntityType.QUEUE_STATS; + } +} \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java new file mode 100644 index 0000000000..b6224d4167 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java @@ -0,0 +1,37 @@ +/** + * 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.queue; + +import lombok.Data; +import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.QueueStatsId; + +@Data +public class QueueStats extends BaseDataWithAdditionalInfo implements HasTenantId { + private TenantId tenantId; + private String queueName; + private String serviceId; + + public QueueStats() { + } + + public QueueStats(QueueStatsId id) { + super(id); + } + +} 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 2fe56f5d1f..166bdbc0ee 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 @@ -610,6 +610,14 @@ public class ModelConstants { public static final String QUEUE_TABLE_NAME = "queue"; public static final String QUEUE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; + /** + * Tenant queue stats constants. + */ + public static final String QUEUE_STATS_TABLE_NAME = "queue_stats"; + public static final String QUEUE_STATS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String QUEUE_STATS_QUEUE_NAME_PROPERTY = "queue_name"; + public static final String QUEUE_STATS_SERVICE_ID_PROPERTY = "service_id"; + /** * Notification constants */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java new file mode 100644 index 0000000000..1cdbd2bc5e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java @@ -0,0 +1,69 @@ +/** + * 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.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.QUEUE_STATS_TABLE_NAME) +public class QueueStatsEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.QUEUE_STATS_TENANT_ID_PROPERTY) + private UUID tenantId; + + @Column(name = ModelConstants.QUEUE_STATS_QUEUE_NAME_PROPERTY) + private String queueName; + + @Column(name = ModelConstants.QUEUE_STATS_SERVICE_ID_PROPERTY) + private String serviceId; + + public QueueStatsEntity() { + } + + public QueueStatsEntity(QueueStats queueStats) { + if (queueStats.getId() != null) { + this.setId(queueStats.getId().getId()); + } + this.setCreatedTime(queueStats.getCreatedTime()); + this.tenantId = DaoUtil.getId(queueStats.getTenantId()); + this.queueName = queueStats.getQueueName(); + this.serviceId = queueStats.getServiceId(); + } + + @Override + public QueueStats toData() { + QueueStats queueStats = new QueueStats(new QueueStatsId(getUuid())); + queueStats.setCreatedTime(createdTime); + queueStats.setTenantId(new TenantId(tenantId)); + queueStats.setQueueName(queueName); + queueStats.setServiceId(serviceId); + return queueStats; + } +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java new file mode 100644 index 0000000000..b58d4a162f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java @@ -0,0 +1,77 @@ +/** + * 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.dao.queue; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +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.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.entity.AbstractEntityService; + +import java.util.List; +import java.util.Optional; + +import static org.thingsboard.server.dao.service.Validator.validateId; + +@Service("QueueStatsDaoService") +@Slf4j +@RequiredArgsConstructor +public class BaseQueueStatsService extends AbstractEntityService implements QueueStatsService { + + @Autowired + private QueueStatsDao queueStatsDao; + + @Override + public QueueStats save(TenantId tenantId, QueueStats queueStats) { + return queueStatsDao.save(tenantId, queueStats); + } + + @Override + public QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId) { + log.trace("Executing findQueueStatsById [{}]", queueStatsId); + validateId(queueStatsId, "Incorrect queueStatsId " + queueStatsId); + return queueStatsDao.findById(tenantId, queueStatsId.getId()); + } + + @Override + public QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId) { + log.trace("Executing findByTenantIdAndNameAndServiceId, tenantId: [{}], queueName: [{}], serviceId: [{}]", tenantId, queueName, serviceId); + return queueStatsDao.findByTenantIdQueueNameAndServiceId(tenantId, queueName, serviceId); + } + + @Override + public List findByTenantId(TenantId tenantId) { + log.trace("Executing findByTenantId, tenantId: [{}]", tenantId); + return queueStatsDao.findByTenantId(tenantId); + } + + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findQueueStatsById(tenantId, new QueueStatsId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return null; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java new file mode 100644 index 0000000000..fc83891036 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java @@ -0,0 +1,30 @@ +/** + * 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.dao.queue; + +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.Dao; + +import java.util.List; + +public interface QueueStatsDao extends Dao { + + QueueStats findByTenantIdQueueNameAndServiceId(TenantId tenantId, String name, String serviceId); + + List findByTenantId(TenantId tenantId); + +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java index 5ef0b7a2ba..2d0cdbd248 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java @@ -47,9 +47,7 @@ public class AssetDataValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, Asset asset) { - if (!BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { - validateNumberOfEntitiesPerTenant(tenantId, EntityType.ASSET); - } + validateNumberOfEntitiesPerTenant(tenantId, EntityType.ASSET); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index bbe212fb0b..f65e0db1c6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -189,7 +189,7 @@ public interface AssetRepository extends JpaRepository, Expor @Param("searchText") String searchText, Pageable pageable); - Long countByTenantIdAndTypeIsNot(UUID tenantId, String type); + Long countByTenantId(UUID tenantId); @Query("SELECT externalId FROM AssetEntity WHERE id = :id") UUID getExternalIdById(@Param("id") UUID id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index b77559be7b..0ef4370f30 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -43,7 +43,6 @@ import java.util.Optional; import java.util.UUID; import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityInfosToDto; -import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; /** * Created by Valerii Sosliuk on 5/19/2017. @@ -244,7 +243,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A @Override public Long countByTenantId(TenantId tenantId) { - return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE); + return assetRepository.countByTenantId(tenantId.getId()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index d2b2567bd7..896fde6644 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -244,6 +244,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityTableMap.put(EntityType.DEVICE_PROFILE, "device_profile"); entityTableMap.put(EntityType.ASSET_PROFILE, "asset_profile"); entityTableMap.put(EntityType.TENANT_PROFILE, "tenant_profile"); + entityTableMap.put(EntityType.QUEUE_STATS, "queue_stats"); entityNameColumns.put(EntityType.DEVICE, "name"); entityNameColumns.put(EntityType.CUSTOMER, "title"); @@ -262,6 +263,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityNameColumns.put(EntityType.TB_RESOURCE, "search_text"); entityNameColumns.put(EntityType.EDGE, "name"); entityNameColumns.put(EntityType.QUEUE, "name"); + entityNameColumns.put(EntityType.QUEUE_STATS, "queue_name"); } public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java new file mode 100644 index 0000000000..29853ce611 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java @@ -0,0 +1,61 @@ +/** + * 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.dao.sql.queue; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.QueueStatsEntity; +import org.thingsboard.server.dao.queue.QueueStatsDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; +import java.util.UUID; + +@Slf4j +@Component +@SqlDao +public class JpaQueueStatsDao extends JpaAbstractDao implements QueueStatsDao { + + @Autowired + private QueueStatsRepository queueStatsRepository; + + @Override + protected Class getEntityClass() { + return QueueStatsEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return queueStatsRepository; + } + + @Override + public QueueStats findByTenantIdQueueNameAndServiceId(TenantId tenantId, String name, String serviceId) { + return DaoUtil.getData(queueStatsRepository.findByTenantIdAndQueueNameAndServiceId(tenantId.getId(), name, serviceId)); + } + + @Override + public List findByTenantId(TenantId tenantId) { + return DaoUtil.convertDataList(queueStatsRepository.findByTenantId(tenantId.getId())); + } + +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java new file mode 100644 index 0000000000..1fffd0913a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueStatsRepository.java @@ -0,0 +1,30 @@ +/** + * 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.dao.sql.queue; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.thingsboard.server.dao.model.sql.QueueStatsEntity; + +import java.util.List; +import java.util.UUID; + +public interface QueueStatsRepository extends JpaRepository { + + QueueStatsEntity findByTenantIdAndQueueNameAndServiceId(UUID tenantId, String name, String serviceId); + + List findByTenantId(UUID tenantId); + +} \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 2352ea2eb3..dab74ec1ab 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -884,3 +884,12 @@ CREATE TABLE IF NOT EXISTS alarm_types ( CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type), CONSTRAINT fk_entity_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenant(id) ON DELETE CASCADE ); + +CREATE TABLE IF NOT EXISTS queue_stats ( + id uuid NOT NULL CONSTRAINT queue_stats_pkey PRIMARY KEY, + created_time bigint NOT NULL, + tenant_id uuid NOT NULL, + queue_name varchar(255) NOT NULL, + service_id varchar(255) NOT NULL, + CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id) +); \ No newline at end of file From c0f9ef8a6dc6686139cf246153ddd5e138d72125 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 9 Feb 2024 15:09:00 +0200 Subject: [PATCH 004/107] new entity for queue statistics: added cleanup for tenant, added tests, updated UI --- .../dashboards/rule_engine_statistics.json | 17 +-- .../main/data/upgrade/3.6.2/schema_update.sql | 5 +- .../server/controller/BaseController.java | 13 -- .../service/security/permission/Resource.java | 3 +- .../controller/BaseQueueControllerTest.java | 1 + .../controller/EntityQueryControllerTest.java | 47 ++++++ .../server/dao/queue/QueueStatsService.java | 4 +- .../common/data/id/EntityIdFactory.java | 2 + .../server/common/data/queue/QueueStats.java | 6 +- common/proto/src/main/proto/queue.proto | 1 + .../server/dao/asset/BaseAssetService.java | 1 - .../dao/queue/BaseQueueStatsService.java | 21 ++- .../server/dao/queue/QueueStatsDao.java | 4 +- .../validator/QueueStatsDataValidator.java | 40 ++++++ .../dao/sql/query/EntityKeyMapping.java | 5 + .../dao/sql/queue/JpaQueueStatsDao.java | 9 +- .../dao/sql/queue/QueueStatsRepository.java | 11 +- .../server/dao/tenant/TenantServiceImpl.java | 6 + .../dao/service/QueueStatsServiceTest.java | 134 ++++++++++++++++++ ui-ngx/src/assets/dashboard/api_usage.json | 17 +-- 20 files changed, 298 insertions(+), 49 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/QueueStatsServiceTest.java diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json index c84faa359e..2a079fcd9d 100644 --- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json +++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json @@ -105,7 +105,7 @@ "_hash": 0.49891007198715376 } ], - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018" + "entityAliasId": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba" } ], "timewindow": { @@ -228,7 +228,7 @@ "_hash": 0.7255162989552142 } ], - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018" + "entityAliasId": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba" } ], "timewindow": { @@ -341,7 +341,7 @@ "_hash": 0.2679547062508352 } ], - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018" + "entityAliasId": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba" } ], "timewindow": { @@ -463,16 +463,13 @@ } }, "entityAliases": { - "140f23dd-e3a0-ed98-6189-03c49d2d8018": { - "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018", + "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba": { + "id": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba", "alias": "TbServiceQueues", "filter": { - "type": "assetType", + "type": "entityType", "resolveMultiple": true, - "assetNameFilter": "", - "assetTypes": [ - "TbServiceQueue" - ] + "entityType": "QUEUE_STATS" } } }, diff --git a/application/src/main/data/upgrade/3.6.2/schema_update.sql b/application/src/main/data/upgrade/3.6.2/schema_update.sql index 1be58e4408..6e0cfd7d95 100644 --- a/application/src/main/data/upgrade/3.6.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.6.2/schema_update.sql @@ -40,9 +40,10 @@ CREATE TABLE IF NOT EXISTS queue_stats ( CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id)); INSERT INTO queue_stats - SELECT id, created_time, tenant_id, split_part(name, '_', 1) AS queue_name, split_part(name, '_', 2) AS service_id + SELECT id, created_time, tenant_id, substring(name FROM 1 FOR position('_' IN name) - 1) AS queue_name, + substring(name FROM position('_' IN name) + 1) AS service_id FROM asset - WHERE type = 'TbServiceQueue'; + WHERE type = 'TbServiceQueue' and name LIKE '%\_%'; DELETE FROM asset WHERE type='TbServiceQueue'; DELETE FROM asset_profile WHERE name ='TbServiceQueue'; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 5f406eed29..c40760dda3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -78,7 +78,6 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; -import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -97,7 +96,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.query.EntityDataSortOrder; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -125,7 +123,6 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; -import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; @@ -310,9 +307,6 @@ public abstract class BaseController { @Autowired protected QueueService queueService; - @Autowired - protected QueueStatsService queueStatsService; - @Autowired protected EntitiesVersionControlService vcService; @@ -606,9 +600,6 @@ public abstract class BaseController { case QUEUE: checkQueueId(new QueueId(entityId.getId()), operation); return; - case QUEUE_STATS: - checkQueueStatsId(new QueueStatsId(entityId.getId()), operation); - return; default: checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); } @@ -785,10 +776,6 @@ public abstract class BaseController { return queue; } - protected QueueStats checkQueueStatsId(QueueStatsId queueStatsId, Operation operation) throws ThingsboardException { - return checkEntityId(queueStatsId, queueStatsService::findQueueStatsById, operation); - } - protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 770745f738..5aa869f2a8 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -46,8 +46,7 @@ public enum Resource { QUEUE(EntityType.QUEUE), VERSION_CONTROL, NOTIFICATION(EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_TEMPLATE, - EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE), - QUEUE_STATS(EntityType.QUEUE_STATS); + EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE); private final Set entityTypes; diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java index 87688a797f..b0a6db944c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java @@ -179,6 +179,7 @@ public class BaseQueueControllerTest extends AbstractControllerTest { List queueStatsList = queueStatsService.findByTenantId(tenantId); assertThat(queueStatsList).hasSize(1); QueueStats queueStats = queueStatsList.get(0); + assertThat(queueStats.getQueueName()).isEqualTo(queue.getName()); ArgumentCaptor ttlCaptor = ArgumentCaptor.forClass(Long.class); verify(timeseriesDao).save(eq(tenantId), eq(queueStats.getId()), argThat(tsKvEntry -> { diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index 67ad2cbe55..577feeb9fb 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -22,11 +22,13 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; @@ -51,10 +53,13 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -69,6 +74,9 @@ public class EntityQueryControllerTest extends AbstractControllerTest { private Tenant savedTenant; private User tenantAdmin; + @Autowired + private QueueStatsService queueStatsService; + @Before public void beforeTest() throws Exception { loginSysAdmin(); @@ -593,4 +601,43 @@ public class EntityQueryControllerTest extends AbstractControllerTest { assertThat(getErrorMessage(result)).contains("Invalid").contains("sort property"); } + @Test + public void testFindQueueStatsEntitiesByQuery() throws Exception { + List queueStatsList = new ArrayList<>(); + for (int i = 0; i < 97; i++) { + QueueStats queueStats = new QueueStats(); + queueStats.setQueueName(StringUtils.randomAlphabetic(5)); + queueStats.setServiceId(StringUtils.randomAlphabetic(5)); + queueStats.setTenantId(savedTenant.getTenantId()); + queueStatsList.add(queueStatsService.save(savedTenant.getId(), queueStats)); + Thread.sleep(1); + } + + EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + entityTypeFilter.setEntityType(EntityType.QUEUE_STATS); + + EntityDataSortOrder sortOrder = new EntityDataSortOrder( + new EntityKey(EntityKeyType.ENTITY_FIELD, "queueName"), EntityDataSortOrder.Direction.ASC + ); + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); + List entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "queueName"), + new EntityKey(EntityKeyType.ENTITY_FIELD, "serviceId")); + + EntityDataQuery query = new EntityDataQuery(entityTypeFilter, pageLink, entityFields, null, null); + + PageData data = + doPostWithTypedResponse("/api/entitiesQuery/find", query, new TypeReference>() { + }); + + Assert.assertEquals(97, data.getTotalElements()); + Assert.assertEquals(10, data.getTotalPages()); + Assert.assertTrue(data.hasNext()); + Assert.assertEquals(10, data.getData().size()); + + EntityCountQuery countQuery = new EntityCountQuery(entityTypeFilter); + + Long count = doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class); + Assert.assertEquals(97, count.longValue()); + } + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java index e7166c4fd6..9ab67c73a7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/queue/QueueStatsService.java @@ -28,8 +28,10 @@ public interface QueueStatsService extends EntityDaoService { QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId); - QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueStatsName, String serviceId); + QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId); List findByTenantId(TenantId tenantId); + void deleteByTenantId(TenantId tenantId); + } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index eb8b337c7d..7a9e4388f7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -103,6 +103,8 @@ public class EntityIdFactory { return new NotificationTemplateId(uuid); case NOTIFICATION: return new NotificationId(uuid); + case QUEUE_STATS: + return new QueueStatsId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java index b6224d4167..04d50dfe6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java @@ -16,13 +16,15 @@ package org.thingsboard.server.common.data.queue; import lombok.Data; -import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.QueueStatsId; +@EqualsAndHashCode(callSuper = true) @Data -public class QueueStats extends BaseDataWithAdditionalInfo implements HasTenantId { +public class QueueStats extends BaseData implements HasTenantId { private TenantId tenantId; private String queueName; private String serviceId; diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index 4da2883291..a332c1858b 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -54,6 +54,7 @@ enum EntityTypeProto { NOTIFICATION_REQUEST = 31; NOTIFICATION = 32; NOTIFICATION_RULE = 33; + QUEUE_STATS = 34; } /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 7220a3acdb..e88618afba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -75,7 +75,6 @@ public class BaseAssetService extends AbstractCachedEntityService queueStatsValidator; @Override public QueueStats save(TenantId tenantId, QueueStats queueStats) { + log.trace("Executing save [{}]", queueStats); + queueStatsValidator.validate(queueStats, QueueStats::getTenantId); return queueStatsDao.save(tenantId, queueStats); } @@ -55,15 +60,23 @@ public class BaseQueueStatsService extends AbstractEntityService implements Queu @Override public QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId) { log.trace("Executing findByTenantIdAndNameAndServiceId, tenantId: [{}], queueName: [{}], serviceId: [{}]", tenantId, queueName, serviceId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); return queueStatsDao.findByTenantIdQueueNameAndServiceId(tenantId, queueName, serviceId); } @Override public List findByTenantId(TenantId tenantId) { log.trace("Executing findByTenantId, tenantId: [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); return queueStatsDao.findByTenantId(tenantId); } + @Override + public void deleteByTenantId(TenantId tenantId) { + log.trace("Executing deleteDevicesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + queueStatsDao.deleteByTenantId(tenantId); + } @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { @@ -72,6 +85,6 @@ public class BaseQueueStatsService extends AbstractEntityService implements Queu @Override public EntityType getEntityType() { - return null; + return EntityType.QUEUE_STATS; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java index fc83891036..1c3db8bb54 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/QueueStatsDao.java @@ -23,8 +23,10 @@ import java.util.List; public interface QueueStatsDao extends Dao { - QueueStats findByTenantIdQueueNameAndServiceId(TenantId tenantId, String name, String serviceId); + QueueStats findByTenantIdQueueNameAndServiceId(TenantId tenantId, String queueName, String serviceId); List findByTenantId(TenantId tenantId); + void deleteByTenantId(TenantId tenantId); + } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java new file mode 100644 index 0000000000..92134b98d5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueStatsDataValidator.java @@ -0,0 +1,40 @@ +/** + * 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.dao.service.validator; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.QueueStats; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +public class QueueStatsDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, QueueStats queueStats) { + if (queueStats.getTenantId() == null) { + throw new DataValidationException("Tenant id should be specified!."); + } + if (queueStats.getQueueName() == null) { + throw new DataValidationException("Queue name should be specified!."); + } + if (StringUtils.isEmpty(queueStats.getServiceId())) { + throw new DataValidationException("Service id should be specified!."); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java index f5874cbd08..43cc3dbc69 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java @@ -73,6 +73,8 @@ public class EntityKeyMapping { public static final String PHONE = "phone"; public static final String ADDITIONAL_INFO = "additionalInfo"; public static final String RELATED_PARENT_ID = "parentId"; + public static final String QUEUE_NAME = "queueName"; + public static final String SERVICE_ID = "serviceId"; public static final List typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO); public static final List widgetEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME); @@ -104,6 +106,7 @@ public class EntityKeyMapping { allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields); allowedEntityFieldMap.put(EntityType.DEVICE_PROFILE, Set.of(CREATED_TIME, NAME, TYPE)); allowedEntityFieldMap.put(EntityType.ASSET_PROFILE, Set.of(CREATED_TIME, NAME)); + allowedEntityFieldMap.put(EntityType.QUEUE_STATS, new HashSet<>(Arrays.asList(CREATED_TIME, QUEUE_NAME, SERVICE_ID))); entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); @@ -124,6 +127,8 @@ public class EntityKeyMapping { entityFieldColumnMap.put(PHONE, ModelConstants.PHONE_PROPERTY); entityFieldColumnMap.put(ADDITIONAL_INFO, ModelConstants.ADDITIONAL_INFO_PROPERTY); entityFieldColumnMap.put(RELATED_PARENT_ID, "parent_id"); + entityFieldColumnMap.put(QUEUE_NAME, ModelConstants.QUEUE_STATS_QUEUE_NAME_PROPERTY); + entityFieldColumnMap.put(SERVICE_ID, ModelConstants.QUEUE_STATS_SERVICE_ID_PROPERTY); Map contactBasedAliases = new HashMap<>(); contactBasedAliases.put(NAME, TITLE); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java index 29853ce611..ac57d54e90 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueStatsDao.java @@ -49,8 +49,8 @@ public class JpaQueueStatsDao extends JpaAbstractDao { - QueueStatsEntity findByTenantIdAndQueueNameAndServiceId(UUID tenantId, String name, String serviceId); + QueueStatsEntity findByTenantIdAndQueueNameAndServiceId(UUID tenantId, String queueName, String serviceId); List findByTenantId(UUID tenantId); + @Transactional + @Modifying + @Query("DELETE FROM QueueStatsEntity t WHERE t.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); + } \ No newline at end of file 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 d91f469c1b..cfef5d4cd0 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 @@ -49,6 +49,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -131,6 +132,10 @@ public class TenantServiceImpl extends AbstractCachedEntityService 0); + Assert.assertEquals(queueStats.getTenantId(), savedQueueStats.getTenantId()); + Assert.assertEquals(savedQueueStats.getQueueName(), queueStats.getQueueName()); + + QueueStats retrievedQueueStatsById = queueStatsService.findQueueStatsById(tenantId, savedQueueStats.getId()); + Assert.assertEquals(retrievedQueueStatsById.getQueueName(), queueName); + + String secondQueueName = StringUtils.randomAlphabetic(8); + queueStats.setQueueName(secondQueueName); + QueueStats savedQueueStats2 = queueStatsService.save(tenantId, queueStats); + QueueStats retrievedQueueStatsById2 = queueStatsService.findQueueStatsById(tenantId, savedQueueStats2.getId()); + Assert.assertEquals(retrievedQueueStatsById2.getQueueName(), secondQueueName); + + List queueStatsList = queueStatsService.findByTenantId(tenantId); + Assert.assertEquals(2, queueStatsList.size()); + assertThat(queueStatsList).containsOnly(retrievedQueueStatsById, retrievedQueueStatsById2); + + queueStatsService.deleteByTenantId(tenantId); + QueueStats retrievedQueueStatsAfterDelete = queueStatsService.findQueueStatsById(tenantId, savedQueueStats.getId()); + Assert.assertNull(retrievedQueueStatsAfterDelete); + } + + @Test + public void testSaveWithNullQueueName() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(null); + queueStats.setServiceId(StringUtils.randomAlphabetic(8)); + + Assertions.assertThrows(DataValidationException.class, () -> { + queueStatsService.save(tenantId, queueStats); + }); + } + + @Test + public void testSaveWithNullServiceId() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(StringUtils.randomAlphabetic(8)); + queueStats.setServiceId(null); + + Assertions.assertThrows(DataValidationException.class, () -> { + queueStatsService.save(tenantId, queueStats); + }); + } + + @Test + public void testFindByTenantIdAndNameAndServiceId() { + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + queueStats.setQueueName(StringUtils.randomAlphabetic(8)); + queueStats.setServiceId(StringUtils.randomAlphabetic(8)); + QueueStats savedQueueStats = queueStatsService.save(tenantId, queueStats); + + QueueStats queueStats2 = new QueueStats(); + queueStats2.setTenantId(tenantId); + queueStats2.setQueueName(StringUtils.randomAlphabetic(8)); + queueStats2.setServiceId(StringUtils.randomAlphabetic(8)); + queueStatsService.save(tenantId, queueStats2); + + QueueStats retrievedQueueStatsById = queueStatsService.findByTenantIdAndNameAndServiceId(tenantId, queueStats.getQueueName(), queueStats.getServiceId()); + assertThat(retrievedQueueStatsById).isEqualTo(savedQueueStats); + } + +} diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json index b1cea42249..9e0af9556a 100644 --- a/ui-ngx/src/assets/dashboard/api_usage.json +++ b/ui-ngx/src/assets/dashboard/api_usage.json @@ -3729,7 +3729,7 @@ "_hash": 0.49891007198715376 } ], - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + "entityAliasId": "1fcc06b0-ba0d-11ee-8765-750128ef3ba9" } ], "timewindow": { @@ -3880,7 +3880,7 @@ "_hash": 0.2679547062508352 } ], - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + "entityAliasId": "1fcc06b0-ba0d-11ee-8765-750128ef3ba9" } ], "timewindow": { @@ -4007,7 +4007,7 @@ "_hash": 0.7255162989552142 } ], - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + "entityAliasId": "1fcc06b0-ba0d-11ee-8765-750128ef3ba9" } ], "timewindow": { @@ -4880,16 +4880,13 @@ "resolveMultiple": false } }, - "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f": { - "id": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "1fcc06b0-ba0d-11ee-8765-750128ef3ba9": { + "id": "1fcc06b0-ba0d-11ee-8765-750128ef3ba9", "alias": "TbServiceQueues", "filter": { - "type": "assetType", + "type": "entityType", "resolveMultiple": true, - "assetNameFilter": "", - "assetTypes": [ - "TbServiceQueue" - ] + "entityType": "QUEUE_STATS" } } }, From aa24da06ea6a2e56bd6e0952f3fee71988a3544c Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 9 Feb 2024 16:13:45 +0200 Subject: [PATCH 005/107] TenantIdLoaderTest fix --- .../server/controller/EntityQueryControllerTest.java | 4 ++++ .../org/thingsboard/rule/engine/api/TbContext.java | 3 +++ .../thingsboard/rule/engine/util/TenantIdLoader.java | 4 ++++ .../rule/engine/util/TenantIdLoaderTest.java | 10 ++++++++++ 4 files changed, 21 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index 577feeb9fb..8de8514e5b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -633,6 +633,10 @@ public class EntityQueryControllerTest extends AbstractControllerTest { Assert.assertEquals(10, data.getTotalPages()); Assert.assertTrue(data.hasNext()); Assert.assertEquals(10, data.getData().size()); + data.getData().forEach(entityData -> { + assertThat(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("queueName")).asString().isNotBlank(); + assertThat(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("serviceId")).asString().isNotBlank(); + }); EntityCountQuery countQuery = new EntityCountQuery(entityTypeFilter); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 59f5b128a7..3d74b70447 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -69,6 +69,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -314,6 +315,8 @@ public interface TbContext { QueueService getQueueService(); + QueueStatsService getQueueStatsService(); + ListeningExecutor getMailExecutor(); ListeningExecutor getSmsExecutor(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java index 0900137b73..152e193ea4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.id.QueueStatsId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -141,6 +142,9 @@ public class TenantIdLoader { case NOTIFICATION_RULE: tenantEntity = ctx.getNotificationRuleService().findNotificationRuleById(ctxTenantId, new NotificationRuleId(id)); break; + case QUEUE_STATS: + tenantEntity = ctx.getQueueStatsService().findQueueStatsById(ctxTenantId, new QueueStatsId(id)); + break; default: throw new RuntimeException("Unexpected entity type: " + entityId.getEntityType()); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java index 5648b02454..e06be15fe6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; @@ -73,6 +74,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.user.UserService; @@ -135,6 +137,8 @@ public class TenantIdLoaderTest { private NotificationRequestService notificationRequestService; @Mock private NotificationRuleService notificationRuleService; + @Mock + private QueueStatsService queueStatsService; private TenantId tenantId; private TenantProfileId tenantProfileId; @@ -352,6 +356,12 @@ public class TenantIdLoaderTest { when(ctx.getNotificationRuleService()).thenReturn(notificationRuleService); doReturn(notificationRule).when(notificationRuleService).findNotificationRuleById(eq(tenantId), any()); break; + case QUEUE_STATS: + QueueStats queueStats = new QueueStats(); + queueStats.setTenantId(tenantId); + when(ctx.getQueueStatsService()).thenReturn(queueStatsService); + doReturn(queueStats).when(queueStatsService).findQueueStatsById(eq(tenantId), any()); + break; default: throw new RuntimeException("Unexpected originator EntityType " + entityType); } From 077a5fa49226b48b1efef76f4081dab887d52096 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 9 Feb 2024 16:23:21 +0200 Subject: [PATCH 006/107] added QueueStatsService to DefaultTbContext --- .../org/thingsboard/server/actors/ActorSystemContext.java | 6 ++++++ .../server/actors/ruleChain/DefaultTbContext.java | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index 31d9e477ac..fa717a2613 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -78,6 +78,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -446,6 +447,11 @@ public class ActorSystemContext { @Getter private QueueService queueService; + @Lazy + @Autowired(required = false) + @Getter + private QueueStatsService queueStatsService; + @Lazy @Autowired(required = false) @Getter diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index cc6653f4c5..d34a099d26 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -96,6 +96,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.queue.QueueStatsService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -738,6 +739,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getQueueService(); } + @Override + public QueueStatsService getQueueStatsService() { + return mainCtx.getQueueStatsService(); + } + @Override public EventLoopGroup getSharedEventLoop() { return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); From a018cdf09a807d0237ac74587fca5e62169518de Mon Sep 17 00:00:00 2001 From: rusikv Date: Tue, 13 Feb 2024 13:01:39 +0200 Subject: [PATCH 007/107] UI: QUEUE_STATS for API usage --- .../dashboards/rule_engine_statistics.json | 94 ++++++++++++++++++- ui-ngx/src/app/core/http/entity.service.ts | 5 + .../profile/asset-profile.component.html | 2 +- .../profile/asset-profile.component.ts | 4 +- .../asset-profiles-table-config.resolver.ts | 9 +- ui-ngx/src/app/shared/models/asset.models.ts | 2 - .../app/shared/models/entity-type.models.ts | 8 ++ ui-ngx/src/app/shared/models/entity.models.ts | 10 ++ .../app/shared/models/query/query.models.ts | 3 + ui-ngx/src/assets/dashboard/api_usage.json | 94 ++++++++++++++++++- .../assets/locale/locale.constant-en_US.json | 6 +- 11 files changed, 215 insertions(+), 22 deletions(-) diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json index 2a079fcd9d..576b1432ff 100644 --- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json +++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json @@ -13,6 +13,7 @@ "datasources": [ { "type": "entity", + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "dataKeys": [ { "name": "successfulMsgs", @@ -105,7 +106,24 @@ "_hash": 0.49891007198715376 } ], - "entityAliasId": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba" + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#ffc107", + "settings": {}, + "_hash": 0.019706324241253403 + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#607d8b", + "settings": {}, + "_hash": 0.6439850190675356 + } + ] } ], "timewindow": { @@ -190,6 +208,7 @@ "datasources": [ { "type": "entity", + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "dataKeys": [ { "name": "ruleEngineException", @@ -228,7 +247,54 @@ "_hash": 0.7255162989552142 } ], - "entityAliasId": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba" + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#ffc107", + "settings": { + "show": false, + "order": null, + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.15709042234841886, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#607d8b", + "settings": { + "show": false, + "order": null, + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.13037127418736705, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] } ], "timewindow": { @@ -279,6 +345,7 @@ "datasources": [ { "type": "entity", + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "dataKeys": [ { "name": "timeoutMsgs", @@ -341,7 +408,24 @@ "_hash": 0.2679547062508352 } ], - "entityAliasId": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba" + "latestDataKeys": [ + { + "name": "queueName", + "type": "entityField", + "label": "Queue name", + "color": "#f44336", + "settings": {}, + "_hash": 0.009348067096302426 + }, + { + "name": "serviceId", + "type": "entityField", + "label": "Service Id", + "color": "#ffc107", + "settings": {}, + "_hash": 0.4586005983243109 + } + ] } ], "timewindow": { @@ -463,8 +547,8 @@ } }, "entityAliases": { - "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba": { - "id": "26f9d890-9611-acd4-1cdb-e0a77fd0e7ba", + "140f23dd-e3a0-ed98-6189-03c49d2d8018": { + "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "alias": "TbServiceQueues", "filter": { "type": "entityType", diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index b29fd6c9a0..ce1c26f17d 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -716,6 +716,7 @@ export class EntityService { entityTypes.push(EntityType.CUSTOMER); entityTypes.push(EntityType.USER); entityTypes.push(EntityType.DASHBOARD); + entityTypes.push(EntityType.QUEUE_STATS); if (authState.edgesSupportEnabled) { entityTypes.push(EntityType.EDGE); } @@ -795,6 +796,10 @@ export class EntityService { case EntityType.API_USAGE_STATE: entityFieldKeys.push(entityFields.name.keyName); break; + case EntityType.QUEUE_STATS: + entityFieldKeys.push(entityFields.queueName.keyName); + entityFieldKeys.push(entityFields.serviceId.keyName); + break; } return query ? entityFieldKeys.filter((entityField) => entityField.toLowerCase().indexOf(query) === 0) : entityFieldKeys; } diff --git a/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html index 5bf94d5c86..67f3bcdf13 100644 --- a/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html @@ -31,7 +31,7 @@ +
+ + {{ 'version-control.rollback-on-error' | translate }} + +
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 8f02129a80..571773f389 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4915,7 +4915,9 @@ "device-credentials-conflict": "Failed to load the device with external id {{entityId}}
due to the same credentials are already present in the database for another device.
Please consider disabling the load credentials setting in the restore form.", "missing-referenced-entity": "Failed to load the {{sourceEntityTypeName}} with external id {{sourceEntityId}}
because it references missing {{targetEntityTypeName}} with id {{targetEntityId}}.", "runtime-failed": "Failed: {{message}}", - "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings." + "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings.", + "rollback-on-error": "Rollback on error", + "rollback-on-error-hint": "If you have a large amount of entities to restore, consider disabling this option to increase performance.
Note, if an error occurs over the course of version loading, already persisted entities (with relations, attributes, etc.) will stay as is" }, "widget": { "widget-library": "Widgets library", From 2ca3a86c96f1e948d2ff28d6b0025be606791514 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 15 Mar 2024 16:51:00 +0200 Subject: [PATCH 020/107] UI: Add in VC support new property rollback on error --- .../home/components/vc/complex-version-load.component.html | 7 ++++++- .../home/components/vc/complex-version-load.component.ts | 1 + .../components/vc/entity-types-version-load.component.html | 5 ----- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html index 0ef7f3bc7c..ccab83b9aa 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html @@ -23,11 +23,16 @@ - + +
+ + {{ 'version-control.rollback-on-error' | translate }} + +
-
- - {{ 'version-control.rollback-on-error' | translate }} - -
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 571773f389..3fc05736e8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4917,7 +4917,7 @@ "runtime-failed": "Failed: {{message}}", "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings.", "rollback-on-error": "Rollback on error", - "rollback-on-error-hint": "If you have a large amount of entities to restore, consider disabling this option to increase performance.
Note, if an error occurs over the course of version loading, already persisted entities (with relations, attributes, etc.) will stay as is" + "rollback-on-error-hint": "If you have a large amount of entities to restore, consider disabling this option to increase performance.\n Note, if an error occurs over the course of version loading, already persisted entities (with relations, attributes, etc.) will stay as is" }, "widget": { "widget-library": "Widgets library", From a4384eb0191e91e14ab73c6183a3d6af8886fcd8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 15 Mar 2024 18:27:47 +0200 Subject: [PATCH 021/107] VC: fix other entities removal --- .../DefaultEntitiesVersionControlService.java | 45 +++++++++++++------ .../vc/complex-version-load.component.ts | 3 +- ui-ngx/src/app/shared/models/vc.models.ts | 1 + 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 123bb8a191..c4454bff9a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.common.data.sync.ie.EntityExportSettings; @@ -89,6 +90,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.function.Function; @@ -331,7 +333,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont sw.startNew("Entities " + entityType.name()); ctx.setSettings(getEntityImportSettings(request, entityType)); importEntities(ctx, entityType); - persistToCache(ctx); } sw.startNew("Reimport"); @@ -343,7 +344,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont .filter(entityType -> request.getEntityTypes().get(entityType).isRemoveOtherEntities()) .sorted(exportImportService.getEntityTypeComparatorForImport().reversed()) .forEach(entityType -> removeOtherEntities(ctx, entityType)); - persistToCache(ctx); sw.startNew("References and Relations"); exportImportService.saveReferencesAndRelations(ctx); @@ -396,6 +396,8 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont ctx.getImportedEntities().computeIfAbsent(entityType, t -> new HashSet<>()) .add(importResult.getSavedEntity().getId()); } + + persistToCache(ctx); log.debug("Imported {} pack ({}) for tenant {}", entityType, entityDataList.size(), ctx.getTenantId()); offset += limit; } while (entityDataList.size() == limit); @@ -420,19 +422,34 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } private void removeOtherEntities(EntitiesImportCtx ctx, EntityType entityType) { - DaoUtil.processInBatches(pageLink -> { - return exportableEntitiesService.findEntitiesByTenantId(ctx.getTenantId(), entityType, pageLink); - }, 100, entity -> { - if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entity.getId())) { - exportableEntitiesService.removeById(ctx.getTenantId(), entity.getId()); - - ctx.addEventCallback(() -> { - entityNotificationService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null, - ActionType.DELETED, ctx.getUser()); - }); - ctx.registerDeleted(entityType); + var entities = new PageDataIterable<>(link -> exportableEntitiesService.findEntitiesIdsByTenantId(ctx.getTenantId(), entityType, link), 100); + Set toRemove = new HashSet<>(); + for (EntityId entityId : entities) { + if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entityId)) { + toRemove.add(entityId); } - }); + } + + for (EntityId entityId : toRemove) { + ExportableEntity entity = exportableEntitiesService.findEntityById(entityId); + exportableEntitiesService.removeById(ctx.getTenantId(), entityId); + + ThrowingRunnable callback = () -> { + entityNotificationService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null, + ActionType.DELETED, ctx.getUser()); + }; + if (ctx.isRollbackOnError()) { + ctx.addEventCallback(callback); + } else { + try { + callback.run(); + } catch (ThingsboardException e) { + throw new RuntimeException(e); + } + } + ctx.registerDeleted(entityType); + } + persistToCache(ctx); } private VersionLoadResult onError(EntityId externalId, Throwable e) { diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts index 77a3ca3194..0fed8dab88 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts @@ -79,7 +79,7 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit ngOnInit(): void { this.loadVersionFormGroup = this.fb.group({ entityTypes: [createDefaultEntityTypesVersionLoad(), []], - rollbackOnError: [false] + rollbackOnError: [true] }); } @@ -117,6 +117,7 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit const request: EntityTypeVersionLoadRequest = { versionId: this.versionId, entityTypes: this.loadVersionFormGroup.get('entityTypes').value, + rollbackOnError: this.loadVersionFormGroup.get('rollbackOnError').value, type: VersionLoadRequestType.ENTITY_TYPE }; this.versionLoadResult$ = this.entitiesVersionControlService.loadEntitiesVersion(request, {ignoreErrors: true}).pipe( diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index 8cf7366a0a..ba54a297a7 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -144,6 +144,7 @@ export interface EntityTypeVersionLoadConfig extends VersionLoadConfig { export interface EntityTypeVersionLoadRequest extends VersionLoadRequest { entityTypes: {[entityType: string]: EntityTypeVersionLoadConfig}; type: VersionLoadRequestType.ENTITY_TYPE; + rollbackOnError: boolean; } export function createDefaultEntityTypesVersionLoad(): {[entityType: string]: EntityTypeVersionLoadConfig} { From 544c9b260c0d6b36268803e50118ef00632cdee3 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 18 Mar 2024 10:35:20 +0100 Subject: [PATCH 022/107] fixed NPE related to the entity service registry initialization --- .../server/dao/entity/BaseEntityService.java | 2 ++ .../dao/entity/DefaultEntityServiceRegistry.java | 14 +++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 4ee812a26b..b91e2ed96d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.entity; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.thingsboard.server.common.data.HasCustomerId; @@ -60,6 +61,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private EntityQueryDao entityQueryDao; @Autowired + @Lazy EntityServiceRegistry entityServiceRegistry; @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java index 6e6233b1b2..ed0f14ad6d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java @@ -17,15 +17,12 @@ package org.thingsboard.server.dao.entity; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; +import javax.annotation.PostConstruct; import java.util.HashMap; +import java.util.List; import java.util.Map; @Service @@ -33,14 +30,13 @@ import java.util.Map; @Slf4j public class DefaultEntityServiceRegistry implements EntityServiceRegistry { - private final ApplicationContext applicationContext; + private final List entityDaoServices; private final Map entityDaoServicesMap = new HashMap<>(); - @EventListener(ContextRefreshedEvent.class) - @Order(Ordered.HIGHEST_PRECEDENCE) + @PostConstruct public void init() { log.debug("Initializing EntityServiceRegistry on ContextRefreshedEvent"); - applicationContext.getBeansOfType(EntityDaoService.class).values().forEach(entityDaoService -> { + entityDaoServices.forEach(entityDaoService -> { EntityType entityType = entityDaoService.getEntityType(); entityDaoServicesMap.put(entityType, entityDaoService); if (EntityType.RULE_CHAIN.equals(entityType)) { From 0cd0c65a08c47f6b31e8e52c02b6986e42922dc4 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 18 Mar 2024 12:37:36 +0200 Subject: [PATCH 023/107] do not refresh token if refresh token expired --- .../mail/RefreshTokenExpCheckService.java | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java b/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java index 8e6dfce8f0..7c7edf2164 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java @@ -56,22 +56,33 @@ public class RefreshTokenExpCheckService { JsonNode jsonValue = settings.getJsonValue(); if (OFFICE_365.name().equals(jsonValue.get("providerId").asText()) && jsonValue.has("refreshToken") && jsonValue.has("refreshTokenExpires")) { - long expiresIn = jsonValue.get("refreshTokenExpires").longValue(); - if ((expiresIn - System.currentTimeMillis()) < 604800000L) { //less than 7 days - log.info("Trying to refresh refresh token."); + try { + long expiresIn = jsonValue.get("refreshTokenExpires").longValue(); + long tokenLifeDuration = expiresIn - System.currentTimeMillis(); + if (tokenLifeDuration < 0) { + ((ObjectNode) jsonValue).put("tokenGenerated", false); + ((ObjectNode) jsonValue).remove("refreshToken"); + ((ObjectNode) jsonValue).remove("refreshTokenExpires"); - String clientId = jsonValue.get("clientId").asText(); - String clientSecret = jsonValue.get("clientSecret").asText(); - String refreshToken = jsonValue.get("refreshToken").asText(); - String tokenUri = jsonValue.get("tokenUri").asText(); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); + } else if (tokenLifeDuration < 604800000L) { //less than 7 days + log.info("Trying to refresh refresh token."); - TokenResponse tokenResponse = new RefreshTokenRequest(new NetHttpTransport(), new GsonFactory(), - new GenericUrl(tokenUri), refreshToken) - .setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)) - .execute(); - ((ObjectNode) jsonValue).put("refreshToken", tokenResponse.getRefreshToken()); - ((ObjectNode) jsonValue).put("refreshTokenExpires", Instant.now().plus(Duration.ofDays(AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS)).toEpochMilli()); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); + String clientId = jsonValue.get("clientId").asText(); + String clientSecret = jsonValue.get("clientSecret").asText(); + String refreshToken = jsonValue.get("refreshToken").asText(); + String tokenUri = jsonValue.get("tokenUri").asText(); + + TokenResponse tokenResponse = new RefreshTokenRequest(new NetHttpTransport(), new GsonFactory(), + new GenericUrl(tokenUri), refreshToken) + .setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)) + .execute(); + ((ObjectNode) jsonValue).put("refreshToken", tokenResponse.getRefreshToken()); + ((ObjectNode) jsonValue).put("refreshTokenExpires", Instant.now().plus(Duration.ofDays(AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS)).toEpochMilli()); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); + } + } catch (Exception e) { + log.error("Error occurred while checking token", e); } } } From 32f0c3c67c3d319cbe609d79860970ba8f22034f Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 19 Mar 2024 13:30:20 +0200 Subject: [PATCH 024/107] Don't create Rule Engine consumer when no partitions to avoid NPE when deleting queue --- .../queue/DefaultTbRuleEngineConsumerService.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index 13de09bb2d..f0b93a175a 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -114,9 +114,17 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< if (partitionService.isManagedByCurrentService(queueKey.getTenantId())) { var consumer = getConsumer(queueKey).orElseGet(() -> { Queue config = queueService.findQueueByTenantIdAndName(queueKey.getTenantId(), queueKey.getQueueName()); + if (config == null) { + if (!partitions.isEmpty()) { + log.error("[{}] Queue configuration is missing", queueKey, new RuntimeException("stacktrace")); + } + return null; + } return createConsumer(queueKey, config); }); - consumer.update(partitions); + if (consumer != null) { + consumer.update(partitions); + } } }); consumers.keySet().stream() From 7a140b25181488e9855fe807c6641afe46f788e1 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 2 Jan 2024 20:13:35 +0100 Subject: [PATCH 025/107] haproxy limits, blocklist, trustlist --- docker/haproxy/config/blocklist.txt | 3 + docker/haproxy/config/haproxy.cfg | 102 ++++++++++++++++++++++++++-- docker/haproxy/config/trustlist.txt | 12 ++++ 3 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 docker/haproxy/config/blocklist.txt create mode 100644 docker/haproxy/config/trustlist.txt diff --git a/docker/haproxy/config/blocklist.txt b/docker/haproxy/config/blocklist.txt new file mode 100644 index 0000000000..ff9429857f --- /dev/null +++ b/docker/haproxy/config/blocklist.txt @@ -0,0 +1,3 @@ +# Blocked subnets and IPs. Use CIDR or IP by one per line +5.136.0.0/13 +217.199.254.1 diff --git a/docker/haproxy/config/haproxy.cfg b/docker/haproxy/config/haproxy.cfg index aa752502bf..757efe692f 100644 --- a/docker/haproxy/config/haproxy.cfg +++ b/docker/haproxy/config/haproxy.cfg @@ -41,6 +41,15 @@ listen stats listen mqtt-in bind *:${MQTT_PORT} mode tcp + + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 50 } + tcp-request connection track-sc1 src + option clitcpka # For TCP keep-alive timeout client 3h timeout server 3h @@ -52,6 +61,15 @@ listen mqtt-in listen edges-rpc-in bind *:${EDGES_RPC_PORT} mode tcp + + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 5 } + tcp-request connection track-sc1 src + option clitcpka # For TCP keep-alive timeout client 3h timeout server 3h @@ -63,18 +81,28 @@ listen edges-rpc-in frontend http-in bind *:${HTTP_PORT} alpn h2,http/1.1 + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 50 } + tcp-request connection track-sc1 src + option forwardfor http-request add-header "X-Forwarded-Proto" "http" acl transport_http_acl path_beg /api/v1/ acl letsencrypt_http_acl path_beg /.well-known/acme-challenge/ + acl tb_images_api_acl path_beg /api/images/ acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /v3/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/ redirect scheme https if !letsencrypt_http_acl !transport_http_acl { env(FORCE_HTTPS_REDIRECT) -m str true } use_backend letsencrypt_http if letsencrypt_http_acl use_backend tb-http-backend if transport_http_acl + use_backend tb-images-api-backend if tb_images_api_acl use_backend tb-api-backend if tb_api_acl default_backend tb-web-backend @@ -82,14 +110,24 @@ frontend http-in frontend https_in bind *:${HTTPS_PORT} ssl crt /usr/local/etc/haproxy/default.pem crt /usr/local/etc/haproxy/certs.d ciphers ECDHE-RSA-AES256-SHA:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM alpn h2,http/1.1 + stick-table type ip size 60k expire 60s store conn_cur + + acl trustlist src -f /config/trustlist.txt + acl blocklist src -f /config/blocklist.txt + tcp-request connection accept if trustlist + tcp-request connection reject if blocklist or { src_conn_cur ge 50 } + tcp-request connection track-sc1 src + option forwardfor http-request add-header "X-Forwarded-Proto" "https" acl transport_http_acl path_beg /api/v1/ + acl tb_images_api_acl path_beg /api/images/ acl tb_api_acl path_beg /api/ /swagger /webjars /v2/ /v3/ /static/rulenode/ /oauth2/ /login/oauth2/ /static/widgets/ use_backend tb-http-backend if transport_http_acl + use_backend tb-images-api-backend if tb_images_api_acl use_backend tb-api-backend if tb_api_acl default_backend tb-web-backend @@ -98,24 +136,76 @@ backend letsencrypt_http server letsencrypt_http_srv 127.0.0.1:8080 backend tb-web-backend + timeout queue 60s balance leastconn option tcp-check option log-health-checks - server tbWeb1 tb-web-ui1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 - server tbWeb2 tb-web-ui2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 + server tbWeb1 tb-web-ui1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbWeb2 tb-web-ui2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 http-request set-header X-Forwarded-Port %[dst_port] backend tb-http-backend + timeout queue 60s balance leastconn option tcp-check option log-health-checks - server tbHttp1 tb-http-transport1:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 - server tbHttp2 tb-http-transport2:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 + server tbHttp1 tb-http-transport1:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbHttp2 tb-http-transport2:8081 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + +# Dummy backends for a stick-table purpose only. +# There is only one stick-table per proxy. At the moment of writing this doc, +# it does not seem useful to have multiple tables per proxy. If this happens +# to be required, simply create a dummy backend with a stick-table in it and +# reference it +# https://www.haproxy.com/documentation/haproxy-configuration-manual/latest/#stick-table +backend st_src_rate10s + stick-table type ip size 60k expire 10s store http_req_rate(10s) + +backend st_src_rate1m + stick-table type ip size 60k expire 1m store http_req_rate(1m) backend tb-api-backend + timeout queue 60s balance source option tcp-check option log-health-checks - server tbApi1 tb-core1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 - server tbApi2 tb-core2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 + + http-request track-sc0 src table st_src_rate10s + http-request track-sc1 src table st_src_rate1m + + acl trustlist src -f /config/trustlist.txt + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 } !trustlist + http-request deny deny_status 429 if { sc_http_req_rate(1) gt 300 } !trustlist + + http-request set-header X-Forwarded-Port %[dst_port] + server tbApi1 tb-core1:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbApi2 tb-core2:8080 check inter 5s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + +# Dummy backends for a stick-table purpose only. +# There is only one stick-table per proxy. At the moment of writing this doc, +# it does not seem useful to have multiple tables per proxy. If this happens +# to be required, simply create a dummy backend with a stick-table in it and +# reference it +# https://www.haproxy.com/documentation/haproxy-configuration-manual/latest/#stick-table +backend st_images_src_rate10s + stick-table type ip size 60k expire 10s store http_req_rate(10s) + +backend st_images_src_rate1m + stick-table type ip size 60k expire 1m store http_req_rate(1m) + +backend tb-images-api-backend + timeout queue 60s + balance source + option tcp-check + option log-health-checks + + http-request track-sc0 src table st_images_src_rate10s + http-request track-sc1 src table st_images_src_rate1m + + acl trustlist src -f /config/trustlist.txt + http-request deny deny_status 429 if { sc_http_req_rate(0) gt 1000 } !trustlist + http-request deny deny_status 429 if { sc_http_req_rate(1) gt 3000 } !trustlist + http-request set-header X-Forwarded-Port %[dst_port] + server tbImagesApi1 tb-core1:8080 check inter 10s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 + server tbImagesApi2 tb-core2:8080 check inter 10s resolvers docker_resolver resolve-prefer ipv4 maxconn 50 diff --git a/docker/haproxy/config/trustlist.txt b/docker/haproxy/config/trustlist.txt new file mode 100644 index 0000000000..93716b46d5 --- /dev/null +++ b/docker/haproxy/config/trustlist.txt @@ -0,0 +1,12 @@ +# Trusted list is intended to do not apply any limitations for trustees +# +# Private subnet example +# 10.0.0.0/8 +# Docker-compose subnet +172.16.0.0/12 +# Local network subnet +192.168.0.0/16 +# Allow loopback interface +127.0.0.1 +::1 +# Allow trusted IPs or CIDRs below From ab638cfa191cdf9066d89c19a5f8558fa0d4c55e Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 20 Mar 2024 18:53:26 +0200 Subject: [PATCH 026/107] add defaultSorting by default --- .../server/common/data/page/PageLink.java | 15 ++++++++++----- .../server/common/data/page/TimePageLink.java | 14 -------------- .../org/thingsboard/server/dao/DaoUtil.java | 18 +++++++++++++++--- .../server/dao/sql/alarm/JpaAlarmDao.java | 2 +- .../dao/sql/widget/JpaWidgetTypeDaoTest.java | 12 +++++++----- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java index f78249d284..042a9b1896 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageLink.java @@ -66,12 +66,17 @@ public class PageLink { return new PageLink(this.pageSize, this.page+1, this.textSearch, this.sortOrder); } - public Sort toSort(SortOrder sortOrder, Map columnMap) { - return toSort(new ArrayList<>(List.of(sortOrder)), columnMap); + public Sort toSort(SortOrder sortOrder, Map columnMap, boolean addDefaultSorting) { + if (sortOrder == null) { + return DEFAULT_SORT; + } else { + return toSort(List.of(sortOrder), columnMap, addDefaultSorting); + } } - public Sort toSort(List sortOrders, Map columnMap) { - if (!isDefaultSortOrderAvailable(sortOrders)) { + public Sort toSort(List sortOrders, Map columnMap, boolean addDefaultSorting) { + if (addDefaultSorting && !isDefaultSortOrderAvailable(sortOrders)) { + sortOrders = new ArrayList<>(sortOrders); sortOrders.add(new SortOrder(DEFAULT_SORT_PROPERTY, SortOrder.Direction.ASC)); } return Sort.by(sortOrders.stream().map(s -> toSortOrder(s, columnMap)).collect(Collectors.toList())); @@ -82,7 +87,7 @@ public class PageLink { if (columnMap.containsKey(property)) { property = columnMap.get(property); } - return new Sort.Order(Sort.Direction.fromString(sortOrder.getDirection().name()), property, Sort.NullHandling.NULLS_LAST); + return new Sort.Order(Sort.Direction.fromString(sortOrder.getDirection().name()), property); } public boolean isDefaultSortOrderAvailable(List sortOrders) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java index aadad68058..365d4f7e35 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/TimePageLink.java @@ -19,11 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import org.springframework.data.domain.Sort; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; @Data @ToString(callSuper = true) @@ -67,13 +62,4 @@ public class TimePageLink extends PageLink { this.startTime, this.endTime); } - @Override - public Sort toSort(SortOrder sortOrder, Map columnMap) { - if (sortOrder == null) { - return super.toSort(sortOrder, columnMap); - } else { - return toSort(new ArrayList<>(List.of(sortOrder)), columnMap); - } - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 0e7fe8f108..865306c73b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -56,11 +56,19 @@ public abstract class DaoUtil { } public static Pageable toPageable(PageLink pageLink) { - return toPageable(pageLink, Collections.emptyMap()); + return toPageable(pageLink, true); + } + + public static Pageable toPageable(PageLink pageLink, boolean addDefaultSorting) { + return toPageable(pageLink, Collections.emptyMap(), addDefaultSorting); } public static Pageable toPageable(PageLink pageLink, Map columnMap) { - return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap)); + return toPageable(pageLink, columnMap, true); + } + + public static Pageable toPageable(PageLink pageLink, Map columnMap, boolean addDefaultSorting) { + return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap, addDefaultSorting)); } public static Pageable toPageable(PageLink pageLink, List sortOrders) { @@ -68,7 +76,11 @@ public abstract class DaoUtil { } public static Pageable toPageable(PageLink pageLink, Map columnMap, List sortOrders) { - return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap)); + return toPageable(pageLink, columnMap, sortOrders, true); + } + + public static Pageable toPageable(PageLink pageLink, Map columnMap, List sortOrders, boolean addDefaultSorting) { + return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap, addDefaultSorting)); } public static List convertDataList(Collection> toDataList) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index 9430773ff9..15d4602230 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -383,7 +383,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Override public PageData findTenantAlarmTypes(UUID tenantId, PageLink pageLink) { - Page page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink)); + Page page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink, false)); if (page.isEmpty()) { return PageData.emptyPageData(); } 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 0f95c3f8cc..e8f3bba9db 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 @@ -16,12 +16,12 @@ package org.thingsboard.server.dao.sql.widget; import com.datastax.oss.driver.api.core.uuid.Uuids; -import org.jetbrains.annotations.NotNull; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; @@ -164,15 +164,16 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { @Test public void testFindSystemWidgetTypesForSameName() throws InterruptedException { - List widgetTypeList = new ArrayList<>(); + List sameNameList = new ArrayList<>(); for (int i = 0; i < 20; i++) { Thread.sleep(2); var widgetType = saveWidgetType(TenantId.SYS_TENANT_ID, "widgetName"); + sameNameList.add(widgetType); widgetTypeList.add(widgetType); } - widgetTypeList.sort(Comparator.comparing(BaseWidgetType::getName).thenComparing((BaseWidgetType baseWidgetType) -> baseWidgetType.getId().getId())); - List expected = widgetTypeList.stream().map(WidgetTypeInfo::new).collect(Collectors.toList()); + sameNameList.sort(Comparator.comparing(BaseWidgetType::getName).thenComparing((BaseWidgetType baseWidgetType) -> baseWidgetType.getId().getId())); + List expected = sameNameList.stream().map(WidgetTypeInfo::new).collect(Collectors.toList()); PageData widgetTypesFirstPage = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"), new PageLink(10, 0, null, new SortOrder("name"))); @@ -180,7 +181,7 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { assertThat(widgetTypesFirstPage.getData()).containsExactlyElementsOf(expected.subList(0, 10)); PageData widgetTypesSecondPage = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"), - new PageLink(10, 0, null, new SortOrder("name"))); + new PageLink(10, 1, null, new SortOrder("name"))); assertEquals(10, widgetTypesSecondPage.getData().size()); assertThat(widgetTypesSecondPage.getData()).containsExactlyElementsOf(expected.subList(10, 20)); } @@ -384,6 +385,7 @@ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { private WidgetTypeDetails saveWidgetType(TenantId tenantId, String name) { WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); + widgetType.setDescription("WIDGET_TYPE_DESCRIPTION" + StringUtils.randomAlphabetic(7)); widgetType.setName(name); var descriptor = JacksonUtil.newObjectNode(); descriptor.put("type","static"); From 28ef94bb1c261a01196d45a8fc4f2dc2dde7ad95 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 21 Mar 2024 11:07:33 +0200 Subject: [PATCH 027/107] Remove api limits from tenantProfileEdgeMsg --- .../tenant/TenantMsgConstructorV1.java | 19 +++++++++++++++++++ .../tenant/TenantMsgConstructorV2.java | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index 6b72e49df6..c6160c05bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; @@ -78,6 +79,23 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { @Override public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + tenantProfile = JacksonUtil.clone(tenantProfile); + // clear all config + DefaultTenantProfileConfiguration configuration = + (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); + configuration.setRpcTtlDays(0); + configuration.setMaxJSExecutions(0); + configuration.setMaxREExecutions(0); + configuration.setMaxDPStorageDays(0); + configuration.setMaxTbelExecutions(0); + configuration.setQueueStatsTtlDays(0); + configuration.setMaxTransportMessages(0); + configuration.setDefaultStorageTtlDays(0); + configuration.setMaxTransportDataPoints(0); + configuration.setRuleEngineExceptionsTtlDays(0); + configuration.setMaxRuleNodeExecutionsPerMessage(0); + tenantProfile.getProfileData().setConfiguration(configuration); + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_2) ? ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() @@ -93,4 +111,5 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java index c6ccb439ea..7ed36d9aef 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java @@ -19,6 +19,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; @@ -36,6 +37,24 @@ public class TenantMsgConstructorV2 implements TenantMsgConstructor { @Override public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + tenantProfile = JacksonUtil.clone(tenantProfile); + // clear all config + DefaultTenantProfileConfiguration configuration = + (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); + configuration.setRpcTtlDays(0); + configuration.setMaxJSExecutions(0); + configuration.setMaxREExecutions(0); + configuration.setMaxDPStorageDays(0); + configuration.setMaxTbelExecutions(0); + configuration.setQueueStatsTtlDays(0); + configuration.setMaxTransportMessages(0); + configuration.setDefaultStorageTtlDays(0); + configuration.setMaxTransportDataPoints(0); + configuration.setRuleEngineExceptionsTtlDays(0); + configuration.setMaxRuleNodeExecutionsPerMessage(0); + tenantProfile.getProfileData().setConfiguration(configuration); + return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); } + } From 420cebcd030a85b03304596fd23b530de086da70 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 21 Mar 2024 11:55:02 +0200 Subject: [PATCH 028/107] Use getDefaultProfileConfiguration instead of casting --- .../edge/rpc/constructor/tenant/TenantMsgConstructorV1.java | 3 +-- .../edge/rpc/constructor/tenant/TenantMsgConstructorV2.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index c6160c05bd..baa4a77b6b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -81,8 +81,7 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { tenantProfile = JacksonUtil.clone(tenantProfile); // clear all config - DefaultTenantProfileConfiguration configuration = - (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); + var configuration = tenantProfile.getDefaultProfileConfiguration(); configuration.setRpcTtlDays(0); configuration.setMaxJSExecutions(0); configuration.setMaxREExecutions(0); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java index 7ed36d9aef..274e18e7f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java @@ -39,8 +39,7 @@ public class TenantMsgConstructorV2 implements TenantMsgConstructor { public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { tenantProfile = JacksonUtil.clone(tenantProfile); // clear all config - DefaultTenantProfileConfiguration configuration = - (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); + var configuration = tenantProfile.getDefaultProfileConfiguration(); configuration.setRpcTtlDays(0); configuration.setMaxJSExecutions(0); configuration.setMaxREExecutions(0); From b46728de705534a6d01bf1a172c73ca1bbe1398c Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Thu, 21 Mar 2024 12:22:48 +0200 Subject: [PATCH 029/107] Fix V1 for tenantPrfoile to correctly set profile data --- .../edge/rpc/constructor/tenant/TenantMsgConstructorV1.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index baa4a77b6b..f75bbd219e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -21,7 +21,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; @@ -81,6 +80,7 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { tenantProfile = JacksonUtil.clone(tenantProfile); // clear all config + var tenantProfileData = tenantProfile.getProfileData(); var configuration = tenantProfile.getDefaultProfileConfiguration(); configuration.setRpcTtlDays(0); configuration.setMaxJSExecutions(0); @@ -93,7 +93,8 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { configuration.setMaxTransportDataPoints(0); configuration.setRuleEngineExceptionsTtlDays(0); configuration.setMaxRuleNodeExecutionsPerMessage(0); - tenantProfile.getProfileData().setConfiguration(configuration); + tenantProfileData.setConfiguration(configuration); + tenantProfile.setProfileData(tenantProfileData); ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_2) ? ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); From 9973cc609a75d2700dd75c13b6281a88ccaab97f Mon Sep 17 00:00:00 2001 From: rusikv Date: Thu, 21 Mar 2024 18:39:05 +0200 Subject: [PATCH 030/107] UI: upgrade oauth checkboxes to chip buttons --- .../pages/admin/oauth2-settings.component.html | 16 +++++++++------- .../pages/admin/oauth2-settings.component.ts | 13 +++++++++++-- .../src/assets/locale/locale.constant-en_US.json | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 0414a5dff7..acaf58895e 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -29,13 +29,15 @@
-
- - {{ 'admin.oauth2.enable' | translate }} - - - {{ 'admin.oauth2.edge-enable' | translate }} - +
+
+ + {{ 'admin.oauth2.enable' | translate }} + + + {{ 'admin.oauth2.edge-enable' | translate }} + +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 9cbf6368e6..f6f8340550 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -205,13 +205,22 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.oauth2SettingsForm = this.fb.group({ oauth2ParamsInfos: this.fb.array([]), enabled: [false], - edgeEnabled: [false] + edgeEnabled: [{value: false, disabled: true}] }); + + this.subscriptions.push(this.oauth2SettingsForm.get('enabled').valueChanges.subscribe(enabled => { + if (enabled) { + this.oauth2SettingsForm.get('edgeEnabled').enable(); + } else { + this.oauth2SettingsForm.get('edgeEnabled').patchValue(false); + this.oauth2SettingsForm.get('edgeEnabled').disable(); + } + })) } private initOAuth2Settings(oauth2Info: OAuth2Info): void { if (oauth2Info) { - this.oauth2SettingsForm.patchValue({enabled: oauth2Info.enabled, edgeEnabled: oauth2Info.edgeEnabled}, {emitEvent: false}); + this.oauth2SettingsForm.patchValue({enabled: oauth2Info.enabled, edgeEnabled: oauth2Info.edgeEnabled}); oauth2Info.oauth2ParamsInfos.forEach((oauth2ParamsInfo) => { this.oauth2ParamsInfos.push(this.buildOAuth2ParamsInfoForm(oauth2ParamsInfo)); }); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 457b6ecf66..a317b9b89d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -283,7 +283,7 @@ "domain-schema-https": "HTTPS", "domain-schema-mixed": "HTTP+HTTPS", "enable": "Enable OAuth2 settings", - "edge-enable": "Propagate OAuth2 settings to Edge", + "edge-enable": "Propagate to Edge", "domains": "Domains", "mobile-apps": "Mobile applications", "no-mobile-apps": "No applications configured", From ee1209b19f4e842aff42a07b81d9c579574d2f32 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 22 Mar 2024 12:01:36 +0200 Subject: [PATCH 031/107] Fix KvProtoUtils order for matching KeyValueType and DataType --- .../device/DeviceActorMessageProcessor.java | 5 +- .../queue/DefaultTbCoreConsumerService.java | 8 +- .../server/common/data/EntityType.java | 3 +- .../server/common/data/kv/DataType.java | 15 +- .../server/common/util/KvProtoUtil.java | 128 +++++++---------- .../server/common/util/KvProtoUtilTest.java | 131 ++++++++++++++++++ 6 files changed, 202 insertions(+), 88 deletions(-) create mode 100644 common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index d6b5da4ddf..d5fc8055d3 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; @@ -68,7 +69,6 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; import org.thingsboard.server.common.util.KvProtoUtil; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; @@ -97,7 +97,6 @@ import org.thingsboard.server.service.rpc.RpcSubmitStrategy; import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; -import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -605,7 +604,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) { List attributes = new ArrayList<>(msg.getValues()); if (attributes.size() > 0) { - List sharedUpdated = msg.getValues().stream().map(t -> KvProtoUtil.toTsKvProto(t.getLastUpdateTs(), t)) + List sharedUpdated = msg.getValues().stream().map(t -> KvProtoUtil.toProto(t.getLastUpdateTs(), t)) .collect(Collectors.toList()); if (!sharedUpdated.isEmpty()) { notification.addAllSharedUpdated(sharedUpdated); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index fd08b9da4d..fbbb65d4ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.queue; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -50,9 +52,9 @@ import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.common.util.KvProtoUtil; import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.dao.resource.ImageCacheKey; -import org.thingsboard.server.common.util.KvProtoUtil; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; @@ -101,8 +103,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -583,7 +583,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService dataTypeByProtoNumber[dataType.getProtoNumber()] = dataType); + } + + public static List toAttributeKvList(List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(fromProto(proto.getKv()), proto.getTs()))); + return result; + } + public static List attrToTsKvProtos(List result) { List clientAttributes; if (result == null || result.isEmpty()) { @@ -41,115 +56,70 @@ public class KvProtoUtil { } else { clientAttributes = new ArrayList<>(result.size()); for (AttributeKvEntry attrEntry : result) { - clientAttributes.add(toTsKvProto(attrEntry.getLastUpdateTs(), attrEntry)); + clientAttributes.add(toProto(attrEntry.getLastUpdateTs(), attrEntry)); } } return clientAttributes; } - - public static List tsToTsKvProtos(List result) { + public static List toProtoList(List result) { List ts; if (result == null || result.isEmpty()) { ts = Collections.emptyList(); } else { ts = new ArrayList<>(result.size()); for (TsKvEntry attrEntry : result) { - ts.add(toTsKvProto(attrEntry.getTs(), attrEntry)); + ts.add(toProto(attrEntry.getTs(), attrEntry)); } } return ts; } - public static TransportProtos.TsKvProto toTsKvProto(long ts, KvEntry kvEntry) { + public static List fromProtoList(List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromProto(proto.getKv())))); + return result; + } + + public static TransportProtos.TsKvProto toProto(long ts, KvEntry kvEntry) { return TransportProtos.TsKvProto.newBuilder().setTs(ts) - .setKv(KvProtoUtil.toKeyValueProto(kvEntry)).build(); + .setKv(KvProtoUtil.toProto(kvEntry)).build(); + } + + public static TsKvEntry fromProto(TransportProtos.TsKvProto proto) { + return new BasicTsKvEntry(proto.getTs(), fromProto(proto.getKv())); } - public static TransportProtos.KeyValueProto toKeyValueProto(KvEntry kvEntry) { + public static TransportProtos.KeyValueProto toProto(KvEntry kvEntry) { TransportProtos.KeyValueProto.Builder builder = TransportProtos.KeyValueProto.newBuilder(); builder.setKey(kvEntry.getKey()); + builder.setType(toProto(kvEntry.getDataType())); switch (kvEntry.getDataType()) { - case BOOLEAN: - builder.setType(TransportProtos.KeyValueType.BOOLEAN_V); - builder.setBoolV(kvEntry.getBooleanValue().get()); - break; - case DOUBLE: - builder.setType(TransportProtos.KeyValueType.DOUBLE_V); - builder.setDoubleV(kvEntry.getDoubleValue().get()); - break; - case LONG: - builder.setType(TransportProtos.KeyValueType.LONG_V); - builder.setLongV(kvEntry.getLongValue().get()); - break; - case STRING: - builder.setType(TransportProtos.KeyValueType.STRING_V); - builder.setStringV(kvEntry.getStrValue().get()); - break; - case JSON: - builder.setType(TransportProtos.KeyValueType.JSON_V); - builder.setJsonV(kvEntry.getJsonValue().get()); - break; + case BOOLEAN -> kvEntry.getBooleanValue().ifPresent(builder::setBoolV); + case LONG -> kvEntry.getLongValue().ifPresent(builder::setLongV); + case DOUBLE -> kvEntry.getDoubleValue().ifPresent(builder::setDoubleV); + case JSON -> kvEntry.getJsonValue().ifPresent(builder::setJsonV); + case STRING -> kvEntry.getStrValue().ifPresent(builder::setStringV); } return builder.build(); } - public static TransportProtos.TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) { - TransportProtos.KeyValueProto.Builder dataBuilder = TransportProtos.KeyValueProto.newBuilder(); - dataBuilder.setKey(attr.getKey()); - dataBuilder.setType(TransportProtos.KeyValueType.forNumber(attr.getDataType().ordinal())); - switch (attr.getDataType()) { - case BOOLEAN: - attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - break; - case LONG: - attr.getLongValue().ifPresent(dataBuilder::setLongV); - break; - case DOUBLE: - attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - break; - case JSON: - attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - break; - case STRING: - attr.getStrValue().ifPresent(dataBuilder::setStringV); - break; - } - return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); + public static KvEntry fromProto(TransportProtos.KeyValueProto proto) { + return switch (fromProto(proto.getType())) { + case BOOLEAN -> new BooleanDataEntry(proto.getKey(), proto.getBoolV()); + case LONG -> new LongDataEntry(proto.getKey(), proto.getLongV()); + case DOUBLE -> new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); + case STRING -> new StringDataEntry(proto.getKey(), proto.getStringV()); + case JSON -> new JsonDataEntry(proto.getKey(), proto.getJsonV()); + }; } - public static List toTsKvEntityList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(proto.getKv())))); - return result; + public static TransportProtos.KeyValueType toProto(DataType dataType) { + return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber()); } - public static List toAttributeKvList(List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BaseAttributeKvEntry(getKvEntry(proto.getKv()), proto.getTs()))); - return result; + public static DataType fromProto(TransportProtos.KeyValueType keyValueType) { + return dataTypeByProtoNumber[keyValueType.getNumber()]; } - private static KvEntry getKvEntry(TransportProtos.KeyValueProto proto) { - KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { - case BOOLEAN: - entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV()); - break; - case LONG: - entry = new LongDataEntry(proto.getKey(), proto.getLongV()); - break; - case DOUBLE: - entry = new DoubleDataEntry(proto.getKey(), proto.getDoubleV()); - break; - case STRING: - entry = new StringDataEntry(proto.getKey(), proto.getStringV()); - break; - case JSON: - entry = new JsonDataEntry(proto.getKey(), proto.getJsonV()); - break; - } - return entry; - } } diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java new file mode 100644 index 0000000000..603d4b3aa6 --- /dev/null +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java @@ -0,0 +1,131 @@ +/** + * 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.util; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.kv.AggTsKvEntry; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class KvProtoUtilTest { + + @Test + void protoDataTypeSerialization() { + for (DataType dataType : DataType.values()) { + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(dataType))).as(dataType.name()).isEqualTo(dataType); + } + } + + @Test + void protoKeyValueProtoSerialization() { + String key = "key"; + KvEntry kvEntry = new BooleanDataEntry(key, true); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new LongDataEntry(key, 23L); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new DoubleDataEntry(key, 23.0); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new StringDataEntry(key, "stringValue"); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new JsonDataEntry(key, "jsonValue"); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); + } + + @Test + void protoTsKvEntrySerialization() { + String key = "key"; + long ts = System.currentTimeMillis(); + KvEntry kvEntry = new BasicTsKvEntry(ts, new BooleanDataEntry(key, true)); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new BasicTsKvEntry(ts, new LongDataEntry(key, 23L)); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new BasicTsKvEntry(ts, new DoubleDataEntry(key, 23.0)); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new BasicTsKvEntry(ts, new StringDataEntry(key, "stringValue")); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + + kvEntry = new BasicTsKvEntry(ts, new JsonDataEntry(key, "jsonValue")); + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + } + + @Test + void protoListTsKvEntrySerialization() { + String key = "key"; + long ts = System.currentTimeMillis(); + KvEntry booleanDataEntry = new BooleanDataEntry(key, true); + KvEntry longDataEntry = new LongDataEntry(key, 23L); + KvEntry doubleDataEntry = new DoubleDataEntry(key, 23.0); + KvEntry stringDataEntry = new StringDataEntry(key, "stringValue"); + KvEntry jsonDataEntry = new JsonDataEntry(key, "jsonValue"); + List protoList = List.of( + new BasicTsKvEntry(ts, booleanDataEntry), + new BasicTsKvEntry(ts, longDataEntry), + new BasicTsKvEntry(ts, doubleDataEntry), + new BasicTsKvEntry(ts, stringDataEntry), + new BasicTsKvEntry(ts, jsonDataEntry) + ); + assertThat(KvProtoUtil.fromProtoList(KvProtoUtil.toProtoList(protoList))).as("deserialized").isEqualTo(protoList); + + protoList = List.of( + new AggTsKvEntry(ts, booleanDataEntry, 3), + new AggTsKvEntry(ts, longDataEntry, 5), + new AggTsKvEntry(ts, doubleDataEntry, 2), + new AggTsKvEntry(ts, stringDataEntry, 1), + new AggTsKvEntry(ts, jsonDataEntry, 0) + ); + assertThat(KvProtoUtil.fromProtoList(KvProtoUtil.toProtoList(protoList))).as("deserialized").isEqualTo(protoList); + } + + @Test + void protoListAttributeKvSerialization() { + String key = "key"; + long ts = System.currentTimeMillis(); + KvEntry booleanDataEntry = new BooleanDataEntry(key, true); + KvEntry longDataEntry = new LongDataEntry(key, 23L); + KvEntry doubleDataEntry = new DoubleDataEntry(key, 23.0); + KvEntry stringDataEntry = new StringDataEntry(key, "stringValue"); + KvEntry jsonDataEntry = new JsonDataEntry(key, "jsonValue"); + List protoList = List.of( + new BaseAttributeKvEntry(ts, booleanDataEntry), + new BaseAttributeKvEntry(ts, longDataEntry), + new BaseAttributeKvEntry(ts, doubleDataEntry), + new BaseAttributeKvEntry(ts, stringDataEntry), + new BaseAttributeKvEntry(ts, jsonDataEntry) + ); + assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(protoList))).as("deserialized").isEqualTo(protoList); + } + +} From 11b8d3c41ecad4871e8f6f44b2adaea774d00515 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 22 Mar 2024 13:06:58 +0200 Subject: [PATCH 032/107] added global queue prefix for pubsub queue factory --- .../provider/PubSubMonolithQueueFactory.java | 30 +++++++++---------- .../provider/PubSubTbCoreQueueFactory.java | 24 +++++++-------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index c7c59974cd..61c2614fc7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -109,40 +109,40 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, configuration.getTopic(), + return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -155,7 +155,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -168,13 +168,13 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), + return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -202,29 +202,29 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic()); + return new TbPubSubProducerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index a6b9e44022..63975e3116 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -100,32 +100,32 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -138,13 +138,13 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic(), + return new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getResponsesTopic()); + return new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -172,24 +172,24 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override From d9669c9391146b402821f6fc872418ecbe0f3686 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 22 Mar 2024 14:05:25 +0200 Subject: [PATCH 033/107] Fix TbSubscriptionUtils order for matching KeyValueType and DataType --- .../subscription/TbSubscriptionUtils.java | 28 ++++++++++++---- .../server/utils/TbSubscriptionUtilsTest.java | 33 +++++++++++++++++++ .../server/common/data/EntityType.java | 3 +- .../server/common/data/kv/DataType.java | 15 ++++++++- 4 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/utils/TbSubscriptionUtilsTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index 24d54109be..4c3a1b2351 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -36,7 +36,6 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; @@ -54,6 +53,7 @@ import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscript import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -62,6 +62,14 @@ import java.util.UUID; public class TbSubscriptionUtils { + private static final DataType[] dataTypeByProtoNumber; + + static { + int arraySize = Arrays.stream(DataType.values()).mapToInt(DataType::getProtoNumber).max().orElse(0); + dataTypeByProtoNumber = new DataType[arraySize + 1]; + Arrays.stream(DataType.values()).forEach(dataType -> dataTypeByProtoNumber[dataType.getProtoNumber()] = dataType); + } + public static ToCoreMsg toSubEventProto(String serviceId, TbEntitySubEvent event) { SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); var builder = TbEntitySubEventProto.newBuilder() @@ -235,7 +243,7 @@ public class TbSubscriptionUtils { private static TsKvProto.Builder toKeyValueProto(long ts, KvEntry attr) { KeyValueProto.Builder dataBuilder = KeyValueProto.newBuilder(); dataBuilder.setKey(attr.getKey()); - dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); + dataBuilder.setType(toProto(attr.getDataType())); switch (attr.getDataType()) { case BOOLEAN: attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); @@ -259,7 +267,7 @@ public class TbSubscriptionUtils { private static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); dataBuilder.setTs(ts); - dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); + dataBuilder.setType(toProto(attr.getDataType())); switch (attr.getDataType()) { case BOOLEAN: attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); @@ -299,8 +307,7 @@ public class TbSubscriptionUtils { private static KvEntry getKvEntry(KeyValueProto proto) { KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { + switch (fromProto(proto.getType())) { case BOOLEAN: entry = new BooleanDataEntry(proto.getKey(), proto.getBoolV()); break; @@ -328,8 +335,7 @@ public class TbSubscriptionUtils { private static KvEntry getKvEntry(String key, TransportProtos.TsValueProto proto) { KvEntry entry = null; - DataType type = DataType.values()[proto.getType().getNumber()]; - switch (type) { + switch (fromProto(proto.getType())) { case BOOLEAN: entry = new BooleanDataEntry(key, proto.getBoolV()); break; @@ -448,4 +454,12 @@ public class TbSubscriptionUtils { return ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg(result).build(); } + public static TransportProtos.KeyValueType toProto(DataType dataType) { + return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber()); + } + + public static DataType fromProto(TransportProtos.KeyValueType keyValueType) { + return dataTypeByProtoNumber[keyValueType.getNumber()]; + } + } diff --git a/application/src/test/java/org/thingsboard/server/utils/TbSubscriptionUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/TbSubscriptionUtilsTest.java new file mode 100644 index 0000000000..a3f1f0f0fa --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/TbSubscriptionUtilsTest.java @@ -0,0 +1,33 @@ +/** + * 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.utils; + +import org.junit.Test; +import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.service.subscription.TbSubscriptionUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TbSubscriptionUtilsTest { + + @Test + public void protoDataTypeSerialization() { + for (DataType dataType : DataType.values()) { + assertThat(TbSubscriptionUtils.fromProto(TbSubscriptionUtils.toProto(dataType))).as(dataType.name()).isEqualTo(dataType); + } + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index fb4fe1011e..3580a86bcc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -26,6 +26,7 @@ import java.util.stream.Collectors; * @author Andrew Shvayka */ public enum EntityType { + TENANT(1), CUSTOMER(2), USER(3), @@ -63,7 +64,7 @@ public enum EntityType { @Getter private final int protoNumber; // Corresponds to EntityTypeProto - private EntityType(int protoNumber) { + EntityType(int protoNumber) { this.protoNumber = protoNumber; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java index a9d857ca9e..a8ad9b42c1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java @@ -15,8 +15,21 @@ */ package org.thingsboard.server.common.data.kv; +import lombok.Getter; + public enum DataType { - STRING, LONG, BOOLEAN, DOUBLE, JSON; + BOOLEAN(0), + LONG(1), + DOUBLE(2), + STRING(3), + JSON(4); + + @Getter + private final int protoNumber; // Corresponds to KeyValueType + + DataType(int protoNumber) { + this.protoNumber = protoNumber; + } } From 8bd2c1c0c4c15fca30347db82f3bbd86de4d0232 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 22 Mar 2024 17:53:40 +0200 Subject: [PATCH 034/107] UI: Fixed mask for older version chrome --- .../widget/lib/gateway/device-gateway-command.component.scss | 2 ++ .../widget/lib/indicator/battery-level-widget.component.scss | 5 +++++ .../device/device-check-connectivity-dialog.component.scss | 2 ++ .../home/pages/edge/edge-instructions-dialog.component.scss | 2 ++ ui-ngx/src/form.scss | 4 ++++ 5 files changed, 15 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss index 3425065ca2..3f66b67943 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss @@ -63,7 +63,9 @@ height: 18px; background: #305680; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss index 444417fb09..a85d7d694b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss @@ -73,8 +73,11 @@ position: absolute; inset: 0; mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; mask-size: cover; + -webkit-mask-size: cover; mask-position: center; + -webkit-mask-position: center; } .tb-battery-level-container { position: absolute; @@ -95,6 +98,7 @@ &.vertical { .tb-battery-level-shape { mask-image: url(/assets/widget/battery-level/battery-shape-vertical.svg); + -webkit-mask-image: url(/assets/widget/battery-level/battery-shape-vertical.svg); } .tb-battery-level-container { flex-direction: column-reverse; @@ -122,6 +126,7 @@ &.horizontal { .tb-battery-level-shape { mask-image: url(/assets/widget/battery-level/battery-shape-horizontal.svg); + -webkit-mask-image: url(/assets/widget/battery-level/battery-shape-horizontal.svg); } .tb-battery-level-container { inset: 6.25% 8.85% 6.25% 3.54%; diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss index f7ed839541..ef5a1c0ff4 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss @@ -155,7 +155,9 @@ height: 18px; background: #305680; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } } diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss index 4ff80e2c21..b28b28863e 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss @@ -92,7 +92,9 @@ height: 18px; background: $tb-primary-color; mask-image: url(/assets/copy-code-icon.svg); + -webkit-mask-image: url(/assets/copy-code-icon.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; } } &.multiline { diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index e2390c795d..f4d3cdb398 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -588,9 +588,13 @@ bottom: 0; background: $tb-primary-color; mask-image: url(/assets/home/no_data_folder_bg.svg); + -webkit-mask-image: url(/assets/home/no_data_folder_bg.svg); mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; mask-size: contain; + -webkit-mask-size: contain; mask-position: center; + -webkit-mask-position: center; } } From 03c30791009d71e0a5752577eac47c5b1e5d4ff7 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 20 Mar 2024 12:50:50 +0200 Subject: [PATCH 035/107] UI: Fixed some time new time windows intervals not apply --- ui-ngx/.eslintrc.json | 3 +- .../components/time/timeinterval.component.ts | 28 +++++++++++-------- .../time/timewindow-panel.component.ts | 1 - 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/ui-ngx/.eslintrc.json b/ui-ngx/.eslintrc.json index cf9f0e8238..764733e4d7 100644 --- a/ui-ngx/.eslintrc.json +++ b/ui-ngx/.eslintrc.json @@ -51,7 +51,8 @@ "import/order": "off", "@typescript-eslint/member-ordering": "off", "no-underscore-dangle": "off", - "@typescript-eslint/naming-convention": "off" + "@typescript-eslint/naming-convention": "off", + "jsdoc/newline-after-description": 0 } }, { diff --git a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts index 8775b0a6ed..ad6b489ade 100644 --- a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts +++ b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts @@ -21,6 +21,7 @@ import { coerceNumberProperty } from '@angular/cdk/coercion'; import { SubscriptSizing } from '@angular/material/form-field'; import { coerceBoolean } from '@shared/decorators/coercion'; import { Interval, IntervalMath, TimeInterval } from '@shared/models/time/time.models'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-timeinterval', @@ -90,14 +91,14 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { secs = 0; interval: Interval = 0; - modelValue: Interval; + intervals: Array; advanced = false; - rendered = false; - intervals: Array; + private modelValue: Interval; + private rendered = false; - private propagateChange = (_: any) => {}; + private propagateChange: (value: any) => void = () => {}; constructor(private timeService: TimeService) { } @@ -108,6 +109,9 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { registerOnChange(fn: any): void { this.propagateChange = fn; + if (isDefined(this.modelValue) && this.rendered) { + this.propagateChange(this.modelValue); + } } registerOnTouched(fn: any): void { @@ -132,7 +136,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - setInterval(interval: Interval) { + private setInterval(interval: Interval) { if (!this.advanced) { this.interval = interval; } @@ -143,7 +147,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.secs = intervalSeconds % 60; } - boundInterval(updateToPreferred = false) { + private boundInterval(updateToPreferred = false) { const min = this.timeService.boundMinInterval(this.minValue); const max = this.timeService.boundMaxInterval(this.maxValue); this.intervals = this.timeService.getIntervals(this.minValue, this.maxValue, this.useCalendarIntervals); @@ -165,7 +169,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - updateView(updateToPreferred = false) { + private updateView(updateToPreferred = false) { if (!this.rendered) { return; } @@ -187,7 +191,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.boundInterval(updateToPreferred); } - calculateIntervalMs(): number { + private calculateIntervalMs(): number { return (this.days * 86400 + this.hours * 3600 + this.mins * 60 + @@ -232,7 +236,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { } } - onSecsChange() { + private onSecsChange() { if (typeof this.secs === 'undefined') { return; } @@ -252,7 +256,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onMinsChange() { + private onMinsChange() { if (typeof this.mins === 'undefined') { return; } @@ -272,7 +276,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onHoursChange() { + private onHoursChange() { if (typeof this.hours === 'undefined') { return; } @@ -292,7 +296,7 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { this.updateView(); } - onDaysChange() { + private onDaysChange() { if (typeof this.days === 'undefined') { return; } diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts index 0c0dd4294c..857d2c7cb7 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.ts @@ -20,7 +20,6 @@ import { AggregationType, DAY, HistoryWindowType, - QuickTimeInterval, quickTimeIntervalPeriod, RealtimeWindowType, Timewindow, From 7a43ae996606f34bf4ffdb95cca9306c183aa3fe Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 22 Mar 2024 18:29:11 +0200 Subject: [PATCH 036/107] UI: Fixed power button widget for firefox --- .../lib/rpc/power-button-widget.models.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts index 51a427d23b..656ba75fa1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -444,7 +444,7 @@ class InnerShadowCircle { add.x('-50%').y('-50%').width('200%').height('200%'); let effect: Effect = add.componentTransfer(components => { components.funcA({ type: 'table', tableValues: '1 0' }); - }).in(add.$fill); + }); effect = effect.gaussianBlur(this.blur, this.blur).attr({stdDeviation: this.blur}); this.blurEffect = effect; effect = effect.offset(this.dx, this.dy); @@ -454,7 +454,7 @@ class InnerShadowCircle { effect = effect.composite(this.offsetEffect, 'in'); effect.composite(add.$sourceAlpha, 'in'); add.merge(m => { - m.mergeNode(add.$fill); + m.mergeNode(); m.mergeNode(); }); }); @@ -538,14 +538,14 @@ class DefaultPowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } @@ -602,14 +602,14 @@ class SimplifiedPowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } } @@ -674,7 +674,7 @@ class OutlinedPowerButtonShape extends PowerButtonShape { const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } @@ -682,7 +682,7 @@ class OutlinedPowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } } @@ -774,14 +774,14 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { this.innerShadow.show(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.innerShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.innerShadow.animateRestore().after(() => { if (this.disabled) { this.innerShadow.hide(); @@ -849,14 +849,14 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { this.backgroundShape.removeClass('tb-shadow'); } powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onCenterGroup).transform({scale: pressedScale}); + powerButtonAnimation(this.onCenterGroup).transform({scale: pressedScale, origin: {x: cx, y: cy}}); this.pressedShadow.animate(8, 0.4); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); + powerButtonAnimation(this.onCenterGroup).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore().after(() => { if (!this.value) { this.backgroundShape.addClass('tb-shadow'); @@ -938,7 +938,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98}); + powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98, origin: {x: cx, y: cy}}); this.pressedShadow.animate(6, 0.6); } @@ -946,7 +946,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1}); + powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); this.pressedShadow.animateRestore(); } From 8423eac5979183a94ef34dafdacb105f294401b8 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 21 Mar 2024 10:14:48 +0200 Subject: [PATCH 037/107] UI: Improved save value in propagateChange --- .../app/shared/components/time/timeinterval.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts index ad6b489ade..8ed6fb2034 100644 --- a/ui-ngx/src/app/shared/components/time/timeinterval.component.ts +++ b/ui-ngx/src/app/shared/components/time/timeinterval.component.ts @@ -97,8 +97,11 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { private modelValue: Interval; private rendered = false; + private propagateChangeValue: any; - private propagateChange: (value: any) => void = () => {}; + private propagateChange = (value: any) => { + this.propagateChangeValue = value; + }; constructor(private timeService: TimeService) { } @@ -109,8 +112,8 @@ export class TimeintervalComponent implements OnInit, ControlValueAccessor { registerOnChange(fn: any): void { this.propagateChange = fn; - if (isDefined(this.modelValue) && this.rendered) { - this.propagateChange(this.modelValue); + if (isDefined(this.propagateChangeValue)) { + this.propagateChange(this.propagateChangeValue); } } From 8910b4b1dfd04319c820ba6fb8d6a7d2ff5bf177 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 22 Mar 2024 20:02:51 +0200 Subject: [PATCH 038/107] Swagger docs Open API 3.1 --- application/pom.xml | 2 +- .../server/config/SwaggerConfiguration.java | 402 +++++++++++++----- .../thingsboard/server/config/WebConfig.java | 5 + .../server/controller/AdminController.java | 6 +- .../controller/AlarmCommentController.java | 7 +- .../server/controller/AlarmController.java | 29 +- .../server/controller/AssetController.java | 39 +- .../controller/AssetProfileController.java | 24 +- .../server/controller/AuditLogController.java | 12 +- .../controller/DashboardController.java | 61 +-- .../server/controller/DeviceController.java | 9 +- .../controller/DeviceProfileController.java | 27 +- .../server/controller/EdgeController.java | 53 +-- .../controller/EdgeEventController.java | 2 +- .../controller/EntityRelationController.java | 25 +- .../controller/EntityViewController.java | 18 +- .../server/controller/EventController.java | 7 +- .../server/controller/Lwm2mController.java | 3 +- .../controller/OtaPackageController.java | 19 +- .../controller/RuleChainController.java | 8 +- .../controller/TbResourceController.java | 21 +- .../controller/TelemetryController.java | 51 +-- .../src/main/resources/thingsboard.yml | 2 + .../common/data/alarm/AlarmComment.java | 11 +- .../server/common/data/id/EntityId.java | 4 +- .../server/common/data/id/UserId.java | 3 +- .../transport/http/DeviceApiController.java | 42 +- pom.xml | 8 +- 28 files changed, 476 insertions(+), 424 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index adc3f1e801..902b541566 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -262,7 +262,7 @@ opensmpp-core - org.springdoc + org.thingsboard springdoc-openapi-starter-webmvc-ui diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 21255bf1f7..963205c08b 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -15,12 +15,17 @@ */ package org.thingsboard.server.config; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.examples.Example; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; @@ -33,29 +38,42 @@ import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; +import org.springdoc.core.discoverer.SpringDocParameterNameDiscoverer; import org.springdoc.core.models.GroupedOpenApi; +import org.springdoc.core.properties.SpringDocConfigProperties; import org.springdoc.core.properties.SwaggerUiConfigProperties; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; +import org.springframework.core.MethodParameter; import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.RequestParam; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse; import org.thingsboard.server.exception.ThingsboardErrorResponse; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.LoginRequest; import org.thingsboard.server.service.security.auth.rest.LoginResponse; -import java.util.Collections; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -67,8 +85,14 @@ public class SwaggerConfiguration { public static final String LOGIN_ENDPOINT = "/api/auth/login"; + private static final ApiResponses loginResponses = loginResponses(); + private static final ApiResponses defaultErrorResponses = defaultErrorResponses(false); + private static final ApiResponses defaultPostErrorResponses = defaultErrorResponses(true); + @Value("${swagger.api_path:/api/**}") private String apiPath; + @Value("${swagger.security_path_regex}") + private String securityPathRegex; @Value("${swagger.non_security_path_regex}") private String nonSecurityPathRegex; @Value("${swagger.title}") @@ -90,6 +114,7 @@ public class SwaggerConfiguration { @Value("${app.version:unknown}") private String appVersion; + @Bean public OpenAPI thingsboardApi() { Contact contact = new Contact() @@ -115,93 +140,24 @@ public class SwaggerConfiguration { SecurityScheme securityScheme = new SecurityScheme() .type(SecurityScheme.Type.HTTP) - .scheme("bearer") - .bearerFormat("JWT") - .in(SecurityScheme.In.HEADER) - .description("Enter Username / Password"); + .description("Enter Username / Password") + .scheme("loginPassword") + .bearerFormat("/api/auth/login|X-Authorization"); var openApi = new OpenAPI() - .addServersItem(new Server().url("/").description("Default Server URL")) .components(new Components().addSecuritySchemes("HTTP login form", securityScheme)) .info(info); + addDefaultSchemas(openApi); addLoginOperation(openApi); return openApi; } - public void addLoginOperation(OpenAPI openAPI) { - openAPI.getComponents() - .addSchemas("LoginRequest", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginRequest.class)).schema) - .addSchemas("LoginResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginResponse.class)).schema) - .addSchemas("ThingsboardErrorResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardErrorResponse.class)).schema) - .addSchemas("ThingsboardCredentialsExpiredResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardCredentialsExpiredResponse.class)).schema); - - var operation = new Operation(); - operation.summary("Login method to get user JWT token data"); - operation.description("Login method used to authenticate user and get JWT token data.\n\nValue of the response **token** " + - "field can be used as **X-Authorization** header value:\n\n`X-Authorization: Bearer $JWT_TOKEN_VALUE`."); - var requestBody = new RequestBody().content(new Content().addMediaType(APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest")))); - operation.requestBody(requestBody); - operation.responses(getResponses()); - operation.addTagsItem("login-endpoint"); - var pathItem = new PathItem().post(operation); - openAPI.path(LOGIN_ENDPOINT, pathItem); - } - - private ApiResponses getResponses() { - ApiResponses apiResponses = new ApiResponses(); - - apiResponses.addApiResponse("200", new ApiResponse().description("OK") - .content(new Content().addMediaType(APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().$ref("#/components/schemas/LoginResponse"))))); - - ApiResponse unauthorizedResponse = new ApiResponse().description("Unauthorized"); - Content content = new Content(); - MediaType mediaType = new MediaType().schema(new Schema().$ref("#/components/schemas/ThingsboardErrorResponse")); - - Map examples = Map.of( - "bad-credentials", errorExample("Bad credentials", - ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), - "token-expired", errorExample("JWT token expired", - ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)), - "account-disabled", errorExample("Disabled account", - ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), - "account-locked", errorExample("Locked account", - ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), - "authentication-failed", errorExample("General authentication error", - ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)) - ); - - mediaType.setExamples(examples); - content.addMediaType(APPLICATION_JSON_VALUE, mediaType); - unauthorizedResponse.setContent(content); - apiResponses.addApiResponse("401", unauthorizedResponse); - - ApiResponse expiredCredentialsResponse = new ApiResponse().description("Unauthorized (**Expired credentials**)"); - Content expiredContent = new Content(); - MediaType expiredMediaType = new MediaType().schema(new Schema().$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse")); - expiredMediaType.addExamples("credentials-expired", errorExample("Expired credentials", - ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30)))); - expiredContent.addMediaType(APPLICATION_JSON_VALUE, expiredMediaType); - expiredCredentialsResponse.setContent(expiredContent); - apiResponses.addApiResponse("401 ", expiredCredentialsResponse); - - return apiResponses; - } - - private Example errorExample(String summary, ThingsboardErrorResponse example) { - return new Example() - .summary(summary) - .value(example); - } - @Bean - public GroupedOpenApi groupedApi() { - return GroupedOpenApi.builder() - .group("thingsboard") - .pathsToMatch(apiPath) - .addOpenApiCustomizer(customOpenApiCustomizer()) - .build(); + @Primary + public SpringDocConfigProperties springDocConfig(SpringDocConfigProperties springDocProperties) { + springDocProperties.getApiDocs().setVersion(SpringDocConfigProperties.ApiDocs.OpenApiVersion.OPENAPI_3_1); + springDocProperties.setRemoveBrokenReferenceDefinitions(false); + return springDocProperties; } @Bean @@ -232,35 +188,166 @@ public class SwaggerConfiguration { return uiProperties; } - public OpenApiCustomizer customOpenApiCustomizer() { - var loginForm = new SecurityRequirement().addList("HTTP login form"); - return openAPI -> openAPI.getPaths().entrySet().stream().peek(entry -> { - securityCustomization(loginForm, entry); - if (!entry.getKey().equals(LOGIN_ENDPOINT)) { - defaultErrorResponsesCustomization(entry.getValue()); + private void addLoginOperation(OpenAPI openAPI) { + var operation = new Operation(); + operation.summary("Login method to get user JWT token data"); + operation.description(""" + Login method used to authenticate user and get JWT token data. + + Value of the response **token** field can be used as **X-Authorization** header value: + + `X-Authorization: Bearer $JWT_TOKEN_VALUE`."""); + var requestBody = new RequestBody().description("Login request") + .content(new Content().addMediaType(APPLICATION_JSON_VALUE, + new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest")))); + operation.requestBody(requestBody); + + operation.responses(loginResponses); + + operation.addTagsItem("login-endpoint"); + var pathItem = new PathItem().post(operation); + openAPI.path(LOGIN_ENDPOINT, pathItem); + } + + @Bean + public GroupedOpenApi groupedApi(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer) { + return GroupedOpenApi.builder() + .group("thingsboard") + .pathsToMatch(apiPath) + .addRouterOperationCustomizer(routerOperationCustomizer(localSpringDocParameterNameDiscoverer)) + .addOperationCustomizer(operationCustomizer()) + .addOpenApiCustomizer(customOpenApiCustomizer()) + .build(); + } + + @Bean + @Lazy(false) + ModelConverter mapAwareConverter() { + return (type, context, chain) -> { + if (chain.hasNext()) { + Schema schema = chain.next().resolve(type, context, chain); + JavaType javaType = Json.mapper().constructType(type.getType()); + if (javaType != null) { + Class cls = javaType.getRawClass(); + if (Map.class.isAssignableFrom(cls)) { + if (schema != null && schema.getProperties() != null) { + schema.getProperties().remove("empty"); + if (schema.getProperties().isEmpty()) { + schema.setProperties(null); + } + } + } + } + return schema; + } else { + return null; } - }).map(this::tagsCustomization).forEach(openAPI::addTagsItem); + }; } - private Tag tagsCustomization(Map.Entry entry) { - var operations = entry.getValue().readOperationsMap().values(); - var tagItem = operations.stream().findAny().get().getTags().get(0); - return tagFromTagItem(tagItem); + private void addDefaultSchemas(OpenAPI openAPI) { + var jsonNodeSchema = ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(JsonNode.class)).schema; + jsonNodeSchema.setType("any"); + //noinspection unchecked + jsonNodeSchema.setExamples(List.of(JacksonUtil.newObjectNode())); + jsonNodeSchema.setDescription("A value representing the any type (object or primitive)"); + openAPI.getComponents() + .addSchemas("JsonNode", jsonNodeSchema) + .addSchemas("LoginRequest", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginRequest.class)).schema) + .addSchemas("LoginResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginResponse.class)).schema) + .addSchemas("ThingsboardErrorResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardErrorResponse.class)).schema) + .addSchemas("ThingsboardCredentialsExpiredResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardCredentialsExpiredResponse.class)).schema); } - private void defaultErrorResponsesCustomization(PathItem pathItem) { - pathItem.readOperationsMap().forEach(((httpMethod, operation) -> { - operation.setResponses(getResponses(operation.getResponses(), httpMethod.equals(PathItem.HttpMethod.POST))); - })); + private RouterOperationCustomizer routerOperationCustomizer(SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer) { + return (routerOperation, handlerMethod) -> { + String[] pNames = localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod()); + String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new); + if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull)) + pNames = reflectionParametersNames; + MethodParameter[] parameters = handlerMethod.getMethodParameters(); + List requestParams = new ArrayList<>(); + for (var i = 0; i < parameters.length; i++) { + var methodParameter = parameters[i]; + RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class); + if (requestParam != null) { + String pName = StringUtils.isNotBlank(requestParam.value()) ? requestParam.value() : + pNames[i]; + if (StringUtils.isNotBlank(pName)) { + requestParams.add(pName); + } + } + } + if (!requestParams.isEmpty()) { + var path = routerOperation.getPath() + "{?" + String.join(",", requestParams) + "}"; + routerOperation.setPath(path); + } + return routerOperation; + }; } - private void securityCustomization(SecurityRequirement loginForm, Map.Entry entry) { - if (!(entry.getKey().matches(nonSecurityPathRegex) || entry.getKey().equals(LOGIN_ENDPOINT))) { - entry.getValue() - .readOperationsMap() - .values() - .forEach(operation -> operation.addSecurityItem(loginForm)); + private OperationCustomizer operationCustomizer() { + return (operation, handlerMethod) -> { + if (StringUtils.isBlank(operation.getSummary())) { + operation.setSummary(operation.getOperationId()); + } + return operation; + }; + } + + private OpenApiCustomizer customOpenApiCustomizer() { + var loginForm = new SecurityRequirement().addList("HTTP login form", Arrays.asList( + Authority.SYS_ADMIN.name(), + Authority.TENANT_ADMIN.name(), + Authority.CUSTOMER_USER.name() + )); + return openAPI -> { + var paths = openAPI.getPaths(); + paths.entrySet().stream().peek(entry -> { + securityCustomization(loginForm, entry); + if (!entry.getKey().equals(LOGIN_ENDPOINT)) { + defaultErrorResponsesCustomization(entry.getValue()); + } + }).map(this::tagsCustomization).filter(Objects::nonNull).distinct().sorted(Comparator.comparing(Tag::getName)).forEach(openAPI::addTagsItem); + + var pathItemsByTags = new TreeMap>(); + paths.forEach((k, v) -> { + var tagItem = tagItemFromPathItem(v); + if (tagItem != null) { + var pathItemMap = pathItemsByTags.computeIfAbsent(tagItem, k1 -> new TreeMap<>()); + pathItemMap.put(k, v); + } + }); + var sortedPaths = new Paths(); + pathItemsByTags.forEach((tagItem, pathItemMap) -> { + pathItemMap.forEach(sortedPaths::addPathItem); + }); + sortedPaths.setExtensions(paths.getExtensions()); + openAPI.setPaths(sortedPaths); + var sortedSchemas = new TreeMap<>(openAPI.getComponents().getSchemas()); + openAPI.getComponents().setSchemas(new LinkedHashMap<>(sortedSchemas)); + }; + } + + + private Tag tagsCustomization(Map.Entry entry) { + var tagItem = tagItemFromPathItem(entry.getValue()); + if (tagItem != null) { + return tagFromTagItem(tagItem); } + return null; + } + + private String tagItemFromPathItem(PathItem item) { + var operations = item.readOperationsMap().values(); + var operation = operations.stream().findAny(); + if (operation.isPresent()) { + var tags = operation.get().getTags(); + if (tags != null && !tags.isEmpty()) { + return tags.get(0); + } + } + return null; } private Tag tagFromTagItem(String tagItem) { @@ -276,32 +363,113 @@ public class SwaggerConfiguration { return new Tag().name(tagItem).description(sb.toString().trim()); } - private ApiResponses getResponses(ApiResponses apiResponses, boolean isPost) { - if (apiResponses == null) { - apiResponses = new ApiResponses(); + private void defaultErrorResponsesCustomization(PathItem pathItem) { + pathItem.readOperationsMap().forEach(((httpMethod, operation) -> { + var errorResponses = httpMethod.equals(PathItem.HttpMethod.POST) ? defaultPostErrorResponses : defaultErrorResponses; + var responses = operation.getResponses(); + if (responses == null) { + responses = errorResponses; + } else { + ApiResponses updated = responses; + errorResponses.forEach((key, apiResponse) -> { + if (!updated.containsKey(key)) { + updated.put(key, apiResponse); + } + }); + } + operation.setResponses(responses); + })); + } + + private void securityCustomization(SecurityRequirement loginForm, Map.Entry entry) { + var path = entry.getKey(); + if (path.matches(securityPathRegex) && !path.matches(nonSecurityPathRegex) && !path.equals(LOGIN_ENDPOINT)) { + entry.getValue() + .readOperationsMap() + .values() + .forEach(operation -> operation.addSecurityItem(loginForm)); } + } - apiResponses.addApiResponse("400", new ApiResponse().description("Bad Request") - .content(getErrorContent(ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST)))); + private static ApiResponses loginResponses() { + ApiResponses apiResponses = new ApiResponses(); + apiResponses.addApiResponse("200", new ApiResponse().description("OK") + .content(new Content().addMediaType(APPLICATION_JSON_VALUE, + new MediaType().schema(new Schema().$ref("#/components/schemas/LoginResponse"))))); + apiResponses.putAll(loginErrorResponses()); + return apiResponses; + } + + private static ApiResponses defaultErrorResponses(boolean isPost) { + ApiResponses apiResponses = new ApiResponses(); + apiResponses.addApiResponse("400", errorResponse("400", "Bad Request", + ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST))); - apiResponses.addApiResponse("401", new ApiResponse().description("Unauthorized") - .content(getErrorContent(ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)))); + apiResponses.addApiResponse("401", errorResponse("401", "Unauthorized", + ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))); - apiResponses.addApiResponse("403", new ApiResponse().description("Forbidden") - .content(getErrorContent(ThingsboardErrorResponse.of("You don't have permission to perform this operation!", ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)))); + apiResponses.addApiResponse("403", errorResponse("403", "Forbidden", + ThingsboardErrorResponse.of("You don't have permission to perform this operation!", ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN))); - apiResponses.addApiResponse("404", new ApiResponse().description("Not Found") - .content(getErrorContent(ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND)))); + apiResponses.addApiResponse("404", errorResponse("404", "Not Found", + ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND))); - apiResponses.addApiResponse("429", new ApiResponse().description("Too Many Requests") - .content(getErrorContent(ThingsboardErrorResponse.of("Too many requests for current tenant!", ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS)))); + apiResponses.addApiResponse("429", errorResponse("429", "Too Many Requests", + ThingsboardErrorResponse.of("Too many requests for current tenant!", ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))); return apiResponses; } - private Content getErrorContent(ThingsboardErrorResponse errorResponse) { - return new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE, - new MediaType().schema(new Schema().example(errorResponse))); + private static ApiResponses loginErrorResponses() { + ApiResponses apiResponses = new ApiResponses(); + + apiResponses.addApiResponse("401", errorResponse("Unauthorized", + Map.of( + "bad-credentials", errorExample("Bad credentials", + ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), + "token-expired", errorExample("JWT token expired", + ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)), + "account-disabled", errorExample("Disabled account", + ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), + "account-locked", errorExample("Locked account", + ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)), + "authentication-failed", errorExample("General authentication error", + ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)) + ) + )); + var credentialsExpiredSchema = new Schema(); + credentialsExpiredSchema.$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse"); + apiResponses.addApiResponse("401 ", errorResponse("Unauthorized (**Expired credentials**)", + Map.of( + "credentials-expired", errorExample("Expired credentials", + ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30))) + ), + credentialsExpiredSchema + )); + return apiResponses; + } + + private static ApiResponse errorResponse(String code, String description, ThingsboardErrorResponse example) { + return errorResponse(description, Map.of("error-code-" + code, errorExample(description, example))); + } + + private static ApiResponse errorResponse(String description, Map examples) { + var schema = new Schema(); + schema.$ref("#/components/schemas/ThingsboardErrorResponse"); + return errorResponse(description, examples, schema); + } + + private static ApiResponse errorResponse(String description, Map examples, Schema errorResponseSchema) { + MediaType mediaType = new MediaType().schema(errorResponseSchema); + mediaType.setExamples(examples); + Content content = new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE, mediaType); + return new ApiResponse().description(description).content(content); + } + + private static Example errorExample(String summary, ThingsboardErrorResponse example) { + return new Example() + .summary(summary) + .value(example); } } diff --git a/application/src/main/java/org/thingsboard/server/config/WebConfig.java b/application/src/main/java/org/thingsboard/server/config/WebConfig.java index 81597952c0..70afc8b99c 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebConfig.java +++ b/application/src/main/java/org/thingsboard/server/config/WebConfig.java @@ -37,4 +37,9 @@ public class WebConfig { response.sendRedirect(baseUrl + "/swagger-ui/"); } + @RequestMapping("/swagger-ui/") + public String redirectSwaggerIndex() throws IOException { + return "forward:/swagger-ui/index.html"; + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 739f386dc9..818a2a15a6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -184,8 +184,7 @@ public class AdminController extends BaseController { } @ApiOperation(value = "Get the JWT Settings object (getJwtSettings)", - notes = "Get the JWT Settings object that contains JWT token policy, etc. " + SYSTEM_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get the JWT Settings object that contains JWT token policy, etc. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/jwtSettings", method = RequestMethod.GET) @ResponseBody @@ -195,8 +194,7 @@ public class AdminController extends BaseController { } @ApiOperation(value = "Update JWT Settings (saveJwtSettings)", - notes = "Updates the JWT Settings object that contains JWT token policy, etc. The tokenSigningKey field is a Base64 encoded string." + SYSTEM_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Updates the JWT Settings object that contains JWT token policy, etc. The tokenSigningKey field is a Base64 encoded string." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/jwtSettings", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java index cca82f5ee5..65f5c1c543 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -69,8 +69,7 @@ public class AlarmCommentController extends BaseController { "Referencing non-existing Alarm Comment Id will cause 'Not Found' error. " + "\n\n To create new Alarm comment entity it is enough to specify 'comment' json element with 'text' node, for example: {\"comment\": { \"text\": \"my comment\"}}. " + "\n\n If comment type is not specified the default value 'OTHER' will be saved. If 'alarmId' or 'userId' specified in body it will be ignored." + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST) @ResponseBody @@ -84,7 +83,7 @@ public class AlarmCommentController extends BaseController { } @ApiOperation(value = "Delete Alarm comment (deleteAlarmComment)", - notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE) @ResponseBody @@ -100,7 +99,7 @@ public class AlarmCommentController extends BaseController { @ApiOperation(value = "Get Alarm comments (getAlarmComments)", notes = "Returns a page of alarm comments for specified alarm. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index d983f08ec2..ba22663467 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -104,7 +104,7 @@ public class AlarmController extends BaseController { "filled in the AlarmInfo object field: 'originatorName' or will returns as null."; @ApiOperation(value = "Get Alarm (getAlarmById)", - notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET) @ResponseBody @@ -117,7 +117,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarm Info (getAlarmInfoById)", notes = "Fetch the Alarm Info object based on the provided Alarm Id. " + - ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET) @ResponseBody @@ -139,7 +139,7 @@ public class AlarmController extends BaseController { "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm", method = RequestMethod.POST) @ResponseBody @@ -155,7 +155,7 @@ public class AlarmController extends BaseController { } @ApiOperation(value = "Delete Alarm (deleteAlarm)", - notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE) @ResponseBody @@ -169,7 +169,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Acknowledge Alarm (ackAlarm)", notes = "Acknowledge the Alarm. " + "Once acknowledged, the 'ack_ts' field will be set to current timestamp and special rule chain event 'ALARM_ACK' will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -184,7 +184,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Clear Alarm (clearAlarm)", notes = "Clear the Alarm. " + "Once cleared, the 'clear_ts' field will be set to current timestamp and special rule chain event 'ALARM_CLEAR' will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -200,7 +200,7 @@ public class AlarmController extends BaseController { notes = "Assign the Alarm. " + "Once assigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_ASSIGNED' " + "(or ALARM_REASSIGNED in case of assigning already assigned alarm) will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/assign/{assigneeId}", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -221,7 +221,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Unassign Alarm (unassignAlarm)", notes = "Unassign the Alarm. " + "Once unassigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_UNASSIGNED' will be generated. " + - "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{alarmId}/assign", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) @@ -236,7 +236,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarms (getAlarms)", notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -292,7 +292,7 @@ public class AlarmController extends BaseController { "If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " + "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarms", method = RequestMethod.GET) @ResponseBody @@ -341,7 +341,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarms (getAlarmsV2)", notes = "Returns a page of alarms for the selected entity. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/v2/alarm/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -407,7 +407,7 @@ public class AlarmController extends BaseController { notes = "Returns a page of alarms that belongs to the current user owner. " + "If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " + "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/v2/alarms", method = RequestMethod.GET) @ResponseBody @@ -468,7 +468,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)", notes = "Search the alarms by originator ('entityType' and entityId') and optional 'status' or 'searchStatus' filters and returns the highest AlarmSeverity(CRITICAL, MAJOR, MINOR, WARNING or INDETERMINATE). " + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -499,8 +499,7 @@ public class AlarmController extends BaseController { } @ApiOperation(value = "Get Alarm Types (getAlarmTypes)", - notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 6f51202c36..751f3fd812 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -103,7 +103,7 @@ public class AssetController extends BaseController { notes = "Fetch the Asset object based on the provided Asset Id. " + "If the user has the authority of 'Tenant Administrator', the server checks that the asset is owned by the same tenant. " + "If the user has the authority of 'Customer User', the server checks that the asset is assigned to the same customer." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.GET) @ResponseBody @@ -118,7 +118,7 @@ public class AssetController extends BaseController { notes = "Fetch the Asset Info object based on the provided Asset Id. " + "If the user has the authority of 'Tenant Administrator', the server checks that the asset is owned by the same tenant. " + "If the user has the authority of 'Customer User', the server checks that the asset is assigned to the same customer. " - + ASSET_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ASSET_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/info/{assetId}", method = RequestMethod.GET) @ResponseBody @@ -135,7 +135,7 @@ public class AssetController extends BaseController { "Specify existing Asset id to update the asset. " + "Referencing non-existing Asset Id will cause 'Not Found' error. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Asset entity. " - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset", method = RequestMethod.POST) @ResponseBody @@ -158,7 +158,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Assign asset to customer (assignAssetToCustomer)", - notes = "Creates assignment of the asset to customer. Customer will be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Creates assignment of the asset to customer. Customer will be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/asset/{assetId}", method = RequestMethod.POST) @ResponseBody @@ -174,7 +174,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Unassign asset from customer (unassignAssetFromCustomer)", - notes = "Clears assignment of the asset to customer. Customer will not be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Clears assignment of the asset to customer. Customer will not be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/asset/{assetId}", method = RequestMethod.DELETE) @ResponseBody @@ -192,7 +192,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Make asset publicly available (assignAssetToPublicCustomer)", notes = "Asset will be available for non-authorized (not logged-in) users. " + "This is useful to create dashboards that you plan to share/embed on a publicly available website. " + - "However, users that are logged-in and belong to different tenant will not be able to access the asset." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "However, users that are logged-in and belong to different tenant will not be able to access the asset." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/asset/{assetId}", method = RequestMethod.POST) @ResponseBody @@ -205,7 +205,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Tenant Assets (getTenantAssets)", notes = "Returns a page of assets owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -233,7 +233,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Tenant Asset Infos (getTenantAssetInfos)", notes = "Returns a page of assets info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -266,7 +266,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Tenant Asset (getTenantAsset)", notes = "Requested asset must be owned by tenant that the user belongs to. " + - "Asset name is an unique property of asset. So it can be used to identify the asset." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Asset name is an unique property of asset. So it can be used to identify the asset." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET) @ResponseBody @@ -279,7 +279,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Customer Assets (getCustomerAssets)", notes = "Returns a page of assets objects assigned to customer. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -312,7 +312,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get Customer Asset Infos (getCustomerAssetInfos)", notes = "Returns a page of assets info objects assigned to customer. " + - PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -349,7 +349,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Get Assets By Ids (getAssetsByIds)", - notes = "Requested assets must be owned by tenant or assigned to customer which user is performing the request. ", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Requested assets must be owned by tenant or assigned to customer which user is performing the request. ") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET) @ResponseBody @@ -376,7 +376,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Find related assets (findByQuery)", notes = "Returns all assets that are related to the specific entity. " + "The entity id, relation type, asset types, depth of the search, and other query parameters defined using complex 'AssetSearchQuery' object. " + - "See 'Model' tab of the Parameters for more info.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assets", method = RequestMethod.POST) @ResponseBody @@ -398,8 +398,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Get Asset Types (getAssetTypes)", - notes = "Deprecated. See 'getAssetProfileNames' API from Asset Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deprecated. See 'getAssetProfileNames' API from Asset Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/types", method = RequestMethod.GET) @ResponseBody @@ -416,8 +415,7 @@ public class AssetController extends BaseController { EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive a copy of assignment asset " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once asset will be delivered to edge service, it's going to be available for usage on remote edge instance.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once asset will be delivered to edge service, it's going to be available for usage on remote edge instance.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST) @ResponseBody @@ -440,8 +438,7 @@ public class AssetController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove asset " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove asset locally.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove asset locally.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE) @ResponseBody @@ -460,7 +457,7 @@ public class AssetController extends BaseController { @ApiOperation(value = "Get assets assigned to edge (getEdgeAssets)", notes = "Returns a page of assets assigned to edge. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -510,7 +507,7 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Import the bulk of assets (processAssetsBulkImport)", - notes = "There's an ability to import the bulk of assets using the only .csv file.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "There's an ability to import the bulk of assets using the only .csv file.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PostMapping("/asset/bulk_import") public BulkImportResult processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java index a6126011cc..a4dc7a1cb0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -78,8 +78,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profile (getAssetProfileById)", notes = "Fetch the Asset Profile object based on the provided Asset Profile Id. " + - "The server checks that the asset profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "The server checks that the asset profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.GET) @ResponseBody @@ -99,8 +98,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profile Info (getAssetProfileInfoById)", notes = "Fetch the Asset Profile Info object based on the provided Asset Profile Id. " - + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assetProfileInfo/{assetProfileId}", method = RequestMethod.GET) @ResponseBody @@ -114,8 +112,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Default Asset Profile (getDefaultAssetProfileInfo)", notes = "Fetch the Default Asset Profile Info object. " + - ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assetProfileInfo/default", method = RequestMethod.GET) @ResponseBody @@ -130,8 +127,7 @@ public class AssetProfileController extends BaseController { "Referencing non-existing asset profile Id will cause 'Not Found' error. " + NEW_LINE + "Asset profile name is unique in the scope of tenant. Only one 'default' asset profile may exist in scope of tenant. " + "Remove 'id', 'tenantId' from the request body example (below) to create new Asset Profile entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile", method = RequestMethod.POST) @ResponseBody @@ -145,8 +141,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Delete asset profile (deleteAssetProfile)", notes = "Deletes the asset profile. Referencing non-existing asset profile Id will cause an error. " + - "Can't delete the asset profile if it is referenced by existing assets." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Can't delete the asset profile if it is referenced by existing assets." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) @@ -160,8 +155,7 @@ public class AssetProfileController extends BaseController { } @ApiOperation(value = "Make Asset Profile Default (setDefaultAssetProfile)", - notes = "Marks asset profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Marks asset profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfile/{assetProfileId}/default", method = RequestMethod.POST) @ResponseBody @@ -177,8 +171,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profiles (getAssetProfiles)", notes = "Returns a page of asset profile objects owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/assetProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -199,8 +192,7 @@ public class AssetProfileController extends BaseController { @ApiOperation(value = "Get Asset Profile infos (getAssetProfileInfos)", notes = "Returns a page of asset profile info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/assetProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index 5e58b28ab0..c8ea4073b1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -74,8 +74,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get audit logs by customer id (getAuditLogsByCustomerId)", notes = "Returns a page of audit logs related to the targeted customer entities (devices, assets, etc.), " + "and users actions (login, logout, etc.) that belong to this customer. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -108,8 +107,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get audit logs by user id (getAuditLogsByUserId)", notes = "Returns a page of audit logs related to the actions of targeted user. " + "For example, RPC call to a particular device, or alarm acknowledgment for a specific device, etc. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -143,8 +141,7 @@ public class AuditLogController extends BaseController { notes = "Returns a page of audit logs related to the actions on the targeted entity. " + "Basically, this API call is used to get the full lifecycle of some specific entity. " + "For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -179,8 +176,7 @@ public class AuditLogController extends BaseController { @ApiOperation(value = "Get all audit logs (getAuditLogs)", notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 0683bc21be..dda88eceb6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -133,9 +133,7 @@ public class DashboardController extends BaseController { } @ApiOperation(value = "Get Dashboard Info (getDashboardInfoById)", - notes = "Get the information about the dashboard based on 'dashboardId' parameter. " + DASHBOARD_INFO_DEFINITION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) - ) + notes = "Get the information about the dashboard based on 'dashboardId' parameter. " + DASHBOARD_INFO_DEFINITION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET) @ResponseBody @@ -148,8 +146,7 @@ public class DashboardController extends BaseController { } @ApiOperation(value = "Get Dashboard (getDashboardById)", - notes = "Get the dashboard based on 'dashboardId' parameter. " + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) + notes = "Get the dashboard based on 'dashboardId' parameter. " + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH ) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.GET) @@ -174,8 +171,7 @@ public class DashboardController extends BaseController { "Specify existing Dashboard id to update the dashboard. " + "Referencing non-existing dashboard Id will cause 'Not Found' error. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Dashboard entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard", method = RequestMethod.POST) @ResponseBody @@ -203,8 +199,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Assign the Dashboard (assignDashboardToCustomer)", notes = "Assign the Dashboard to specified Customer or do nothing if the Dashboard is already assigned to that Customer. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST) @ResponseBody @@ -226,8 +221,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Unassign the Dashboard (unassignDashboardFromCustomer)", notes = "Unassign the Dashboard from specified Customer or do nothing if the Dashboard is already assigned to that Customer. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody @@ -247,8 +241,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Update the Dashboard Customers (updateDashboardCustomers)", notes = "Updates the list of Customers that this Dashboard is assigned to. Removes previous assignments to customers that are not in the provided list. " + - "Returns the Dashboard object. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard/{dashboardId}/customers", method = RequestMethod.POST) @@ -267,8 +260,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Adds the Dashboard Customers (addDashboardCustomers)", notes = "Adds the list of Customers to the existing list of assignments for the Dashboard. Keeps previous assignments to customers that are not in the provided list. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard/{dashboardId}/customers/add", method = RequestMethod.POST) @ResponseBody @@ -286,8 +278,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Remove the Dashboard Customers (removeDashboardCustomers)", notes = "Removes the list of Customers from the existing list of assignments for the Dashboard. Keeps other assignments to customers that are not in the provided list. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/dashboard/{dashboardId}/customers/remove", method = RequestMethod.POST) @ResponseBody @@ -309,8 +300,7 @@ public class DashboardController extends BaseController { "Be aware that making the dashboard public does not mean that it automatically makes all devices and assets you use in the dashboard to be public." + "Use [assign Asset to Public Customer](#!/asset-controller/assignAssetToPublicCustomerUsingPOST) and " + "[assign Device to Public Customer](#!/device-controller/assignDeviceToPublicCustomerUsingPOST) for this purpose. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) @ResponseBody @@ -325,8 +315,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Unassign the Dashboard from Public Customer (unassignDashboardFromPublicCustomer)", notes = "Unassigns the dashboard from a special, auto-generated 'Public' Customer. Once unassigned, unauthenticated users may no longer browse the dashboard. " + - "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody @@ -341,8 +330,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboards)", notes = "Returns a page of dashboard info objects owned by tenant. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + - SYSTEM_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -367,8 +355,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Tenant Dashboards (getTenantDashboards)", notes = "Returns a page of dashboard info objects owned by the tenant of a current user. " - + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -396,8 +383,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Customer Dashboards (getCustomerDashboards)", notes = "Returns a page of dashboard info objects owned by the specified customer. " - + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -432,8 +418,7 @@ public class DashboardController extends BaseController { notes = "Returns the home dashboard object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the User. " + "If 'homeDashboardId' parameter is not set on the User level and the User has authority 'CUSTOMER_USER', check the same parameter for the corresponding Customer. " + "If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " - + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET) @ResponseBody @@ -465,8 +450,7 @@ public class DashboardController extends BaseController { notes = "Returns the home dashboard info object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the User. " + "If 'homeDashboardId' parameter is not set on the User level and the User has authority 'CUSTOMER_USER', check the same parameter for the corresponding Customer. " + "If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET) @ResponseBody @@ -496,8 +480,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", notes = "Returns the home dashboard info object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the corresponding tenant. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET) @ResponseBody @@ -518,8 +501,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Update Tenant Home Dashboard Info (getTenantHomeDashboardInfo)", notes = "Update the home dashboard assignment for the current tenant. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) @@ -584,8 +566,7 @@ public class DashboardController extends BaseController { "Second, remote edge service will receive a copy of assignment dashboard " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once dashboard will be delivered to edge service, it's going to be available for usage on remote edge instance." + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST) @ResponseBody @@ -608,8 +589,7 @@ public class DashboardController extends BaseController { "Second, remote edge service will receive an 'unassign' command to remove dashboard " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once 'unassign' command will be delivered to edge service, it's going to remove dashboard locally." + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody @@ -629,8 +609,7 @@ public class DashboardController extends BaseController { @ApiOperation(value = "Get Edge Dashboards (getEdgeDashboards)", notes = "Returns a page of dashboard info objects assigned to the specified edge. " - + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 555769370c..23518c96bf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -534,8 +534,7 @@ public class DeviceController extends BaseController { } @ApiOperation(value = "Get Device Types (getDeviceTypes)", - notes = "Deprecated. See 'getDeviceProfileNames' API from Device Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Deprecated. See 'getDeviceProfileNames' API from Device Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device/types", method = RequestMethod.GET) @ResponseBody @@ -668,8 +667,7 @@ public class DeviceController extends BaseController { EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive a copy of assignment device " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once device will be delivered to edge service, it's going to be available for usage on remote edge instance." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once device will be delivered to edge service, it's going to be available for usage on remote edge instance." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST) @ResponseBody @@ -693,8 +691,7 @@ public class DeviceController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove device " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove device locally." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove device locally." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 02cf15c81d..b84e801d31 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -86,8 +86,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profile (getDeviceProfileById)", notes = "Fetch the Device Profile object based on the provided Device Profile Id. " + - "The server checks that the device profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "The server checks that the device profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET) @ResponseBody @@ -107,8 +106,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)", notes = "Fetch the Device Profile Info object based on the provided Device Profile Id. " - + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET) @ResponseBody @@ -122,8 +120,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Default Device Profile (getDefaultDeviceProfileInfo)", notes = "Fetch the Default Device Profile Info object. " + - DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET) @ResponseBody @@ -136,8 +133,7 @@ public class DeviceProfileController extends BaseController { "If profile is not set returns a list of unique keys among all profiles. " + "The call is used for auto-complete in the UI forms. " + "The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/devices/keys/timeseries", method = RequestMethod.GET) @ResponseBody @@ -160,8 +156,7 @@ public class DeviceProfileController extends BaseController { "If profile is not set returns a list of unique keys among all profiles. " + "The call is used for auto-complete in the UI forms. " + "The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/devices/keys/attributes", method = RequestMethod.GET) @ResponseBody @@ -186,8 +181,7 @@ public class DeviceProfileController extends BaseController { "Referencing non-existing device profile Id will cause 'Not Found' error. " + NEW_LINE + "Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA + "Remove 'id', 'tenantId' from the request body example (below) to create new Device Profile entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile", method = RequestMethod.POST) @ResponseBody @@ -215,8 +209,7 @@ public class DeviceProfileController extends BaseController { } @ApiOperation(value = "Make Device Profile Default (setDefaultDeviceProfile)", - notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST) @ResponseBody @@ -232,8 +225,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profiles (getDeviceProfiles)", notes = "Returns a page of devices profile objects owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -254,8 +246,7 @@ public class DeviceProfileController extends BaseController { @ApiOperation(value = "Get Device Profiles for transport type (getDeviceProfileInfos)", notes = "Returns a page of devices profile info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 9781d48828..3042b8f2a1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -120,8 +120,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge (getEdgeById)", - notes = "Get the Edge object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get the Edge object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET) @ResponseBody @@ -133,8 +132,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge Info (getEdgeInfoById)", - notes = "Get the Edge Info object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get the Edge Info object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET) @ResponseBody @@ -152,8 +150,7 @@ public class EdgeController extends BaseController { "Referencing non-existing Edge Id will cause 'Not Found' error." + "\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Edge entity. " + - TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge", method = RequestMethod.POST) @ResponseBody @@ -193,7 +190,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edges (getEdges)", notes = "Returns a page of edges owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -213,8 +210,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Assign edge to customer (assignEdgeToCustomer)", - notes = "Creates assignment of the edge to customer. Customer will be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Creates assignment of the edge to customer. Customer will be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST) @ResponseBody @@ -232,8 +228,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Unassign edge from customer (unassignEdgeFromCustomer)", - notes = "Clears assignment of the edge to customer. Customer will not be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Clears assignment of the edge to customer. Customer will not be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE) @ResponseBody @@ -253,8 +248,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Make edge publicly available (assignEdgeToPublicCustomer)", notes = "Edge will be available for non-authorized (not logged-in) users. " + "This is useful to create dashboards that you plan to share/embed on a publicly available website. " + - "However, users that are logged-in and belong to different tenant will not be able to access the edge." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "However, users that are logged-in and belong to different tenant will not be able to access the edge." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST) @ResponseBody @@ -268,7 +262,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edges (getTenantEdges)", notes = "Returns a page of edges owned by tenant. " + - PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -296,8 +290,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edge Infos (getTenantEdgeInfos)", notes = "Returns a page of edges info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -325,8 +318,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Tenant Edge (getTenantEdge)", notes = "Requested edge must be owned by tenant or customer that the user belongs to. " + - "Edge name is an unique property of edge. So it can be used to identify the edge." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Edge name is an unique property of edge. So it can be used to identify the edge." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET) @ResponseBody @@ -338,8 +330,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Set root rule chain for provided edge (setEdgeRootRuleChain)", notes = "Change root rule chain of the edge to the new provided rule chain. \n" + - "This operation will send a notification to update root rule chain on remote edge service." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "This operation will send a notification to update root rule chain on remote edge service." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST) @ResponseBody @@ -359,7 +350,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Customer Edges (getCustomerEdges)", notes = "Returns a page of edges objects assigned to customer. " + - PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -395,7 +386,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Customer Edge Infos (getCustomerEdgeInfos)", notes = "Returns a page of edges info objects assigned to customer. " + - PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody @@ -430,8 +421,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edges By Ids (getEdgesByIds)", - notes = "Requested edges must be owned by tenant or assigned to customer which user is performing the request." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Requested edges must be owned by tenant or assigned to customer which user is performing the request." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET) @ResponseBody @@ -459,8 +449,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Find related edges (findByQuery)", notes = "Returns all edges that are related to the specific entity. " + "The entity id, relation type, edge types, depth of the search, and other query parameters defined using complex 'EdgeSearchQuery' object. " + - "See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edges", method = RequestMethod.POST) @ResponseBody @@ -485,8 +474,7 @@ public class EdgeController extends BaseController { @ApiOperation(value = "Get Edge Types (getEdgeTypes)", notes = "Returns a set of unique edge types based on edges that are either owned by the tenant or assigned to the customer which user is performing the request." - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/edge/types", method = RequestMethod.GET) @ResponseBody @@ -542,8 +530,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Import the bulk of edges (processEdgesBulkImport)", - notes = "There's an ability to import the bulk of edges using the only .csv file." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "There's an ability to import the bulk of edges using the only .csv file." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @PostMapping("/edge/bulk_import") public BulkImportResult processEdgesBulkImport(@RequestBody BulkImportRequest request) throws Exception { @@ -557,8 +544,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge Install Instructions (getEdgeInstallInstructions)", - notes = "Get an install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get an install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/instructions/install/{edgeId}/{method}", method = RequestMethod.GET) @ResponseBody @@ -579,8 +565,7 @@ public class EdgeController extends BaseController { } @ApiOperation(value = "Get Edge Upgrade Instructions (getEdgeUpgradeInstructions)", - notes = "Get an upgrade instructions for provided edge version." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Get an upgrade instructions for provided edge version." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/instructions/upgrade/{edgeVersion}/{method}", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java index 8828a714fc..d1912ba38f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java @@ -60,7 +60,7 @@ public class EdgeEventController extends BaseController { @ApiOperation(value = "Get Edge Events (getEdgeEvents)", notes = "Returns a page of edge events for the requested edge. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 3932e483d5..0cd31a729b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -135,8 +135,7 @@ public class EntityRelationController extends BaseController { } @ApiOperation(value = "Get Relation (getRelation)", - notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE}) @ResponseBody @@ -161,8 +160,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByFrom)", notes = "Returns list of relation objects for the specified entity by the 'from' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) @ResponseBody @@ -180,8 +178,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relation Infos (findInfoByFrom)", notes = "Returns list of relation info objects for the specified entity by the 'from' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE}) @ResponseBody @@ -199,8 +196,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByFrom)", notes = "Returns list of relation objects for the specified entity by the 'from' direction and relation type. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE}) @ResponseBody @@ -220,8 +216,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByTo)", notes = "Returns list of relation objects for the specified entity by the 'to' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) @ResponseBody @@ -239,8 +234,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relation Infos (findInfoByTo)", notes = "Returns list of relation info objects for the specified entity by the 'to' direction. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) @ResponseBody @@ -258,8 +252,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Get List of Relations (findByTo)", notes = "Returns list of relation objects for the specified entity by the 'to' direction and relation type. " + - SECURITY_CHECKS_ENTITY_DESCRIPTION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE}) @ResponseBody @@ -280,7 +273,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Find related entities (findByQuery)", notes = "Returns all entities that are related to the specific entity. " + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + - "See 'Model' tab of the Parameters for more info.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info.") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.POST) @ResponseBody @@ -296,7 +289,7 @@ public class EntityRelationController extends BaseController { @ApiOperation(value = "Find related entity infos (findInfoByQuery)", notes = "Returns all entity infos that are related to the specific entity. " + "The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " + - "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations/info", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 59877bd4d2..8b33aeeb15 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -97,8 +97,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Get entity view (getEntityViewById)", notes = "Fetch the EntityView object based on the provided entity view id. " - + ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET) @ResponseBody @@ -111,8 +110,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Get Entity View info (getEntityViewInfoById)", notes = "Fetch the Entity View info object based on the provided Entity View Id. " - + ENTITY_VIEW_INFO_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + ENTITY_VIEW_INFO_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView/info/{entityViewId}", method = RequestMethod.GET) @ResponseBody @@ -127,8 +125,7 @@ public class EntityViewController extends BaseController { @ApiOperation(value = "Save or update entity view (saveEntityView)", notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/entityView", method = RequestMethod.POST) @ResponseBody @@ -162,8 +159,7 @@ public class EntityViewController extends BaseController { } @ApiOperation(value = "Get Entity View by name (getTenantEntityView)", - notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET) @ResponseBody @@ -399,8 +395,7 @@ public class EntityViewController extends BaseController { EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive a copy of assignment entity view " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST) @ResponseBody @@ -424,8 +419,7 @@ public class EntityViewController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove entity view " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.", - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index b1e2405ae6..372d724912 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -108,7 +108,7 @@ public class EventController extends BaseController { @ApiOperation(value = "Get Events by type (getEvents)", notes = "Returns a page of events for specified entity by specifying event type. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET) @ResponseBody @@ -150,7 +150,7 @@ public class EventController extends BaseController { "The call was deprecated to improve the performance of the system. " + "Current implementation will return 'Lifecycle' events only. " + "Use 'Get events by type' or 'Get events by filter' instead. " + - PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET) @ResponseBody @@ -190,8 +190,7 @@ public class EventController extends BaseController { @ApiOperation(value = "Get Events by event filter (getEvents)", notes = "Returns a page of events for the chosen entity by specifying the event filter. " + PAGE_DATA_PARAMETERS + NEW_LINE + - EVENT_FILTER_DEFINITION, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + EVENT_FILTER_DEFINITION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index f6670193d9..25e49eb198 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -59,8 +59,7 @@ public class Lwm2mController extends BaseController { @ApiOperation(value = "Get Lwm2m Bootstrap SecurityInfo (getLwm2mBootstrapSecurityInfo)", notes = "Get the Lwm2m Bootstrap SecurityInfo object (of the current server) based on the provided isBootstrapServer parameter. If isBootstrapServer == true, get the parameters of the current Bootstrap Server. If isBootstrapServer == false, get the parameters of the current Lwm2m Server. Used for client settings when starting the client in Bootstrap mode. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 4ceb39534f..f2b77c3b7b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -105,8 +105,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package Info (getOtaPackageInfoById)", notes = "Fetch the OTA Package Info object based on the provided OTA Package Id. " + - OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET) @ResponseBody @@ -119,8 +118,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package (getOtaPackageById)", notes = "Fetch the OTA Package object based on the provided OTA Package Id. " + - "The server checks that the OTA Package is owned by the same tenant. " + OTA_PACKAGE_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + "The server checks that the OTA Package is owned by the same tenant. " + OTA_PACKAGE_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET) @ResponseBody @@ -136,8 +134,7 @@ public class OtaPackageController extends BaseController { "The newly created OTA Package id will be present in the response. " + "Specify existing OTA Package id to update the OTA Package Info. " + "Referencing non-existing OTA Package Id will cause 'Not Found' error. " + - "\n\nOTA Package combination of the title with the version is unique in the scope of tenant. " + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + "\n\nOTA Package combination of the title with the version is unique in the scope of tenant. " + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) @ResponseBody @@ -151,7 +148,6 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Save OTA Package data (saveOtaPackageData)", notes = "Update the OTA Package. Adds the date to the existing OTA Package Info" + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)), requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = MULTIPART_FORM_DATA_VALUE))) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA_VALUE) @@ -176,8 +172,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package Infos (getOtaPackages)", notes = "Returns a page of OTA Package Info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/otaPackages", method = RequestMethod.GET) @ResponseBody @@ -197,8 +192,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Get OTA Package Infos (getOtaPackages)", notes = "Returns a page of OTA Package Info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET) @ResponseBody @@ -225,8 +219,7 @@ public class OtaPackageController extends BaseController { @ApiOperation(value = "Delete OTA Package (deleteOtaPackage)", notes = "Deletes the OTA Package. Referencing non-existing OTA Package Id will cause an error. " + - "Can't delete the OTA Package if it is referenced by existing devices or device profile." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE))) + "Can't delete the OTA Package if it is referenced by existing devices or device profile." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index e7c6e138ca..8d51daab8b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -371,7 +371,7 @@ public class RuleChainController extends BaseController { public JsonNode testScript( @Parameter(description = "Script language: JS or TBEL") @RequestParam(required = false) ScriptLanguage scriptLang, - @Parameter(description = "Test JS request. See API call description above.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Test JS request. See API call description above.") @RequestBody JsonNode inputParams) throws ThingsboardException, JsonProcessingException { String script = inputParams.get("script").asText(); String scriptType = inputParams.get("scriptType").asText(); @@ -499,8 +499,7 @@ public class RuleChainController extends BaseController { "Second, remote edge service will receive a copy of assignment rule chain " + EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION + "Third, once rule chain will be delivered to edge service, it's going to start processing messages locally. " + - "\n\nOnly rule chain with type 'EDGE' can be assigned to edge." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "\n\nOnly rule chain with type 'EDGE' can be assigned to edge." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST) @ResponseBody @@ -522,8 +521,7 @@ public class RuleChainController extends BaseController { EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION + "Second, remote edge service will receive an 'unassign' command to remove rule chain " + EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION + - "Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally." + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally." + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index bf59f78519..1908459200 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -146,8 +146,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource Info (getResourceInfoById)", notes = "Fetch the Resource Info object based on the provided Resource Id. " + - RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/info/{resourceId}") public TbResourceInfo getResourceInfoById(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION) @@ -159,8 +158,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource (getResourceById)", notes = "Fetch the Resource object based on the provided Resource Id. " + - RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)), hidden = true) + RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, hidden = true) @Deprecated // resource's data should be fetched with a download request @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource/{resourceId}") @@ -178,8 +176,7 @@ public class TbResourceController extends BaseController { "Referencing non-existing Resource Id will cause 'Not Found' error. " + "\n\nResource combination of the title with the key is unique in the scope of tenant. " + "Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." + - SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PostMapping(value = "/resource") public TbResourceInfo saveResource(@Parameter(description = "A JSON value representing the Resource.") @@ -191,8 +188,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource Infos (getResources)", notes = "Returns a page of Resource Info objects owned by tenant or sysadmin. " + - PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @GetMapping(value = "/resource") public PageData getResources(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -227,8 +223,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get All Resource Infos (getAllResources)", notes = "Returns a page of Resource Info objects owned by tenant. " + - PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/tenant") public PageData getTenantResources(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -251,8 +246,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjectsPage)", notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " + - PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m/page") public List getLwm2mListObjectsPage(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @@ -271,8 +265,7 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)", notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " + - "You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @GetMapping(value = "/resource/lwm2m") public List getLwm2mListObjects(@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}, required = true)) diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 9ca600603c..4e28caa3ba 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -177,8 +177,7 @@ public class TelemetryController extends BaseController { "\n\n * SERVER_SCOPE - supported for all entity types;" + "\n * CLIENT_SCOPE - supported for devices;" + "\n * SHARED_SCOPE - supported for devices. " - + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET) @ResponseBody @@ -193,8 +192,7 @@ public class TelemetryController extends BaseController { "\n\n * SERVER_SCOPE - supported for all entity types;" + "\n * CLIENT_SCOPE - supported for devices;" + "\n * SHARED_SCOPE - supported for devices. " - + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET) @ResponseBody @@ -212,8 +210,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET) @ResponseBody @@ -235,8 +232,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET) @ResponseBody @@ -252,8 +248,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", notes = "Returns a set of unique time-series key names for the selected entity. " + - "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET) @ResponseBody @@ -275,8 +270,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + LATEST_TS_STRICT_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET) @ResponseBody @@ -299,8 +293,7 @@ public class TelemetryController extends BaseController { + MARKDOWN_CODE_BLOCK_START + TS_STRICT_DATA_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) @ResponseBody @@ -348,8 +341,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", notes = "Creates or updates the device attributes based on device id and specified attribute scope. " + SAVE_ATTRIBUTES_REQUEST_PAYLOAD - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + "Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED', " + @@ -365,7 +357,7 @@ public class TelemetryController extends BaseController { public DeferredResult saveDeviceAttributes( @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope, - @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); return saveAttributes(getTenantId(), entityId, scope, request); } @@ -374,8 +366,7 @@ public class TelemetryController extends BaseController { notes = "Creates or updates the entity attributes based on Entity Id and the specified attribute scope. " + ENTITY_SAVE_ATTRIBUTE_SCOPES + SAVE_ATTRIBUTES_REQUEST_PAYLOAD - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK), @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST), @@ -389,7 +380,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveAttributes(getTenantId(), entityId, scope, request); } @@ -398,8 +389,7 @@ public class TelemetryController extends BaseController { notes = "Creates or updates the entity attributes based on Entity Id and the specified attribute scope. " + ENTITY_SAVE_ATTRIBUTE_SCOPES + SAVE_ATTRIBUTES_REQUEST_PAYLOAD - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK), @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST), @@ -413,7 +403,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope, - @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveAttributes(getTenantId(), entityId, scope, request); } @@ -423,8 +413,7 @@ public class TelemetryController extends BaseController { notes = "Creates or updates the entity time-series data based on the Entity Id and request payload." + SAVE_TIMESERIES_REQUEST_PAYLOAD + "\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. " - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ENTITY_TIMESERIES_STATUS_OK), @ApiResponse(responseCode = "400", description = INVALID_STRUCTURE_OF_THE_REQUEST), @@ -448,8 +437,7 @@ public class TelemetryController extends BaseController { SAVE_TIMESERIES_REQUEST_PAYLOAD + "\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. " + "\n\nThe ttl parameter takes affect only in case of Cassandra DB." - + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = SAVE_ENTITY_TIMESERIES_STATUS_OK), @ApiResponse(responseCode = "400", description = INVALID_STRUCTURE_OF_THE_REQUEST), @@ -476,8 +464,7 @@ public class TelemetryController extends BaseController { " Use 'deleteLatest' to delete latest value (stored in separate table for performance) if the value's timestamp matches the time-range. " + " Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." + " The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Timeseries for the selected keys in the request was removed. " + "Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED'."), @@ -552,8 +539,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Delete device attributes (deleteDeviceAttributes)", notes = "Delete device attributes using provided Device Id, scope and a list of keys. " + - "Referencing a non-existing Device Id will cause an error" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "Referencing a non-existing Device Id will cause an error" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Device attributes was removed for the selected keys in the request. " + "Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED'."), @@ -575,8 +561,7 @@ public class TelemetryController extends BaseController { @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", notes = "Delete entity attributes using provided Entity Id, scope and a list of keys. " + - INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Entity attributes was removed for the selected keys in the request. " + "Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED'."), diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 521be28f84..daf9f5d1c1 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1332,6 +1332,8 @@ springdoc: swagger: # General swagger match pattern of swagger UI links api_path: "${SWAGGER_API_PATH:/api/**}" + # General swagger match pattern path of swagger UI links + security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api/.*}" # Nonsecurity API path match pattern of swagger UI links non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/(?:noauth|v1)/.*}" # The title on the API doc UI page diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 23b6c9b76d..0bd8e3a687 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -25,6 +25,7 @@ import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.AlarmCommentId; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; @@ -36,21 +37,21 @@ import org.thingsboard.server.common.data.validation.NoXss; @AllArgsConstructor public class AlarmComment extends BaseData implements HasName { @Schema(description = "JSON object with Alarm id.", accessMode = Schema.AccessMode.READ_ONLY) - private EntityId alarmId; + private AlarmId alarmId; @Schema(description = "JSON object with User id.", accessMode = Schema.AccessMode.READ_ONLY) private UserId userId; @Schema(description = "Defines origination of comment. System type means comment was created by TB. OTHER type means comment was created by user.", example = "SYSTEM/OTHER", accessMode = Schema.AccessMode.READ_ONLY) private AlarmCommentType type; - @Schema(description = "JSON object with text of comment.",implementation = com.fasterxml.jackson.databind.JsonNode.class) + @Schema(description = "JSON object with text of comment.") @NoXss @Length(fieldName = "comment", max = 10000) @EqualsAndHashCode.Include - private transient JsonNode comment; + private JsonNode comment; @Schema(description = "JSON object with the alarm comment Id. " + "Specify this field to update the alarm comment. " + "Referencing non-existing alarm Id will cause error. " + - "Omit this field to create new alarm." ) + "Omit this field to create new alarm.", accessMode = Schema.AccessMode.READ_ONLY) @Override public AlarmCommentId getId() { return super.getId(); @@ -72,7 +73,7 @@ public class AlarmComment extends BaseData implements HasName { @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) - @Schema(required = true, description = "representing comment text", example = "Please take a look") + @Schema(accessMode = Schema.AccessMode.READ_ONLY, description = "representing comment text", example = "Please take a look") public String getName() { return comment.toString(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java index 8b4b1f156d..64c6b03427 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityId.java @@ -35,10 +35,10 @@ public interface EntityId extends HasUUID, Serializable { //NOSONAR, the constan UUID NULL_UUID = UUID.fromString("13814000-1dd2-11b2-8080-808080808080"); - @Schema(required = true, description = "ID of the entity, time-based UUID v1", example = "784f394c-42b6-435a-983c-b7beff2784f9") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "ID of the entity, time-based UUID v1", example = "784f394c-42b6-435a-983c-b7beff2784f9") UUID getId(); - @Schema(required = true, example = "DEVICE") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, example = "DEVICE") EntityType getEntityType(); @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java index 1f18f5a78e..65658246d2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserId.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; +@Schema public class UserId extends UUIDBased implements EntityId { @JsonCreator @@ -33,7 +34,7 @@ public class UserId extends UUIDBased implements EntityId { return new UserId(UUID.fromString(userId)); } - @Schema(required = true, description = "string", example = "USER", allowableValues = "USER") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "string", example = "USER", allowableValues = "USER") @Override public EntityType getEntityType() { return EntityType.USER; diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 5e2d916397..7d2198f45a 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -137,8 +137,7 @@ public class DeviceApiController implements TbTransportService { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_PAYLOAD_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public DeferredResult getDeviceAttributes( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -174,13 +173,12 @@ public class DeviceApiController implements TbTransportService { + MARKDOWN_CODE_BLOCK_START + ATTRIBUTE_PAYLOAD_EXAMPLE + MARKDOWN_CODE_BLOCK_END - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.POST) public DeferredResult postDeviceAttributes( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, - @Parameter(description = "JSON with attribute key-value pairs. See API call description for example.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON with attribute key-value pairs. See API call description for example.") @RequestBody String json) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -196,8 +194,7 @@ public class DeviceApiController implements TbTransportService { description = "Post time-series data on behalf of device. " + "\n Example of the request: " + TS_PAYLOAD - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/telemetry", method = RequestMethod.POST) public DeferredResult postTelemetry( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -222,8 +219,7 @@ public class DeviceApiController implements TbTransportService { + MARKDOWN_CODE_BLOCK_END + "Note: both 'secretKey' and 'durationMs' is optional parameters. " + "In case the secretKey is not specified, the empty string as a default value is used. In case the durationMs is not specified, the system parameter device.claim.duration is used.\n\n" - + REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/claim", method = RequestMethod.POST) public DeferredResult claimDevice( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -244,8 +240,7 @@ public class DeviceApiController implements TbTransportService { description = "Subscribes to RPC commands using http long polling. " + "Deprecated, since long polling is resource and network consuming. " + "Consider using MQTT or CoAP protocol for light-weight real-time updates. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public DeferredResult subscribeToCommands( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -268,15 +263,14 @@ public class DeviceApiController implements TbTransportService { @Operation(summary = "Reply to RPC commands (replyToCommand)", description = "Replies to server originated RPC command identified by 'requestId' parameter. The response is arbitrary JSON.\n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST) public DeferredResult replyToCommand( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, @Parameter(description = "RPC request id from the incoming RPC request", required = true , schema = @Schema(defaultValue = "123")) @PathVariable("requestId") Integer requestId, - @Parameter(description = "Reply to the RPC request, JSON. For example: {\"status\":\"success\"}", required = true) + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Reply to the RPC request, JSON. For example: {\"status\":\"success\"}", required = true) @RequestBody String json) { DeferredResult responseWriter = new DeferredResult(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -296,13 +290,12 @@ public class DeviceApiController implements TbTransportService { MARKDOWN_CODE_BLOCK_START + "{\"result\": 4}" + MARKDOWN_CODE_BLOCK_END + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.POST) public DeferredResult postRpcRequest( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, - @Parameter(description = "The RPC request JSON", required = true) + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "The RPC request JSON", required = true) @RequestBody String json) { DeferredResult responseWriter = new DeferredResult(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -324,8 +317,7 @@ public class DeviceApiController implements TbTransportService { description = "Subscribes to client and shared scope attribute updates using http long polling. " + "Deprecated, since long polling is resource and network consuming. " + "Consider using MQTT or CoAP protocol for light-weight real-time updates. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/attributes/updates", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public DeferredResult subscribeToAttributes( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -356,8 +348,7 @@ public class DeviceApiController implements TbTransportService { "Optional 'chunk' and 'size' parameters may be used to download the firmware in chunks. " + "For example, device may request first 16 KB of firmware using 'chunk'=0 and 'size'=16384. " + "Next 16KB using 'chunk'=1 and 'size'=16384. The last chunk should have less bytes then requested using 'size' parameter. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/firmware", method = RequestMethod.GET) public DeferredResult getFirmware( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -383,8 +374,7 @@ public class DeviceApiController implements TbTransportService { "Optional 'chunk' and 'size' parameters may be used to download the software in chunks. " + "For example, device may request first 16 KB of software using 'chunk'=0 and 'size'=16384. " + "Next 16KB using 'chunk'=1 and 'size'=16384. The last chunk should have less bytes then requested using 'size' parameter. \n\n" + - REQUIRE_ACCESS_TOKEN, - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + REQUIRE_ACCESS_TOKEN) @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET) public DeferredResult getSoftware( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -418,12 +408,10 @@ public class DeviceApiController implements TbTransportService { " \"credentialsType\":\"ACCESS_TOKEN\",\n" + " \"credentialsValue\":\"DEVICE_ACCESS_TOKEN\",\n" + " \"status\":\"SUCCESS\"\n" + - "}" + MARKDOWN_CODE_BLOCK_END - , - responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))) + "}" + MARKDOWN_CODE_BLOCK_END) @RequestMapping(value = "/provision", method = RequestMethod.POST) public DeferredResult provisionDevice( - @Parameter(description = "JSON with provision request. See API call description for example.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON with provision request. See API call description for example.") @RequestBody String json) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), diff --git a/pom.xml b/pom.xml index a9b7efc653..d2450e0a65 100755 --- a/pom.xml +++ b/pom.xml @@ -92,8 +92,8 @@ 4.8.0 3.0.0-M9 3.0.2 - 2.1.0 - 2.2.9 + 2.4.0TB + 2.2.20 0.7 1.18.2 1.69 @@ -111,7 +111,7 @@ 3.2.0 4.1.1 2.7.7 - 2.0 + 2.2 1.11.747 1.105.0 2.1.0 @@ -1775,7 +1775,7 @@ ${curator.version} - org.springdoc + org.thingsboard springdoc-openapi-starter-webmvc-ui ${springdoc-swagger.version} From 8a58fd49b544d5de63038d87df5e8092f40df4b7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 25 Mar 2024 11:28:59 +0200 Subject: [PATCH 039/107] Fix import in DefaultEntityServiceRegistry --- .../server/dao/entity/DefaultEntityServiceRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java index ed0f14ad6d..8202d49db8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/DefaultEntityServiceRegistry.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.entity; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; -import javax.annotation.PostConstruct; import java.util.HashMap; import java.util.List; import java.util.Map; From bec63ab1bf64486128e09ffc72dee564664f694e Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 25 Mar 2024 12:28:04 +0200 Subject: [PATCH 040/107] Swagger doc improvement. --- .../org/thingsboard/server/controller/DashboardController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index dda88eceb6..013575361d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -176,7 +176,7 @@ public class DashboardController extends BaseController { @RequestMapping(value = "/dashboard", method = RequestMethod.POST) @ResponseBody public Dashboard saveDashboard( - @Parameter(description = "A JSON value representing the dashboard.") + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.") @RequestBody Dashboard dashboard) throws Exception { dashboard.setTenantId(getTenantId()); checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD); From 8a890a451218f6e9615899cba09fc96c5dd05391 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 25 Mar 2024 12:45:28 +0200 Subject: [PATCH 041/107] Fix compilation error --- .../server/dao/sql/alarm/JpaAlarmCommentDaoTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java index f6ef6e8940..037377dc7a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java @@ -81,7 +81,7 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest { private void saveAlarmComment(UUID id, UUID alarmId, UUID userId, AlarmCommentType type) { AlarmComment alarmComment = new AlarmComment(); alarmComment.setId(new AlarmCommentId(id)); - alarmComment.setAlarmId(TenantId.fromUUID(alarmId)); + alarmComment.setAlarmId(new AlarmId(alarmId)); alarmComment.setUserId(new UserId(userId)); alarmComment.setType(type); alarmComment.setComment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10))); From 7edb18673c9d8d5913e7b00551da0279c4340211 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 25 Mar 2024 12:47:20 +0200 Subject: [PATCH 042/107] Improve test for KvProtoUtil --- .../server/common/util/KvProtoUtilTest.java | 135 ++++++++---------- 1 file changed, 59 insertions(+), 76 deletions(-) diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java index 603d4b3aa6..0fad4e48a2 100644 --- a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java @@ -16,6 +16,10 @@ package org.thingsboard.server.common.util; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.thingsboard.server.common.data.kv.AggTsKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; @@ -30,102 +34,81 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; class KvProtoUtilTest { - @Test - void protoDataTypeSerialization() { - for (DataType dataType : DataType.values()) { - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(dataType))).as(dataType.name()).isEqualTo(dataType); - } - } + private static final long TS = System.currentTimeMillis(); - @Test - void protoKeyValueProtoSerialization() { + private static Stream kvEntryData() { String key = "key"; - KvEntry kvEntry = new BooleanDataEntry(key, true); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); - - kvEntry = new LongDataEntry(key, 23L); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); - - kvEntry = new DoubleDataEntry(key, 23.0); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); - - kvEntry = new StringDataEntry(key, "stringValue"); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); - - kvEntry = new JsonDataEntry(key, "jsonValue"); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))).as("deserialized").isEqualTo(kvEntry); + return Stream.of( + new BooleanDataEntry(key, true), + new LongDataEntry(key, 23L), + new DoubleDataEntry(key, 23.0), + new StringDataEntry(key, "stringValue"), + new JsonDataEntry(key, "jsonValue") + ); } - @Test - void protoTsKvEntrySerialization() { - String key = "key"; - long ts = System.currentTimeMillis(); - KvEntry kvEntry = new BasicTsKvEntry(ts, new BooleanDataEntry(key, true)); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + private static Stream basicTsKvEntryData() { + return kvEntryData().map(kvEntry -> new BasicTsKvEntry(TS, kvEntry)); + } - kvEntry = new BasicTsKvEntry(ts, new LongDataEntry(key, 23L)); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + private static Stream attributeKvEntryData() { + return kvEntryData().map(kvEntry -> new BaseAttributeKvEntry(TS, kvEntry)); + } - kvEntry = new BasicTsKvEntry(ts, new DoubleDataEntry(key, 23.0)); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + private static List createTsKvEntryList(boolean withAggregation) { + return kvEntryData().map(kvEntry -> { + if (withAggregation) { + return new AggTsKvEntry(TS, kvEntry, 0); + } else { + return new BasicTsKvEntry(TS, kvEntry); + } + }).collect(Collectors.toList()); + } - kvEntry = new BasicTsKvEntry(ts, new StringDataEntry(key, "stringValue")); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + @ParameterizedTest + @EnumSource(DataType.class) + void protoDataTypeSerialization(DataType dataType) { + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(dataType))).as(dataType.name()).isEqualTo(dataType); + } - kvEntry = new BasicTsKvEntry(ts, new JsonDataEntry(key, "jsonValue")); - assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(ts, kvEntry))).as("deserialized").isEqualTo(kvEntry); + @ParameterizedTest + @MethodSource("kvEntryData") + void protoKeyValueProtoSerialization(KvEntry kvEntry) { + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(kvEntry))) + .as("deserialized") + .isEqualTo(kvEntry); } - @Test - void protoListTsKvEntrySerialization() { - String key = "key"; - long ts = System.currentTimeMillis(); - KvEntry booleanDataEntry = new BooleanDataEntry(key, true); - KvEntry longDataEntry = new LongDataEntry(key, 23L); - KvEntry doubleDataEntry = new DoubleDataEntry(key, 23.0); - KvEntry stringDataEntry = new StringDataEntry(key, "stringValue"); - KvEntry jsonDataEntry = new JsonDataEntry(key, "jsonValue"); - List protoList = List.of( - new BasicTsKvEntry(ts, booleanDataEntry), - new BasicTsKvEntry(ts, longDataEntry), - new BasicTsKvEntry(ts, doubleDataEntry), - new BasicTsKvEntry(ts, stringDataEntry), - new BasicTsKvEntry(ts, jsonDataEntry) - ); - assertThat(KvProtoUtil.fromProtoList(KvProtoUtil.toProtoList(protoList))).as("deserialized").isEqualTo(protoList); + @ParameterizedTest + @MethodSource("basicTsKvEntryData") + void protoTsKvEntrySerialization(KvEntry kvEntry) { + assertThat(KvProtoUtil.fromProto(KvProtoUtil.toProto(TS, kvEntry))) + .as("deserialized") + .isEqualTo(kvEntry); + } - protoList = List.of( - new AggTsKvEntry(ts, booleanDataEntry, 3), - new AggTsKvEntry(ts, longDataEntry, 5), - new AggTsKvEntry(ts, doubleDataEntry, 2), - new AggTsKvEntry(ts, stringDataEntry, 1), - new AggTsKvEntry(ts, jsonDataEntry, 0) - ); - assertThat(KvProtoUtil.fromProtoList(KvProtoUtil.toProtoList(protoList))).as("deserialized").isEqualTo(protoList); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void protoListTsKvEntrySerialization(boolean withAggregation) { + List tsKvEntries = createTsKvEntryList(withAggregation); + assertThat(KvProtoUtil.fromProtoList(KvProtoUtil.toProtoList(tsKvEntries))) + .as("deserialized") + .isEqualTo(tsKvEntries); } @Test void protoListAttributeKvSerialization() { - String key = "key"; - long ts = System.currentTimeMillis(); - KvEntry booleanDataEntry = new BooleanDataEntry(key, true); - KvEntry longDataEntry = new LongDataEntry(key, 23L); - KvEntry doubleDataEntry = new DoubleDataEntry(key, 23.0); - KvEntry stringDataEntry = new StringDataEntry(key, "stringValue"); - KvEntry jsonDataEntry = new JsonDataEntry(key, "jsonValue"); - List protoList = List.of( - new BaseAttributeKvEntry(ts, booleanDataEntry), - new BaseAttributeKvEntry(ts, longDataEntry), - new BaseAttributeKvEntry(ts, doubleDataEntry), - new BaseAttributeKvEntry(ts, stringDataEntry), - new BaseAttributeKvEntry(ts, jsonDataEntry) - ); - assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(protoList))).as("deserialized").isEqualTo(protoList); + List protoList = attributeKvEntryData().toList(); + assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(protoList))) + .as("deserialized") + .isEqualTo(protoList); } } From 2e20ad6dc659aaeb7aa38d683725e23761ca88fb Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 25 Mar 2024 12:51:58 +0200 Subject: [PATCH 043/107] Add tenantId tag to Rule Engine consumer stats --- .../queue/TbRuleEngineConsumerStats.java | 21 ++++++++++--------- .../common/stats/DefaultStatsFactory.java | 14 +++++++++---- .../server/common/stats/StatsFactory.java | 4 +++- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java index c860a00237..5479677f39 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.queue; import io.micrometer.core.instrument.Timer; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.stats.StatsCounter; import org.thingsboard.server.common.stats.StatsFactory; @@ -45,6 +44,7 @@ public class TbRuleEngineConsumerStats { public static final String FAILED_MSGS = "failedMsgs"; public static final String SUCCESSFUL_ITERATIONS = "successfulIterations"; public static final String FAILED_ITERATIONS = "failedIterations"; + public static final String TENANT_ID_TAG = "tenantId"; private final StatsFactory statsFactory; @@ -73,14 +73,15 @@ public class TbRuleEngineConsumerStats { this.statsFactory = statsFactory; String statsKey = StatsType.RULE_ENGINE.getName() + "." + queueName; - this.totalMsgCounter = statsFactory.createStatsCounter(statsKey, TOTAL_MSGS); - this.successMsgCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_MSGS); - this.timeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TIMEOUT_MSGS); - this.failedMsgCounter = statsFactory.createStatsCounter(statsKey, FAILED_MSGS); - this.tmpTimeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_TIMEOUT); - this.tmpFailedMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_FAILED); - this.successIterationsCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_ITERATIONS); - this.failedIterationsCounter = statsFactory.createStatsCounter(statsKey, FAILED_ITERATIONS); + String tenant = tenantId == null || tenantId.isSysTenantId() ? "system" : tenantId.toString(); + this.totalMsgCounter = statsFactory.createStatsCounter(statsKey, TOTAL_MSGS, TENANT_ID_TAG, tenant); + this.successMsgCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_MSGS, TENANT_ID_TAG, tenant); + this.timeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TIMEOUT_MSGS, TENANT_ID_TAG, tenant); + this.failedMsgCounter = statsFactory.createStatsCounter(statsKey, FAILED_MSGS, TENANT_ID_TAG, tenant); + this.tmpTimeoutMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_TIMEOUT, TENANT_ID_TAG, tenant); + this.tmpFailedMsgCounter = statsFactory.createStatsCounter(statsKey, TMP_FAILED, TENANT_ID_TAG, tenant); + this.successIterationsCounter = statsFactory.createStatsCounter(statsKey, SUCCESSFUL_ITERATIONS, TENANT_ID_TAG, tenant); + this.failedIterationsCounter = statsFactory.createStatsCounter(statsKey, FAILED_ITERATIONS, TENANT_ID_TAG, tenant); counters.add(totalMsgCounter); counters.add(successMsgCounter); @@ -93,7 +94,7 @@ public class TbRuleEngineConsumerStats { counters.add(failedIterationsCounter); } - public Timer getTimer(TenantId tenantId, String status){ + public Timer getTimer(TenantId tenantId, String status) { return tenantMsgProcessTimers.computeIfAbsent(tenantId, id -> statsFactory.createTimer(StatsType.RULE_ENGINE.getName() + "." + queueName, "tenantId", tenantId.getId().toString(), diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java index a6ec01d089..e3ef373014 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/DefaultStatsFactory.java @@ -19,6 +19,7 @@ import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tags; import io.micrometer.core.instrument.Timer; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -61,12 +62,17 @@ public class DefaultStatsFactory implements StatsFactory { @Override - public StatsCounter createStatsCounter(String key, String statsName) { + public StatsCounter createStatsCounter(String key, String statsName, String... otherTags) { + String[] tags = new String[]{STATS_NAME_TAG, statsName}; + if (otherTags.length > 0) { + if (otherTags.length % 2 != 0) { + throw new IllegalArgumentException("Invalid tags array size"); + } + tags = ArrayUtils.addAll(tags, otherTags); + } return new StatsCounter( new AtomicInteger(0), - metricsEnabled ? - meterRegistry.counter(key, STATS_NAME_TAG, statsName) - : STUB_COUNTER, + metricsEnabled ? meterRegistry.counter(key, tags) : STUB_COUNTER, statsName ); } diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java index 8e6989016b..b9bf0b97fb 100644 --- a/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/StatsFactory.java @@ -18,7 +18,8 @@ package org.thingsboard.server.common.stats; import io.micrometer.core.instrument.Timer; public interface StatsFactory { - StatsCounter createStatsCounter(String key, String statsName); + + StatsCounter createStatsCounter(String key, String statsName, String... otherTags); DefaultCounter createDefaultCounter(String key, String... tags); @@ -27,4 +28,5 @@ public interface StatsFactory { MessagesStats createMessagesStats(String key); Timer createTimer(String key, String... tags); + } From 6890b18dfb6d73dee43b7d162f02e4a3338a88bf Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 25 Mar 2024 13:10:58 +0200 Subject: [PATCH 044/107] Swagger doc improvement. --- .../thingsboard/server/controller/AlarmCommentController.java | 2 +- .../org/thingsboard/server/controller/AlarmController.java | 2 +- .../org/thingsboard/server/controller/AssetController.java | 2 +- .../org/thingsboard/server/controller/CustomerController.java | 2 +- .../org/thingsboard/server/controller/DeviceController.java | 2 +- .../thingsboard/server/controller/TelemetryController.java | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java index 65f5c1c543..15918f0a08 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -74,7 +74,7 @@ public class AlarmCommentController extends BaseController { @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST) @ResponseBody public AlarmComment saveAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) - @PathVariable(ALARM_ID) String strAlarmId, @Parameter(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { + @PathVariable(ALARM_ID) String strAlarmId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmInfoId(alarmId, Operation.WRITE); diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index ba22663467..e78313f6d8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -143,7 +143,7 @@ public class AlarmController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm", method = RequestMethod.POST) @ResponseBody - public Alarm saveAlarm(@Parameter(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException { + public Alarm saveAlarm(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException { alarm.setTenantId(getTenantId()); checkNotNull(alarm.getOriginator()); checkEntity(alarm.getId(), alarm, Resource.ALARM); diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 751f3fd812..8c03d34e8e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -139,7 +139,7 @@ public class AssetController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset", method = RequestMethod.POST) @ResponseBody - public Asset saveAsset(@Parameter(description = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception { + public Asset saveAsset(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception { asset.setTenantId(getTenantId()); checkEntity(asset.getId(), asset, Resource.ASSET); return tbAssetService.save(asset, getCurrentUser()); diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index c24c345f76..a46cbe7dba 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -130,7 +130,7 @@ public class CustomerController extends BaseController { @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/customer", method = RequestMethod.POST) @ResponseBody - public Customer saveCustomer(@Parameter(description = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception { + public Customer saveCustomer(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception { customer.setTenantId(getTenantId()); checkEntity(customer.getId(), customer, Resource.CUSTOMER); return tbCustomerService.save(customer, getCurrentUser()); diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 23518c96bf..fedbd48503 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -177,7 +177,7 @@ public class DeviceController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device", method = RequestMethod.POST) @ResponseBody - public Device saveDevice(@Parameter(description = "A JSON value representing the device.") @RequestBody Device device, + public Device saveDevice(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the device.") @RequestBody Device device, @Parameter(description = "Optional value of the device credentials to be used during device creation. " + "If omitted, access token will be auto-generated.") @RequestParam(name = "accessToken", required = false) String accessToken) throws Exception { device.setTenantId(getCurrentUser().getTenantId()); diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 4e28caa3ba..c73ac09cf1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -427,7 +427,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType, @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope, - @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, 0L); } @@ -452,7 +452,7 @@ public class TelemetryController extends BaseController { @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")AttributeScope scope, @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl, - @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); return saveTelemetry(getTenantId(), entityId, requestBody, ttl); } From 2e91612534cb6ccf46f311d0b5e9dc502e103bb2 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 25 Mar 2024 14:04:08 +0200 Subject: [PATCH 045/107] Minor refactoring --- .../subscription/TbSubscriptionUtils.java | 56 +------------------ .../server/common/util/KvProtoUtil.java | 34 +++++++++++ .../server/common/util/KvProtoUtilTest.java | 34 ++++++----- 3 files changed, 56 insertions(+), 68 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index 243a840f51..2d8440fa20 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -23,13 +23,6 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; -import org.thingsboard.server.common.data.kv.BooleanDataEntry; -import org.thingsboard.server.common.data.kv.DoubleDataEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.gen.transport.TransportProtos; @@ -55,8 +48,9 @@ import java.util.Map; import java.util.TreeMap; import java.util.UUID; -import static org.thingsboard.server.common.util.KvProtoUtil.fromKeyValueTypeProto; -import static org.thingsboard.server.common.util.KvProtoUtil.toKeyValueTypeProto; +import static org.thingsboard.server.common.util.KvProtoUtil.fromTsValueProtoList; +import static org.thingsboard.server.common.util.KvProtoUtil.toTsKvProtoBuilder; +import static org.thingsboard.server.common.util.KvProtoUtil.toTsValueProto; public class TbSubscriptionUtils { @@ -332,48 +326,4 @@ public class TbSubscriptionUtils { return ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg(result).build(); } - public static TransportProtos.TsKvProto.Builder toTsKvProtoBuilder(long ts, KvEntry attr) { - TransportProtos.KeyValueProto.Builder dataBuilder = TransportProtos.KeyValueProto.newBuilder(); - dataBuilder.setKey(attr.getKey()); - dataBuilder.setType(toKeyValueTypeProto(attr.getDataType())); - switch (attr.getDataType()) { - case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV); - case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV); - } - return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); - } - - public static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { - TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); - dataBuilder.setTs(ts); - dataBuilder.setType(toKeyValueTypeProto(attr.getDataType())); - switch (attr.getDataType()) { - case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); - case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV); - case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); - case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV); - case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV); - } - return dataBuilder.build(); - } - - private static List fromTsValueProtoList(String key, List dataList) { - List result = new ArrayList<>(dataList.size()); - dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsKvProto(key, proto)))); - return result; - } - - private static KvEntry fromTsKvProto(String key, TransportProtos.TsValueProto proto) { - return switch (fromKeyValueTypeProto(proto.getType())) { - case BOOLEAN -> new BooleanDataEntry(key, proto.getBoolV()); - case LONG -> new LongDataEntry(key, proto.getLongV()); - case DOUBLE -> new DoubleDataEntry(key, proto.getDoubleV()); - case STRING -> new StringDataEntry(key, proto.getStringV()); - case JSON -> new JsonDataEntry(key, proto.getJsonV()); - }; - } - } diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java b/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java index 14368693c5..74674e1e45 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/KvProtoUtil.java @@ -114,6 +114,40 @@ public class KvProtoUtil { }; } + public static TransportProtos.TsKvProto.Builder toTsKvProtoBuilder(long ts, KvEntry kvEntry) { + return TransportProtos.TsKvProto.newBuilder().setTs(ts).setKv(KvProtoUtil.toKeyValueTypeProto(kvEntry)); + } + + public static List fromTsValueProtoList(String key, List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), fromTsValueProto(key, proto)))); + return result; + } + + public static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { + TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); + dataBuilder.setTs(ts); + dataBuilder.setType(toKeyValueTypeProto(attr.getDataType())); + switch (attr.getDataType()) { + case BOOLEAN -> attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); + case LONG -> attr.getLongValue().ifPresent(dataBuilder::setLongV); + case DOUBLE -> attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); + case JSON -> attr.getJsonValue().ifPresent(dataBuilder::setJsonV); + case STRING -> attr.getStrValue().ifPresent(dataBuilder::setStringV); + } + return dataBuilder.build(); + } + + public static KvEntry fromTsValueProto(String key, TransportProtos.TsValueProto proto) { + return switch (fromKeyValueTypeProto(proto.getType())) { + case BOOLEAN -> new BooleanDataEntry(key, proto.getBoolV()); + case LONG -> new LongDataEntry(key, proto.getLongV()); + case DOUBLE -> new DoubleDataEntry(key, proto.getDoubleV()); + case STRING -> new StringDataEntry(key, proto.getStringV()); + case JSON -> new JsonDataEntry(key, proto.getJsonV()); + }; + } + public static TransportProtos.KeyValueType toKeyValueTypeProto(DataType dataType) { return TransportProtos.KeyValueType.forNumber(dataType.getProtoNumber()); } diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java index 1fbfaf52d9..72d71c7c93 100644 --- a/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/KvProtoUtilTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.common.util; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; @@ -58,8 +57,8 @@ class KvProtoUtilTest { return kvEntryData().map(kvEntry -> new BasicTsKvEntry(TS, kvEntry)); } - private static Stream attributeKvEntryData() { - return kvEntryData().map(kvEntry -> new BaseAttributeKvEntry(TS, kvEntry)); + private static Stream> attributeKvEntryData() { + return Stream.of(kvEntryData().map(kvEntry -> new BaseAttributeKvEntry(TS, kvEntry)).toList()); } private static List createTsKvEntryList(boolean withAggregation) { @@ -75,23 +74,29 @@ class KvProtoUtilTest { @ParameterizedTest @EnumSource(DataType.class) void protoDataTypeSerialization(DataType dataType) { - assertThat(KvProtoUtil.fromKeyValueTypeProto(KvProtoUtil.toKeyValueTypeProto(dataType))).as(dataType.name()).isEqualTo(dataType); + assertThat(KvProtoUtil.fromKeyValueTypeProto(KvProtoUtil.toKeyValueTypeProto(dataType))) + .as(dataType.name()).isEqualTo(dataType); } @ParameterizedTest @MethodSource("kvEntryData") void protoKeyValueProtoSerialization(KvEntry kvEntry) { assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toKeyValueTypeProto(kvEntry))) - .as("deserialized") - .isEqualTo(kvEntry); + .as("deserialized").isEqualTo(kvEntry); } @ParameterizedTest @MethodSource("basicTsKvEntryData") void protoTsKvEntrySerialization(KvEntry kvEntry) { assertThat(KvProtoUtil.fromTsKvProto(KvProtoUtil.toTsKvProto(TS, kvEntry))) - .as("deserialized") - .isEqualTo(kvEntry); + .as("deserialized").isEqualTo(kvEntry); + } + + @ParameterizedTest + @MethodSource("kvEntryData") + void protoTsValueSerialization(KvEntry kvEntry) { + assertThat(KvProtoUtil.fromTsValueProto(kvEntry.getKey(), KvProtoUtil.toTsValueProto(TS, kvEntry))) + .as("deserialized").isEqualTo(kvEntry); } @ParameterizedTest @@ -99,16 +104,15 @@ class KvProtoUtilTest { void protoListTsKvEntrySerialization(boolean withAggregation) { List tsKvEntries = createTsKvEntryList(withAggregation); assertThat(KvProtoUtil.fromTsKvProtoList(KvProtoUtil.toTsKvProtoList(tsKvEntries))) - .as("deserialized") - .isEqualTo(tsKvEntries); + .as("deserialized").isEqualTo(tsKvEntries); } - @Test - void protoListAttributeKvSerialization() { - List protoList = attributeKvEntryData().toList(); - assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(protoList))) + @ParameterizedTest + @MethodSource("attributeKvEntryData") + void protoListAttributeKvSerialization(List attributeKvEntries) { + assertThat(KvProtoUtil.toAttributeKvList(KvProtoUtil.attrToTsKvProtos(attributeKvEntries))) .as("deserialized") - .isEqualTo(protoList); + .isEqualTo(attributeKvEntries); } } From 753bacd2dce7b58bcbe45596da59098a1fb8fc32 Mon Sep 17 00:00:00 2001 From: rusikv Date: Mon, 25 Mar 2024 16:31:56 +0200 Subject: [PATCH 046/107] UI: oauth settings style improvement --- .../pages/admin/oauth2-settings.component.html | 8 ++++---- .../pages/admin/oauth2-settings.component.scss | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index acaf58895e..2e85f8b54b 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -29,8 +29,8 @@
-
-
+
+
{{ 'admin.oauth2.enable' | translate }} @@ -38,9 +38,9 @@ {{ 'admin.oauth2.edge-enable' | translate }}
-
+
-
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index a476f438e3..639fb877f2 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -13,7 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import '../../../../../scss/constants'; + :host{ + .gap-xs-12 { + @media #{$mat-xs} { + gap: 12px; + } + } + + .mdc-evolution-chip-set .mdc-evolution-chip { + margin-top: 0; + margin-bottom: 0; + } + .checkbox-row { margin-top: 1em; } @@ -31,7 +44,7 @@ } .container{ - margin-bottom: 1em; + margin-bottom: 16px; .tb-highlight{ margin: 0; From 2efd411a36d835498f59191774f24d8202214c7b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 25 Mar 2024 15:34:57 +0100 Subject: [PATCH 047/107] Check alarm status node: log severity DEBUG for "Failed to parse alarm" for known runtime exceptions. The ERROR severity will fire only on unexpected exception types. This will prevent flooding the error logger --- .../engine/filter/TbCheckAlarmStatusNode.java | 9 +++++++-- .../filter/TbCheckAlarmStatusNodeTest.java | 13 +++++++++++++ .../src/test/resources/logback-test.xml | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/test/resources/logback-test.xml diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java index 08a1776ba3..8d7f12d14d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import javax.annotation.Nullable; +import java.util.Objects; @Slf4j @RuleNode( @@ -57,7 +58,7 @@ public class TbCheckAlarmStatusNode implements TbNode { public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { try { Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class); - + Objects.requireNonNull(alarm, "alarm is null"); ListenableFuture latest = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), alarm.getId()); Futures.addCallback(latest, new FutureCallback<>() { @@ -78,7 +79,11 @@ public class TbCheckAlarmStatusNode implements TbNode { } }, ctx.getDbCallbackExecutor()); } catch (Exception e) { - log.error("Failed to parse alarm: [{}]", msg.getData()); + if (e instanceof IllegalArgumentException || e instanceof NullPointerException) { + log.debug("Failed to parse alarm: [{}] error [{}]", msg.getData(), e.getMessage()); + } else { + log.error("Failed to parse alarm: [{}]", msg.getData(), e); + } throw new TbNodeException(e); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index c79f962818..1fc0b84172 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -158,6 +159,18 @@ class TbCheckAlarmStatusNodeTest { assertThat(value).isInstanceOf(TbNodeException.class).hasMessage("No such alarm found."); } + @Test + void givenUnparseableAlarm_whenOnMsg_then_Failure() { + String msgData = "{\"Number\":1113718,\"id\":8.1}"; + TbMsg msg = getTbMsg(msgData); + + assertThatThrownBy(() -> node.onMsg(ctx, msg)) + .as("onMsg") + .isInstanceOf(TbNodeException.class) + .hasCauseInstanceOf(IllegalArgumentException.class) + .hasMessage("java.lang.IllegalArgumentException: The given string value cannot be transformed to Json object: {\"Number\":1113718,\"id\":8.1}"); + } + private TbMsg getTbMsg(String msgData) { return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, msgData); } diff --git a/rule-engine/rule-engine-components/src/test/resources/logback-test.xml b/rule-engine/rule-engine-components/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..0695fe1e1a --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + From add2bea56a9cd5a54fdfe7dc16b78fde37e4c9c2 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Mar 2024 09:04:37 +0100 Subject: [PATCH 048/107] Check alarm status node: added tenant and ruleChainName to the log to make a log useful in multi-tenant deployments --- .../rule/engine/filter/TbCheckAlarmStatusNode.java | 4 ++-- .../rule/engine/filter/TbCheckAlarmStatusNodeTest.java | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java index 8d7f12d14d..1d2f665376 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java @@ -80,9 +80,9 @@ public class TbCheckAlarmStatusNode implements TbNode { }, ctx.getDbCallbackExecutor()); } catch (Exception e) { if (e instanceof IllegalArgumentException || e instanceof NullPointerException) { - log.debug("Failed to parse alarm: [{}] error [{}]", msg.getData(), e.getMessage()); + log.debug("[{}][{}] Failed to parse alarm: [{}] error [{}]", ctx.getTenantId(), ctx.getRuleChainName(), msg.getData(), e.getMessage()); } else { - log.error("Failed to parse alarm: [{}]", msg.getData(), e); + log.error("[{}][{}] Failed to parse alarm: [{}]", ctx.getTenantId(), ctx.getRuleChainName(), msg.getData(), e); } throw new TbNodeException(e); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java index 1fc0b84172..5b71db5dfb 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -163,6 +164,7 @@ class TbCheckAlarmStatusNodeTest { void givenUnparseableAlarm_whenOnMsg_then_Failure() { String msgData = "{\"Number\":1113718,\"id\":8.1}"; TbMsg msg = getTbMsg(msgData); + willReturn("Default Rule Chain").given(ctx).getRuleChainName(); assertThatThrownBy(() -> node.onMsg(ctx, msg)) .as("onMsg") From 646c8679a662c56f6c33dc2908c4af6a8902ec81 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 26 Mar 2024 12:19:21 +0200 Subject: [PATCH 049/107] Fix oauth support: send to edge, when propagate to edge is false --- .../service/edge/EdgeEventSourcingListener.java | 3 --- .../oauth2/OAuth2MsgConstructor.java | 1 + .../edge/rpc/fetch/OAuth2EdgeEventFetcher.java | 7 +++---- .../thingsboard/server/edge/OAuth2EdgeTest.java | 17 +++++++++-------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index b48b4ac3b2..6c2f81a200 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -160,9 +160,6 @@ public class EdgeEventSourcingListener { private boolean isValidSaveEntityEventForEdgeProcessing(SaveEntityEvent event) { Object entity = event.getEntity(); Object oldEntity = event.getOldEntity(); - if (entity instanceof OAuth2Info oAuth2Info) { - return oAuth2Info.isEdgeEnabled(); - } switch (event.getEntityId().getEntityType()) { case RULE_CHAIN: if (entity instanceof RuleChain ruleChain) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java index a141e618f4..c628b042a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java @@ -28,4 +28,5 @@ public class OAuth2MsgConstructor { public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Info oAuth2Info) { return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Info)).build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java index e4ef6cfdb6..4412f91183 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java @@ -47,11 +47,10 @@ public class OAuth2EdgeEventFetcher implements EdgeEventFetcher { public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { List result = new ArrayList<>(); OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info(); - if (oAuth2Info.isEdgeEnabled()) { - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2, - EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info))); - } + result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2, + EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info))); // returns PageData object to be in sync with other fetchers return new PageData<>(result, 1, result.size(), false); } + } diff --git a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java index f10338b37b..b3cc133f4b 100644 --- a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java @@ -46,12 +46,6 @@ public class OAuth2EdgeTest extends AbstractEdgeTest { edgeImitator.expectMessageAmount(1); OAuth2Info oAuth2Info = createDefaultOAuth2Info(); oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); - Assert.assertFalse(edgeImitator.waitForMessages(10)); - - // enable edge support - edgeImitator.expectMessageAmount(1); - oAuth2Info.setEdgeEnabled(true); - oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); Assert.assertTrue(edgeImitator.waitForMessages()); AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); @@ -59,16 +53,23 @@ public class OAuth2EdgeTest extends AbstractEdgeTest { OAuth2Info result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); Assert.assertEquals(oAuth2Info, result); - // disable oauth suppor + // disable oauth support + edgeImitator.expectMessageAmount(1); oAuth2Info.setEnabled(false); oAuth2Info.setEdgeEnabled(false); doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); + oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; + result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); + Assert.assertEquals(oAuth2Info, result); loginTenantAdmin(); } private OAuth2Info createDefaultOAuth2Info() { - return new OAuth2Info(true, false, Lists.newArrayList( + return new OAuth2Info(true, true, Lists.newArrayList( OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build() From a7eee8fe6268f851081f18f8374310c7be54a9bc Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 26 Mar 2024 13:01:03 +0200 Subject: [PATCH 050/107] UI: Time-series chart dark mode improvement --- .../home/components/widget/lib/chart/time-series-chart.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 462825087e..65a323e4b6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -139,7 +139,8 @@ export class TbTimeSeriesChart { this.settings = mergeDeep({} as TimeSeriesChartSettings, timeSeriesChartDefaultSettings, this.inputSettings as TimeSeriesChartSettings); - const dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page'); + const $dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page'); + const dashboardPageElement = $($dashboardPageElement[$dashboardPageElement.length-1]); this.darkMode = this.settings.darkMode || dashboardPageElement.hasClass('dark'); this.setupYAxes(); this.setupData(); From 8a0de018a3d5340f24a06552393c4c7385a8ac2e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 26 Mar 2024 13:02:11 +0200 Subject: [PATCH 051/107] UI: Fixed draggeble marker and not draw new poligon --- ui-ngx/package.json | 22 +++++++++++----------- ui-ngx/yarn.lock | 34 +++++++++++++++++----------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/ui-ngx/package.json b/ui-ngx/package.json index a74a1f2953..46cd5dd3cd 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -30,7 +30,7 @@ "@date-io/date-fns": "1.3.7", "@flowjs/flow.js": "^2.14.1", "@flowjs/ngx-flow": "~0.6.0", - "@geoman-io/leaflet-geoman-free": "^2.13.0", + "@geoman-io/leaflet-geoman-free": "2.14.2", "@iplab/ngx-color-picker": "^15.0.2", "@juggle/resize-observer": "^3.4.0", "@mat-datetimepicker/core": "~11.0.3", @@ -67,11 +67,11 @@ "jstree": "^3.3.15", "jstree-bootstrap-theme": "^1.0.1", "jszip": "^3.10.1", - "leaflet": "~1.8.0", - "leaflet-polylinedecorator": "^1.6.0", - "leaflet-providers": "^1.13.0", - "leaflet.gridlayer.googlemutant": "^0.13.5", - "leaflet.markercluster": "^1.5.3", + "leaflet": "1.8.0", + "leaflet-polylinedecorator": "1.6.0", + "leaflet-providers": "1.13.0", + "leaflet.gridlayer.googlemutant": "0.14.1", + "leaflet.markercluster": "1.5.3", "libphonenumber-js": "^1.10.4", "marked": "^4.0.17", "moment": "^2.29.4", @@ -130,11 +130,11 @@ "@types/jasminewd2": "^2.0.10", "@types/jquery": "^3.5.16", "@types/js-beautify": "^1.13.3", - "@types/leaflet": "~1.8.0", - "@types/leaflet-polylinedecorator": "^1.6.1", - "@types/leaflet-providers": "^1.2.1", - "@types/leaflet.gridlayer.googlemutant": "^0.4.6", - "@types/leaflet.markercluster": "^1.5.1", + "@types/leaflet": "1.8.0", + "@types/leaflet-polylinedecorator": "1.6.4", + "@types/leaflet-providers": "1.2.4", + "@types/leaflet.gridlayer.googlemutant": "0.4.9", + "@types/leaflet.markercluster": "1.5.4", "@types/lodash": "^4.14.192", "@types/marked": "^4.0.8", "@types/node": "~18.15.11", diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index ca5c179ad5..33362767bd 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -1582,10 +1582,10 @@ dependencies: tslib "^2.2.0" -"@geoman-io/leaflet-geoman-free@^2.13.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.16.0.tgz#c8a92fcc2cdd5770ebf43f8ef9f03cc8ef842359" - integrity sha512-BnKAAoTXraWVFfhX/0gT/iBgAz1BPfpbdQ9dJamEFI4lIku9UNXXluu/E0k7YkZETq0tENX2GPnKLB4p+VgrSw== +"@geoman-io/leaflet-geoman-free@2.14.2": + version "2.14.2" + resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.14.2.tgz#c84c2115c263f34d11dc0b43859551639fe3d56b" + integrity sha512-6lIyG8RvSVdFjVjiQgBPyNASjymSyqzsiUeBW0pA+q41lB5fAg4SDC6SfJvWdEyDHa81Jb5FWjUkCc9O+u0gbg== dependencies: "@turf/boolean-contains" "^6.5.0" "@turf/kinks" "^6.5.0" @@ -3079,28 +3079,28 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/leaflet-polylinedecorator@^1.6.1": +"@types/leaflet-polylinedecorator@1.6.4": version "1.6.4" resolved "https://registry.yarnpkg.com/@types/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.4.tgz#2270b84585bd28bbf1fef237be8480196c44ac06" integrity sha512-meH/jC58Drt/9aqLhuzMCUQjHgMTud+UuQBK5gT6E8M6OfM/rftz/VgvdxOCCAQC9isrp4pqahDgEswesb5X3A== dependencies: "@types/leaflet" "*" -"@types/leaflet-providers@^1.2.1": +"@types/leaflet-providers@1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@types/leaflet-providers/-/leaflet-providers-1.2.4.tgz#284e8a197d4c2dbfeda436842e03b2adb502be16" integrity sha512-4wYEpreixp+G5t510s202eQ5eubOmxHevIfCNrzUZbzp50XQsC9lSetX7MnPl3ANRJnUBbCWOzZ9EQFiH0Jm+g== dependencies: "@types/leaflet" "*" -"@types/leaflet.gridlayer.googlemutant@^0.4.6": +"@types/leaflet.gridlayer.googlemutant@0.4.9": version "0.4.9" resolved "https://registry.yarnpkg.com/@types/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.4.9.tgz#9090ddf4578ce631d22b8aafc5ed12525b4fbf90" integrity sha512-u/5Avs1KKkeABRDixGbGp2ldFs67+fYIATpkvvFgN+LQPNfq7dFd5adkksshiQBfYJ4SaQg1VJgN2dNirIC0aQ== dependencies: "@types/leaflet" "*" -"@types/leaflet.markercluster@^1.5.1": +"@types/leaflet.markercluster@1.5.4": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/leaflet.markercluster/-/leaflet.markercluster-1.5.4.tgz#2ab43417cf3f6a42d0f1baf4e1c8f659cf1dc3a1" integrity sha512-tfMP8J62+wfsVLDLGh5Zh1JZxijCaBmVsMAX78MkLPwvPitmZZtSin5aWOVRhZrCS+pEOZwNzexbfWXlY+7yjg== @@ -3114,7 +3114,7 @@ dependencies: "@types/geojson" "*" -"@types/leaflet@~1.8.0": +"@types/leaflet@1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.8.0.tgz#dc92d3e868fb6d5067b4b59fa08cd4441f84fabe" integrity sha512-+sXFmiJTFdhaXXIGFlV5re9AdqtAODoXbGAvxx02e5SHXL3ir7ClP5J7pahO8VmzKY3dth4RUS1nf2BTT+DW1A== @@ -7750,14 +7750,14 @@ lcov-parse@1.0.0: resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" integrity sha512-aprLII/vPzuQvYZnDRU78Fns9I2Ag3gi4Ipga/hxnVMCZC8DnR2nI7XBqrPoywGfxqIx/DgarGvDJZAD3YBTgQ== -leaflet-polylinedecorator@^1.6.0: +leaflet-polylinedecorator@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/leaflet-polylinedecorator/-/leaflet-polylinedecorator-1.6.0.tgz#9ef79fd1b5302d67b72efe959a8ecd2553f27266" integrity sha512-kn3krmZRetgvN0wjhgYL8kvyLS0tUogAl0vtHuXQnwlYNjbl7aLQpkoFUo8UB8gVZoB0dhI4Tb55VdTJAcYzzQ== dependencies: leaflet-rotatedmarker "^0.2.0" -leaflet-providers@^1.13.0: +leaflet-providers@1.13.0: version "1.13.0" resolved "https://registry.yarnpkg.com/leaflet-providers/-/leaflet-providers-1.13.0.tgz#10c843a23d5823a65096d40ad53f27029e13434b" integrity sha512-f/sN5wdgBbVA2jcCYzScIfYNxKdn2wBJP9bu+5cRX9Xj6g8Bt1G9Sr8WgJAt/ckIFIc3LVVxCBNFpSCfTuUElg== @@ -7767,17 +7767,17 @@ leaflet-rotatedmarker@^0.2.0: resolved "https://registry.yarnpkg.com/leaflet-rotatedmarker/-/leaflet-rotatedmarker-0.2.0.tgz#4467f49f98d1bfd56959bd9c6705203dd2601277" integrity sha512-yc97gxLXwbZa+Gk9VCcqI0CkvIBC9oNTTjFsHqq4EQvANrvaboib4UdeQLyTnEqDpaXHCqzwwVIDHtvz2mUiDg== -leaflet.gridlayer.googlemutant@^0.13.5: - version "0.13.5" - resolved "https://registry.yarnpkg.com/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.13.5.tgz#7182c26f479e726bff8b85a7ebf9d3f44dd3ea57" - integrity sha512-DHUEXpo1t0WZ9tpdLUHHkrTK7LldCXr/gqkV5E/4hHidDJB2srceLLCZj4PV/pl0jPdTkVBT+Lr8nL9EZDB5hw== +leaflet.gridlayer.googlemutant@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.14.1.tgz#c282209aa1a39eb2f87d8aaa4e9894181c9e20a0" + integrity sha512-/OYxEjmgxO1U1KOhTzg+m8c0b95J0943LU8DXQmdJu/x2f+1Ur78rvEPO2QCS0cmwZ3m6FvE5I3zXnBzJNWRCA== -leaflet.markercluster@^1.5.3: +leaflet.markercluster@1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz#9cdb52a4eab92671832e1ef9899669e80efc4056" integrity sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA== -leaflet@~1.8.0: +leaflet@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.8.0.tgz#4615db4a22a304e8e692cae9270b983b38a2055e" integrity sha512-gwhMjFCQiYs3x/Sf+d49f10ERXaEFCPr+nVTryhAW8DWbMGqJqt9G4XuIaHmFW08zYvhgdzqXGr8AlW8v8dQkA== From 89780d381a86ca61295d84d3d2720172fd3def4d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 26 Mar 2024 15:31:17 +0200 Subject: [PATCH 052/107] UI: Fixed not opened image gallery when used multiple-gallery-image-input.component.ts --- ...ultiple-gallery-image-input.component.html | 5 +- .../multiple-gallery-image-input.component.ts | 49 ++++++++----------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html index 4a60b29ba9..ea6a4cc603 100644 --- a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html @@ -85,12 +85,11 @@
- diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts index 260a7667f8..a1183acf88 100644 --- a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core'; +import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -24,10 +24,13 @@ import { DndDropEvent } from 'ngx-drag-drop'; import { isUndefined } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; import { ImageLinkType } from '@shared/components/image/gallery-image-input.component'; -import { TbPopoverService } from '@shared/components/popover.service'; -import { MatButton } from '@angular/material/button'; -import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component'; -import { prependTbImagePrefixToUrls, removeTbImagePrefixFromUrls } from '@shared/models/resource.models'; +import { + ImageResourceInfo, + prependTbImagePrefixToUrls, + removeTbImagePrefixFromUrls +} from '@shared/models/resource.models'; +import { MatDialog } from '@angular/material/dialog'; +import { ImageGalleryDialogComponent } from '@shared/components/image/image-gallery-dialog.component'; @Component({ selector: 'tb-multiple-gallery-image-input', @@ -66,10 +69,8 @@ export class MultipleGalleryImageInputComponent extends PageComponent implements private propagateChange = null; constructor(protected store: Store, - private cd: ChangeDetectorRef, - private renderer: Renderer2, - private viewContainerRef: ViewContainerRef, - private popoverService: TbPopoverService) { + private dialog: MatDialog, + private cd: ChangeDetectorRef) { super(store); } @@ -130,31 +131,21 @@ export class MultipleGalleryImageInputComponent extends PageComponent implements this.updateModel(); } - toggleGallery($event: Event, browseGalleryButton: MatButton) { + toggleGallery($event: Event) { if ($event) { $event.stopPropagation(); } - const trigger = browseGalleryButton._elementRef.nativeElement; - if (this.popoverService.hasPopover(trigger)) { - this.popoverService.hidePopover(trigger); - } else { - const ctx: any = { - pageMode: false, - popoverMode: true, - mode: 'grid', - selectionMode: true - }; - const imageGalleryPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, ImageGalleryComponent, 'top', true, null, - ctx, - {}, - {}, {}, true); - imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => { - imageGalleryPopover.hide(); + this.dialog.open(ImageGalleryDialogComponent, { + autoFocus: false, + disableClose: false, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe((image) => { + if (image) { this.imageUrls.push(image.link); this.updateModel(); - }); - } + } + }); } imageDragStart(index: number) { From 16d1e353feeb22c51cedd84a703942aec38be134 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Tue, 26 Mar 2024 17:10:34 +0200 Subject: [PATCH 053/107] Fix oauth --- .../edge/EdgeEventSourcingListener.java | 62 ++++++++++--------- .../processor/oauth2/OAuth2EdgeProcessor.java | 1 - .../server/edge/AbstractEdgeTest.java | 15 +++++ .../server/edge/OAuth2EdgeTest.java | 2 + 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index 6c2f81a200..d975217e1f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -160,39 +160,41 @@ public class EdgeEventSourcingListener { private boolean isValidSaveEntityEventForEdgeProcessing(SaveEntityEvent event) { Object entity = event.getEntity(); Object oldEntity = event.getOldEntity(); - switch (event.getEntityId().getEntityType()) { - case RULE_CHAIN: - if (entity instanceof RuleChain ruleChain) { - return RuleChainType.EDGE.equals(ruleChain.getType()); - } - break; - case USER: - if (entity instanceof User user) { - if (Authority.SYS_ADMIN.equals(user.getAuthority())) { - return false; + if (event.getEntityId() != null) { + switch (event.getEntityId().getEntityType()) { + case RULE_CHAIN: + if (entity instanceof RuleChain ruleChain) { + return RuleChainType.EDGE.equals(ruleChain.getType()); + } + break; + case USER: + if (entity instanceof User user) { + if (Authority.SYS_ADMIN.equals(user.getAuthority())) { + return false; + } + if (oldEntity != null) { + User oldUser = (User) oldEntity; + cleanUpUserAdditionalInfo(oldUser); + cleanUpUserAdditionalInfo(user); + return !user.equals(oldUser); + } } - if (oldEntity != null) { - User oldUser = (User) oldEntity; - cleanUpUserAdditionalInfo(oldUser); - cleanUpUserAdditionalInfo(user); - return !user.equals(oldUser); + break; + case OTA_PACKAGE: + if (entity instanceof OtaPackageInfo otaPackageInfo) { + return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData(); } - } - break; - case OTA_PACKAGE: - if (entity instanceof OtaPackageInfo otaPackageInfo) { - return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData(); - } - break; - case ALARM: - if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) { + break; + case ALARM: + if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) { + return false; + } + break; + case TENANT: + return !event.getCreated(); + case API_USAGE_STATE, EDGE: return false; - } - break; - case TENANT: - return !event.getCreated(); - case API_USAGE_STATE, EDGE: - return false; + } } // Default: If the entity doesn't match any of the conditions, consider it as valid. return true; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java index 1c62ad5de8..ce0600b491 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java @@ -55,7 +55,6 @@ public class OAuth2EdgeProcessor extends BaseEdgeProcessor { if (oAuth2Info == null) { return Futures.immediateFuture(null); } - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); return processActionForAllEdges(tenantId, type, actionType, null, JacksonUtil.toJsonNode(edgeNotificationMsg.getBody()), null); diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 0787d08fa7..e7f664477c 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -63,6 +63,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -85,6 +86,7 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; +import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; @@ -140,6 +142,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { installation(); edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); + edgeImitator.ignoreType(OAuth2UpdateMsg.class); edgeImitator.expectMessageAmount(21); edgeImitator.connect(); @@ -538,6 +541,18 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(customer.isPublic()); } + private void validateOAuth2() throws Exception { + Optional oAuth2UpdateMsgOpt = edgeImitator.findMessageByType(OAuth2UpdateMsg.class); + Assert.assertTrue(oAuth2UpdateMsgOpt.isPresent()); + OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2UpdateMsgOpt.get(); + OAuth2Info oAuth2Info = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); + Assert.assertNotNull(oAuth2Info); + OAuth2Info auth2Info = doGet("/api/oauth2/config", OAuth2Info.class); + Assert.assertNotNull(auth2Info); + Assert.assertEquals(oAuth2Info, auth2Info); + testAutoGeneratedCodeByProtobuf(oAuth2UpdateMsg); + } + private void validateSyncCompleted() { Optional syncCompletedMsgOpt = edgeImitator.findMessageByType(SyncCompletedMsg.class); Assert.assertTrue(syncCompletedMsgOpt.isPresent()); diff --git a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java index b3cc133f4b..b5ed5e788c 100644 --- a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java @@ -43,6 +43,7 @@ public class OAuth2EdgeTest extends AbstractEdgeTest { loginSysAdmin(); // enable oauth, verify nothing sent to edge + edgeImitator.allowIgnoredTypes(); edgeImitator.expectMessageAmount(1); OAuth2Info oAuth2Info = createDefaultOAuth2Info(); oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); @@ -65,6 +66,7 @@ public class OAuth2EdgeTest extends AbstractEdgeTest { result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); Assert.assertEquals(oAuth2Info, result); + edgeImitator.ignoreType(OAuth2UpdateMsg.class); loginTenantAdmin(); } From 11f16dd3e26f3afd0c04b76a01fa75f0fb53e5aa Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 26 Mar 2024 18:01:51 +0200 Subject: [PATCH 054/107] UI: Update echarts dependency to version 5.5.0-TB --- ui-ngx/package.json | 2 +- ui-ngx/patches/echarts+5.5.0.patch | 426 ------------------ .../bar-chart-with-labels-widget.component.ts | 5 +- .../widget/lib/chart/echarts-widget.models.ts | 90 ++-- .../lib/chart/range-chart-widget.component.ts | 5 +- .../lib/chart/time-series-chart.models.ts | 5 +- .../widget/lib/chart/time-series-chart.ts | 24 +- ui-ngx/yarn.lock | 7 +- 8 files changed, 65 insertions(+), 499 deletions(-) delete mode 100644 ui-ngx/patches/echarts+5.5.0.patch diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 606864b7cd..a5304207f5 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -55,7 +55,7 @@ "core-js": "^3.29.1", "date-fns": "2.0.0-alpha.27", "dayjs": "1.11.4", - "echarts": "^5.5.0", + "echarts": "https://github.com/thingsboard/echarts/archive/5.5.0-TB.tar.gz", "flot": "https://github.com/thingsboard/flot.git#0.9-work", "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master", "font-awesome": "^4.7.0", diff --git a/ui-ngx/patches/echarts+5.5.0.patch b/ui-ngx/patches/echarts+5.5.0.patch deleted file mode 100644 index 4f1ebed81a..0000000000 --- a/ui-ngx/patches/echarts+5.5.0.patch +++ /dev/null @@ -1,426 +0,0 @@ -diff --git a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js -index d6c05f3..aafb0b8 100644 ---- a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js -+++ b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js -@@ -362,7 +362,10 @@ var DataZoomModel = /** @class */function (_super) { - return axisProxy.getDataValueWindow(); - } - } else { -- return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow(); -+ var axisProxy = this.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ return axisProxy.getDataValueWindow(); -+ } - } - }; - /** -@@ -381,10 +384,10 @@ var DataZoomModel = /** @class */function (_super) { - var axisInfo = this._targetAxisInfoMap.get(axisDim); - for (var j = 0; j < axisInfo.indexList.length; j++) { - var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]); -- if (proxy.hostedBy(this)) { -+ if (proxy && proxy.hostedBy(this)) { - return proxy; - } -- if (!firstProxy) { -+ if (proxy && !firstProxy) { - firstProxy = proxy; - } - } -diff --git a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js -index 06469b2..cf0b2ea 100644 ---- a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js -+++ b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js -@@ -96,11 +96,14 @@ var getRangeHandlers = { - range[0] = (range[0] - percentPoint) * scale + percentPoint; - range[1] = (range[1] - percentPoint) * scale + percentPoint; - // Restrict range. -- var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); -- sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); -- this.range = range; -- if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { -- return range; -+ var proxy = this.dataZoomModel.findRepresentativeAxisProxy(); -+ if (proxy) { -+ var minMaxSpan = proxy.getMinMaxSpan(); -+ sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); -+ this.range = range; -+ if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { -+ return range; -+ } - } - }, - pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) { -diff --git a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js -index 98912e0..0cda6be 100644 ---- a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js -+++ b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js -@@ -64,7 +64,7 @@ var DEFAULT_MOVE_HANDLE_SIZE = 7; - var HORIZONTAL = 'horizontal'; - var VERTICAL = 'vertical'; - var LABEL_GAP = 5; --var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; -+var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter', 'custom']; - var REALTIME_ANIMATION_CONFIG = { - easing: 'cubicOut', - duration: 100, -@@ -359,30 +359,33 @@ var SliderZoomView = /** @class */function (_super) { - var result; - var ecModel = this.ecModel; - dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { -- var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels(); -- each(seriesModels, function (seriesModel) { -- if (result) { -- return; -- } -- if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { -- return; -- } -- var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; -- var otherDim = getOtherDim(axisDim); -- var otherAxisInverse; -- var coordSys = seriesModel.coordinateSystem; -- if (otherDim != null && coordSys.getOtherAxis) { -- otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; -- } -- otherDim = seriesModel.getData().mapDimension(otherDim); -- result = { -- thisAxis: thisAxis, -- series: seriesModel, -- thisDim: axisDim, -- otherDim: otherDim, -- otherAxisInverse: otherAxisInverse -- }; -- }, this); -+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ var seriesModels = axisProxy.getTargetSeriesModels(); -+ each(seriesModels, function (seriesModel) { -+ if (result) { -+ return; -+ } -+ if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { -+ return; -+ } -+ var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; -+ var otherDim = getOtherDim(axisDim); -+ var otherAxisInverse; -+ var coordSys = seriesModel.coordinateSystem; -+ if (otherDim != null && coordSys.getOtherAxis) { -+ otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; -+ } -+ otherDim = seriesModel.getData().mapDimension(otherDim); -+ result = { -+ thisAxis: thisAxis, -+ series: seriesModel, -+ thisDim: axisDim, -+ otherDim: otherDim, -+ otherAxisInverse: otherAxisInverse -+ }; -+ }, this); -+ } - }, this); - return result; - }; -@@ -530,12 +533,17 @@ var SliderZoomView = /** @class */function (_super) { - var dataZoomModel = this.dataZoomModel; - var handleEnds = this._handleEnds; - var viewExtend = this._getViewExtent(); -- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); -- var percentExtent = [0, 100]; -- sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); -- var lastRange = this._range; -- var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); -- return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; -+ var proxy = dataZoomModel.findRepresentativeAxisProxy(); -+ if (proxy) { -+ var minMaxSpan = proxy.getMinMaxSpan(); -+ var percentExtent = [0, 100]; -+ sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); -+ var lastRange = this._range; -+ var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); -+ return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; -+ } else { -+ return false; -+ } - }; - SliderZoomView.prototype._updateView = function (nonRealtime) { - var displaybles = this._displayables; -diff --git a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js -index ce98fed..e154118 100644 ---- a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js -+++ b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js -@@ -90,7 +90,10 @@ var dataZoomProcessor = { - // init stage and not after action dispatch handler, because - // reset should be called after seriesData.restoreData. - dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { -- dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel); -+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ axisProxy.reset(dataZoomModel); -+ } - }); - // Caution: data zoom filtering is order sensitive when using - // percent range and no min/max/scale set on axis. -@@ -107,7 +110,10 @@ var dataZoomProcessor = { - // So we should filter x-axis after reset x-axis immediately, - // and then reset y-axis and filter y-axis. - dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { -- dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api); -+ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); -+ if (axisProxy) { -+ axisProxy.filterData(dataZoomModel, api); -+ } - }); - }); - ecModel.eachComponent('dataZoom', function (dataZoomModel) { -diff --git a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js -index cf8d6bc..f9b9f90 100644 ---- a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js -+++ b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js -@@ -109,9 +109,12 @@ var DataZoomFeature = /** @class */function (_super) { - var axisModel = axis.model; - var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); - // Restrict range. -- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); -- if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { -- minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); -+ var proxy = dataZoomModel.findRepresentativeAxisProxy(axisModel); -+ if (proxy) { -+ var minMaxSpan = proxy.getMinMaxSpan(); -+ if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { -+ minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); -+ } - } - dataZoomModel && (snapshot[dataZoomModel.id] = { - dataZoomId: dataZoomModel.id, -diff --git a/node_modules/echarts/lib/component/tooltip/TooltipView.js b/node_modules/echarts/lib/component/tooltip/TooltipView.js -index b8a9b95..11e49c0 100644 ---- a/node_modules/echarts/lib/component/tooltip/TooltipView.js -+++ b/node_modules/echarts/lib/component/tooltip/TooltipView.js -@@ -360,7 +360,7 @@ var TooltipView = /** @class */function (_super) { - each(itemCoordSys.dataByAxis, function (axisItem) { - var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex); - var axisValue = axisItem.value; -- if (!axisModel || axisValue == null) { -+ if (!axisModel || !axisModel.axis || axisValue == null) { - return; - } - var axisValueLabel = axisPointerViewHelper.getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt); -diff --git a/node_modules/echarts/lib/coord/axisHelper.js b/node_modules/echarts/lib/coord/axisHelper.js -index a76c66b..e5b7ee5 100644 ---- a/node_modules/echarts/lib/coord/axisHelper.js -+++ b/node_modules/echarts/lib/coord/axisHelper.js -@@ -187,7 +187,9 @@ export function createScaleByModel(model, axisType) { - }); - default: - // case 'value'/'interval', 'log', or others. -- return new (Scale.getClass(axisType) || IntervalScale)(); -+ return new (Scale.getClass(axisType) || IntervalScale)({ -+ ticksGenerator: model.get('ticksGenerator') -+ }); - } - } - } -diff --git a/node_modules/echarts/lib/coord/cartesian/Grid.js b/node_modules/echarts/lib/coord/cartesian/Grid.js -index 5b18f02..39a57f8 100644 ---- a/node_modules/echarts/lib/coord/cartesian/Grid.js -+++ b/node_modules/echarts/lib/coord/cartesian/Grid.js -@@ -91,11 +91,11 @@ var Grid = /** @class */function () { - var scale = axis.scale; - if ( - // Only value and log axis without interval support alignTicks. -- isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) { -+ isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null && model.get('ticksGenerator') == null) { - axisNeedsAlign.push(axis); - } else { - niceScaleExtent(scale, model); -- if (isIntervalOrLogScale(scale)) { -+ if (isIntervalOrLogScale(scale) && !scale.isBlank()) { - // Can only align to interval or log axis. - alignTo = axis; - } -@@ -105,10 +105,15 @@ var Grid = /** @class */function () { - // All axes has set alignTicks. Pick the first one. - // PENDING. Should we find the axis that both set interval, min, max and align to this one? - if (axisNeedsAlign.length) { -- if (!alignTo) { -- alignTo = axisNeedsAlign.pop(); -- niceScaleExtent(alignTo.scale, alignTo.model); -+ while (!alignTo && axisNeedsAlign.length) { -+ var axis = axisNeedsAlign.pop(); -+ niceScaleExtent(axis.scale, axis.model); -+ if (!axis.scale.isBlank()) { -+ alignTo = axis; -+ } - } -+ } -+ if (axisNeedsAlign.length && alignTo) { - each(axisNeedsAlign, function (axis) { - alignScaleTicks(axis.scale, axis.model, alignTo.scale); - }); -diff --git a/node_modules/echarts/lib/data/SeriesData.js b/node_modules/echarts/lib/data/SeriesData.js -index 98d5ce8..1c293a6 100644 ---- a/node_modules/echarts/lib/data/SeriesData.js -+++ b/node_modules/echarts/lib/data/SeriesData.js -@@ -900,13 +900,16 @@ var SeriesData = /** @class */function () { - var dimInfo = data._dimInfos[dim]; - // Currently, only dimensions that has ordinalMeta can create inverted indices. - var ordinalMeta = dimInfo.ordinalMeta; -+ var stack = dimInfo.stack; - var store = data._store; -- if (ordinalMeta) { -- invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(ordinalMeta.categories.length); -- // The default value of TypedArray is 0. To avoid miss -- // mapping to 0, we should set it as INDEX_NOT_FOUND. -- for (var i = 0; i < invertedIndices.length; i++) { -- invertedIndices[i] = INDEX_NOT_FOUND; -+ if (ordinalMeta || stack) { -+ invertedIndices = invertedIndicesMap[dim] = stack ? new Array(store.count()) : new CtorInt32Array(ordinalMeta.categories.length); -+ if (ordinalMeta) { -+ // The default value of TypedArray is 0. To avoid miss -+ // mapping to 0, we should set it as INDEX_NOT_FOUND. -+ for (var i = 0; i < invertedIndices.length; i++) { -+ invertedIndices[i] = INDEX_NOT_FOUND; -+ } - } - for (var i = 0; i < store.count(); i++) { - // Only support the case that all values are distinct. -diff --git a/node_modules/echarts/lib/data/Source.js b/node_modules/echarts/lib/data/Source.js -index 7dda49b..2dd2b98 100644 ---- a/node_modules/echarts/lib/data/Source.js -+++ b/node_modules/echarts/lib/data/Source.js -@@ -252,7 +252,8 @@ function normalizeDimensionsOption(dimensionsDefine) { - var item = { - name: rawItem.name, - displayName: rawItem.displayName, -- type: rawItem.type -+ type: rawItem.type, -+ stack: rawItem.stack - }; - // User can set null in dimensions. - // We don't auto specify name, otherwise a given name may -diff --git a/node_modules/echarts/lib/data/helper/createDimensions.js b/node_modules/echarts/lib/data/helper/createDimensions.js -index 00d7eb7..dd514b1 100644 ---- a/node_modules/echarts/lib/data/helper/createDimensions.js -+++ b/node_modules/echarts/lib/data/helper/createDimensions.js -@@ -110,6 +110,9 @@ source, opt) { - } - dimDefItem.type != null && (resultItem.type = dimDefItem.type); - dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName); -+ if (dimDefItem.stack) { -+ resultItem.stack = true; -+ } - var newIdx = resultList.length; - indicesMap[dimIdx] = newIdx; - resultItem.storeDimIndex = dimIdx; -diff --git a/node_modules/echarts/lib/data/helper/dataStackHelper.js b/node_modules/echarts/lib/data/helper/dataStackHelper.js -index c25de1b..ea8300d 100644 ---- a/node_modules/echarts/lib/data/helper/dataStackHelper.js -+++ b/node_modules/echarts/lib/data/helper/dataStackHelper.js -@@ -91,7 +91,7 @@ export function enableDataStack(seriesModel, dimensionsInput, opt) { - } - if (mayStack && !dimensionInfo.isExtraCoord) { - // Find the first ordinal dimension as the stackedByDimInfo. -- if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { -+ if (!byIndex && !stackedByDimInfo && (dimensionInfo.ordinalMeta || dimensionInfo.stack)) { - stackedByDimInfo = dimensionInfo; - } - // Find the first stackable dimension as the stackedDimInfo. -diff --git a/node_modules/echarts/lib/scale/Interval.js b/node_modules/echarts/lib/scale/Interval.js -index 1094662..363c0a5 100644 ---- a/node_modules/echarts/lib/scale/Interval.js -+++ b/node_modules/echarts/lib/scale/Interval.js -@@ -46,12 +46,17 @@ import * as numberUtil from '../util/number.js'; - import * as formatUtil from '../util/format.js'; - import Scale from './Scale.js'; - import * as helper from './helper.js'; -+import { isFunction } from 'zrender/lib/core/util.js'; - var roundNumber = numberUtil.round; - var IntervalScale = /** @class */function (_super) { - __extends(IntervalScale, _super); -- function IntervalScale() { -- var _this = _super !== null && _super.apply(this, arguments) || this; -+ function IntervalScale(setting) { -+ var _this = _super.call(this, setting) || this; - _this.type = 'interval'; -+ var ticksGenerator = _this.getSetting('ticksGenerator'); -+ if (isFunction(ticksGenerator)) { -+ _this._ticksGenerator = ticksGenerator; -+ } - // Step is calculated in adjustExtent. - _this._interval = 0; - _this._intervalPrecision = 2; -@@ -104,7 +109,17 @@ var IntervalScale = /** @class */function (_super) { - var extent = this._extent; - var niceTickExtent = this._niceExtent; - var intervalPrecision = this._intervalPrecision; -- var ticks = []; -+ var ticksGenerator = this._ticksGenerator; -+ var ticks; -+ if (ticksGenerator) { -+ try { -+ ticks = ticksGenerator(extent, interval, niceTickExtent, intervalPrecision); -+ if (ticks) { -+ return ticks; -+ } -+ } catch (_e) {} -+ } -+ ticks = []; - // If interval is 0, return []; - if (!interval) { - return ticks; -diff --git a/node_modules/echarts/types/dist/shared.d.ts b/node_modules/echarts/types/dist/shared.d.ts -index ca74097..ef41ce2 100644 ---- a/node_modules/echarts/types/dist/shared.d.ts -+++ b/node_modules/echarts/types/dist/shared.d.ts -@@ -2422,6 +2422,9 @@ interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMixin { - max: number; - }) => ScaleDataValue); - } -+ -+declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; -+ - interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - boundaryGap?: [number | string, number | string]; - /** -@@ -2447,6 +2450,8 @@ interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - * Will be ignored if interval is set. - */ - alignTicks?: boolean; -+ -+ ticksGenerator?: NumericAxisTicksGenerator; - } - interface CategoryAxisBaseOption extends AxisBaseOptionCommon { - type?: 'category'; -@@ -6412,6 +6417,7 @@ declare type DimensionDefinition = { - type?: DataStoreDimensionType; - name?: DimensionName; - displayName?: string; -+ stack?: boolean; - }; - declare type DimensionDefinitionLoose = DimensionDefinition['name'] | DimensionDefinition; - declare const SOURCE_FORMAT_ORIGINAL: "original"; -diff --git a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts -index c5c2792..d524b70 100644 ---- a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts -+++ b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts -@@ -56,6 +56,9 @@ export interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMi - max: number; - }) => ScaleDataValue); - } -+ -+export declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[]; -+ - export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - boundaryGap?: [number | string, number | string]; - /** -@@ -81,6 +84,8 @@ export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon { - * Will be ignored if interval is set. - */ - alignTicks?: boolean; -+ -+ ticksGenerator?: NumericAxisTicksGenerator; - } - export interface CategoryAxisBaseOption extends AxisBaseOptionCommon { - type?: 'category'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts index da7f4d4e40..e2779f2821 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts @@ -54,7 +54,7 @@ import { echartsModule, EChartsOption, EChartsSeriesItem, - echartsTooltipFormatter, + echartsTooltipFormatter, timeAxisBandWidthCalculator, toNamedData } from '@home/components/widget/lib/chart/echarts-widget.models'; import { AggregationType, IntervalMath } from '@shared/models/time/time.models'; @@ -424,7 +424,8 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft onZero: false }, min: this.ctx.defaultSubscription.timeWindow.minTime, - max: this.ctx.defaultSubscription.timeWindow.maxTime + max: this.ctx.defaultSubscription.timeWindow.maxTime, + bandWidthCalculator: timeAxisBandWidthCalculator }, yAxis: { type: 'value', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts index 7fdd1fbfff..816f45a7e8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts @@ -15,11 +15,9 @@ /// import * as echarts from 'echarts/core'; -import { Axis } from 'echarts'; import AxisModel from 'echarts/types/src/coord/cartesian/AxisModel'; import { estimateLabelUnionRect } from 'echarts/lib/coord/axisHelper'; import { formatValue, isDefinedAndNotNull } from '@core/utils'; -import TimeScale from 'echarts/types/src/scale/Time'; import { DataZoomComponent, DataZoomComponentOption, @@ -51,7 +49,7 @@ import { IntervalMath, WidgetTimewindow } from '@shared/models/time/time.models'; -import { CallbackDataParams } from 'echarts/types/dist/shared'; +import { CallbackDataParams, TimeAxisBandWidthCalculator } from 'echarts/types/dist/shared'; import { Renderer2 } from '@angular/core'; import { DateFormatProcessor, DateFormatSettings, Font } from '@shared/models/widget-settings.models'; import GlobalModel from 'echarts/types/src/model/Global'; @@ -63,53 +61,6 @@ class EChartsModule { init() { if (!this.initialized) { - const axisGetBandWidth = Axis.prototype.getBandWidth; - - Axis.prototype.getBandWidth = function(){ - const model: AxisModel = this.model; - const axisOption = model.option; - if (this.scale.type === 'time') { - let interval: number; - const seriesDataIndices = axisOption.axisPointer?.seriesDataIndices; - if (seriesDataIndices?.length) { - const seriesDataIndex = seriesDataIndices[0]; - const series = model.ecModel.getSeriesByIndex(seriesDataIndex.seriesIndex); - if (series) { - const values = series.getData().getValues(seriesDataIndex.dataIndex); - const start = values[2]; - const end = values[3]; - if (typeof start === 'number' && typeof end === 'number') { - interval = Math.max(end - start, 1); - } - } - } - if (!interval) { - const tbTimeWindow: WidgetTimewindow = (axisOption as any).tbTimeWindow; - if (isDefinedAndNotNull(tbTimeWindow)) { - if (axisOption.axisPointer?.value && typeof axisOption.axisPointer?.value === 'number') { - const intervalArray = calculateAggIntervalWithWidgetTimeWindow(tbTimeWindow, axisOption.axisPointer.value); - const start = intervalArray[0]; - const end = intervalArray[1]; - interval = Math.max(end - start, 1); - } else { - interval = IntervalMath.numberValue(tbTimeWindow.interval); - } - } - } - if (interval) { - const timeScale: TimeScale = this.scale; - const axisExtent: [number, number] = this._extent; - const dataExtent = timeScale.getExtent(); - const size = Math.abs(axisExtent[1] - axisExtent[0]); - return interval * (size / (dataExtent[1] - dataExtent[0])); - } else { - return axisGetBandWidth.call(this); - } - } else { - return axisGetBandWidth.call(this); - } - }; - echarts.use([ TooltipComponent, GridComponent, @@ -124,7 +75,6 @@ class EChartsModule { CanvasRenderer, SVGRenderer ]); - this.initialized = true; } } @@ -160,6 +110,44 @@ export type EChartsSeriesItem = { decimals?: number; }; +export const timeAxisBandWidthCalculator: TimeAxisBandWidthCalculator = (model) => { + let interval: number; + const axisOption = model.option; + const seriesDataIndices = axisOption.axisPointer?.seriesDataIndices; + if (seriesDataIndices?.length) { + const seriesDataIndex = seriesDataIndices[0]; + const series = model.ecModel.getSeriesByIndex(seriesDataIndex.seriesIndex); + if (series) { + const values = series.getData().getValues(seriesDataIndex.dataIndex); + const start = values[2]; + const end = values[3]; + if (typeof start === 'number' && typeof end === 'number') { + interval = Math.max(end - start, 1); + } + } + } + if (!interval) { + const tbTimeWindow: WidgetTimewindow = (axisOption as any).tbTimeWindow; + if (isDefinedAndNotNull(tbTimeWindow)) { + if (axisOption.axisPointer?.value && typeof axisOption.axisPointer?.value === 'number') { + const intervalArray = calculateAggIntervalWithWidgetTimeWindow(tbTimeWindow, axisOption.axisPointer.value); + const start = intervalArray[0]; + const end = intervalArray[1]; + interval = Math.max(end - start, 1); + } else { + interval = IntervalMath.numberValue(tbTimeWindow.interval); + } + } + } + if (interval) { + const timeScale = model.axis.scale; + const axisExtent = model.axis.getExtent(); + const dataExtent = timeScale.getExtent(); + const size = Math.abs(axisExtent[1] - axisExtent[0]); + return interval * (size / (dataExtent[1] - dataExtent[0])); + } +}; + export const getXAxis = (chart: ECharts): Axis2D => { const model: GlobalModel = (chart as any).getModel(); const models = model.queryComponents({mainType: 'xAxis'}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index 1705074a1e..1ef756f1f7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -50,7 +50,7 @@ import { ECharts, echartsModule, EChartsOption, - echartsTooltipFormatter, + echartsTooltipFormatter, timeAxisBandWidthCalculator, toNamedData } from '@home/components/widget/lib/chart/echarts-widget.models'; import { CallbackDataParams } from 'echarts/types/dist/shared'; @@ -356,7 +356,8 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn onZero: false }, min: this.ctx.defaultSubscription.timeWindow.minTime, - max: this.ctx.defaultSubscription.timeWindow.maxTime + max: this.ctx.defaultSubscription.timeWindow.maxTime, + bandWidthCalculator: timeAxisBandWidthCalculator }, yAxis: { type: 'value', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index 6f985466c4..e931349bca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -20,7 +20,7 @@ import { EChartsSeriesItem, EChartsTooltipTrigger, EChartsTooltipWidgetSettings, - measureThresholdLabelOffset + measureThresholdLabelOffset, timeAxisBandWidthCalculator } from '@home/components/widget/lib/chart/echarts-widget.models'; import { autoDateFormat, @@ -969,7 +969,8 @@ export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettin } }, min, - max + max, + bandWidthCalculator: timeAxisBandWidthCalculator }; }; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 65a323e4b6..bd9005c497 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -140,8 +140,8 @@ export class TbTimeSeriesChart { timeSeriesChartDefaultSettings, this.inputSettings as TimeSeriesChartSettings); const $dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page'); - const dashboardPageElement = $($dashboardPageElement[$dashboardPageElement.length-1]); - this.darkMode = this.settings.darkMode || dashboardPageElement.hasClass('dark'); + const dashboardPageElement = $dashboardPageElement.length ? $($dashboardPageElement[$dashboardPageElement.length-1]) : null; + this.darkMode = this.settings.darkMode || dashboardPageElement?.hasClass('dark'); this.setupYAxes(); this.setupData(); this.setupThresholds(); @@ -155,15 +155,17 @@ export class TbTimeSeriesChart { }); this.shapeResize$.observe(this.chartElement); } - this.darkModeObserver = new MutationObserver(mutations => { - for(let mutation of mutations) { - if (mutation.type === 'attributes' && mutation.attributeName === 'class') { - const darkMode = dashboardPageElement.hasClass('dark'); - this.setDarkMode(darkMode); + if (dashboardPageElement) { + this.darkModeObserver = new MutationObserver(mutations => { + for (const mutation of mutations) { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + const darkMode = dashboardPageElement.hasClass('dark'); + this.setDarkMode(darkMode); + } } - } - }); - this.darkModeObserver.observe(dashboardPageElement[0], { attributes: true }); + }); + this.darkModeObserver.observe(dashboardPageElement[0], {attributes: true}); + } } public update(): void { @@ -270,7 +272,7 @@ export class TbTimeSeriesChart { } this.yMinSubject.complete(); this.yMaxSubject.complete(); - this.darkModeObserver.disconnect(); + this.darkModeObserver?.disconnect(); } public resize(): void { diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 33362767bd..ddcd48243b 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -5437,10 +5437,9 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -echarts@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.5.0.tgz#c13945a7f3acdd67c134d8a9ac67e917830113ac" - integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== +"echarts@https://github.com/thingsboard/echarts/archive/5.5.0-TB.tar.gz": + version "5.5.0-TB" + resolved "https://github.com/thingsboard/echarts/archive/5.5.0-TB.tar.gz#d1017728576b3fd65532eb17e503d1349f7feaed" dependencies: tslib "2.3.0" zrender "5.5.0" From 79803292b6956e6cd805a1fb033e9be73ff5be0c Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 26 Mar 2024 18:03:54 +0200 Subject: [PATCH 055/107] Fix time series charts initialization --- .../widget/lib/chart/time-series-chart.ts | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 462825087e..bd9005c497 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -139,8 +139,9 @@ export class TbTimeSeriesChart { this.settings = mergeDeep({} as TimeSeriesChartSettings, timeSeriesChartDefaultSettings, this.inputSettings as TimeSeriesChartSettings); - const dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page'); - this.darkMode = this.settings.darkMode || dashboardPageElement.hasClass('dark'); + const $dashboardPageElement = this.ctx.$containerParent.parents('.tb-dashboard-page'); + const dashboardPageElement = $dashboardPageElement.length ? $($dashboardPageElement[$dashboardPageElement.length-1]) : null; + this.darkMode = this.settings.darkMode || dashboardPageElement?.hasClass('dark'); this.setupYAxes(); this.setupData(); this.setupThresholds(); @@ -154,15 +155,17 @@ export class TbTimeSeriesChart { }); this.shapeResize$.observe(this.chartElement); } - this.darkModeObserver = new MutationObserver(mutations => { - for(let mutation of mutations) { - if (mutation.type === 'attributes' && mutation.attributeName === 'class') { - const darkMode = dashboardPageElement.hasClass('dark'); - this.setDarkMode(darkMode); + if (dashboardPageElement) { + this.darkModeObserver = new MutationObserver(mutations => { + for (const mutation of mutations) { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + const darkMode = dashboardPageElement.hasClass('dark'); + this.setDarkMode(darkMode); + } } - } - }); - this.darkModeObserver.observe(dashboardPageElement[0], { attributes: true }); + }); + this.darkModeObserver.observe(dashboardPageElement[0], {attributes: true}); + } } public update(): void { @@ -269,7 +272,7 @@ export class TbTimeSeriesChart { } this.yMinSubject.complete(); this.yMaxSubject.complete(); - this.darkModeObserver.disconnect(); + this.darkModeObserver?.disconnect(); } public resize(): void { From eba645c542e4f06ff67ce8f3bf7154f3ba4e51fc Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Mar 2024 19:25:44 +0100 Subject: [PATCH 056/107] Nashorn LOCAL_JS_SANDBOX_MAX_MEMORY introduced --- application/src/main/resources/thingsboard.yml | 2 ++ .../script/api/AbstractScriptInvokeService.java | 4 ++-- .../thingsboard/script/api/js/NashornJsInvokeService.java | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index daf9f5d1c1..01f0d83771 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -866,6 +866,8 @@ js: monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" # Maximum CPU time in milliseconds allowed for script execution max_cpu_time: "${LOCAL_JS_SANDBOX_MAX_CPU_TIME:8000}" + # Maximum memory in Bytes which JS executor thread can allocate (approximate calculation). A zero memory limit in combination with a non-zero CPU limit is not recommended due to the implementation of Nashorn 0.4.2. 100MiB is effectively unlimited for most cases + max_memory: "${LOCAL_JS_SANDBOX_MAX_MEMORY:104857600}" # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted max_errors: "${LOCAL_JS_SANDBOX_MAX_ERRORS:3}" # JS Eval max request timeout. 0 - no timeout diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java index f88625f7fd..32e209b2ea 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java @@ -145,14 +145,14 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout()); var task = doInvokeFunction(scriptId, args); - var resultFuture = Futures.transformAsync(task.getResultFuture(), output -> { + var resultFuture = Futures.transform(task.getResultFuture(), output -> { String result = JacksonUtil.toString(output); if (resultSizeExceeded(result)) { throw new TbScriptException(scriptId, TbScriptException.ErrorCode.OTHER, null, new RuntimeException( format("Script invocation result exceeds maximum allowed size of %s symbols", getMaxResultSize()) )); } - return Futures.immediateFuture(output); + return output; }, MoreExecutors.directExecutor()); return withTimeoutAndStatsCallback(scriptId, task, resultFuture, invokeCallback, getMaxInvokeRequestsTimeout()); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java index 0e37bd89d6..6a07e596ef 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/NashornJsInvokeService.java @@ -41,7 +41,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; @Slf4j @@ -65,6 +64,9 @@ public class NashornJsInvokeService extends AbstractJsInvokeService { @Value("${js.local.max_cpu_time}") private long maxCpuTime; + @Value("${js.local.max_memory}") + private long maxMemory; + @Getter @Value("${js.local.max_errors}") private int maxErrors; @@ -107,12 +109,13 @@ public class NashornJsInvokeService extends AbstractJsInvokeService { @Override public void init() { super.init(); - jsExecutor = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(jsExecutorThreadPoolSize)); + jsExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(jsExecutorThreadPoolSize, "nashorn-js-executor")); if (useJsSandbox) { sandbox = NashornSandboxes.create(); monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(monitorThreadPoolSize, "nashorn-js-monitor"); sandbox.setExecutor(monitorExecutorService); sandbox.setMaxCPUTime(maxCpuTime); + sandbox.setMaxMemory(maxMemory); sandbox.allowNoBraces(false); sandbox.allowLoadFunctions(true); sandbox.setMaxPreparedStatements(30); From 3d1f1e4af0ccc844fd9c752af1db26df99a943ee Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Mar 2024 19:27:23 +0100 Subject: [PATCH 057/107] NashornJsInvokeServiceTest refactored --- .../script/NashornJsInvokeServiceTest.java | 31 +++++++++++-------- .../src/test/resources/logback-test.xml | 2 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java index 55c521c309..bb9dc57d3c 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.service.script; -import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.test.context.TestPropertySource; -import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.TbStopWatch; import org.thingsboard.script.api.ScriptType; import org.thingsboard.script.api.js.NashornJsInvokeService; import org.thingsboard.server.common.data.id.TenantId; @@ -32,6 +32,7 @@ import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; @@ -41,8 +42,9 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE "js.max_script_body_size=50", "js.max_total_args_size=50", "js.max_result_size=50", - "js.local.max_errors=2" + "js.local.max_errors=2", }) +@Slf4j class NashornJsInvokeServiceTest extends AbstractControllerTest { @Autowired @@ -56,23 +58,26 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { int iterations = 1000; UUID scriptId = evalScript("return msg.temperature > 20"); // warmup - ObjectNode msg = JacksonUtil.newObjectNode(); - for (int i = 0; i < 100; i++) { - msg.put("temperature", i); + log.info("Warming up 1000 times..."); + var warmupWatch = TbStopWatch.create(); + for (int i = 0; i < 1000; i++) { boolean expected = i > 20; - boolean result = Boolean.valueOf(invokeScript(scriptId, JacksonUtil.toString(msg))); + boolean result = Boolean.parseBoolean(invokeScript(scriptId, "{\"temperature\":" + i + "}")); Assert.assertEquals(expected, result); } - long startTs = System.currentTimeMillis(); + log.info("Warming up finished in {} ms", warmupWatch.stopAndGetTotalTimeMillis()); + log.info("Starting performance test..."); + var watch = TbStopWatch.create(); for (int i = 0; i < iterations; i++) { - msg.put("temperature", i); boolean expected = i > 20; - boolean result = Boolean.valueOf(invokeScript(scriptId, JacksonUtil.toString(msg))); + boolean result = Boolean.parseBoolean(invokeScript(scriptId, "{\"temperature\":" + i + "}")); + log.debug("asserting result"); Assert.assertEquals(expected, result); } - long duration = System.currentTimeMillis() - startTs; - System.out.println(iterations + " invocations took: " + duration + "ms"); - Assert.assertTrue(duration < TimeUnit.MINUTES.toMillis(4)); + long duration = watch.stopAndGetTotalTimeMillis(); + log.info("Performance test with {} invocations took: {} ms", iterations, duration); + assertThat(duration).as("duration ms") + .isLessThan(TimeUnit.MINUTES.toMillis(1)); // effective exec time is about 500ms } @Test diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 981bcab132..d72bccb7a6 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -16,7 +16,7 @@ - + From cd722a11484bf3f54cdeb628135af7018ac36f65 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Mar 2024 19:34:13 +0100 Subject: [PATCH 058/107] bump delight-nashorn-sandbox.version to the latest 0.4.2. As it supports Java 17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2450e0a65..f8e6c1ec1f 100755 --- a/pom.xml +++ b/pom.xml @@ -103,7 +103,7 @@ org/thingsboard/server/extensions/core/plugin/telemetry/gen/**/* 5.0.2 - 0.2.1 + 0.4.2 15.4 -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss index feb65353f6..ac7229672e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.scss @@ -19,8 +19,10 @@ position: relative; display: flex; flex-direction: column; - gap: 16px; - padding: 20px 24px 24px 24px; + gap: 8px; + &.overlay { + padding: 20px 24px 24px 24px; + } > div:not(.tb-bar-chart-overlay) { z-index: 1; } @@ -40,7 +42,7 @@ min-height: 0; display: flex; flex-direction: column; - gap: 16px; + gap: 8px; &.legend-top { flex-direction: column-reverse; } @@ -88,8 +90,9 @@ } } &.legend-right, &.legend-left { - gap: 24px; + gap: 16px; .tb-bar-chart-legend { + padding-top: 8px; flex-direction: column-reverse; justify-content: flex-end; align-items: stretch; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts index 5aaa2671ea..b840f49e3b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts @@ -65,6 +65,8 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; + overlayEnabled: boolean; + padding: string; legendKeys: DataKey[]; legendLabelStyle: ComponentStyle; @@ -84,6 +86,8 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); + this.overlayEnabled = this.settings.background.overlay.enabled; + this.padding = this.overlayEnabled ? undefined : this.settings.padding; this.showLegend = this.settings.showLegend; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index 153a038d1e..8d99a7bd3f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -44,6 +44,7 @@ export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSe legendLabelFont: Font; legendLabelColor: string; background: BackgroundSettings; + padding: string; } export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings = { @@ -110,7 +111,8 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings color: 'rgba(255,255,255,0.72)', blur: 3 } - } + }, + padding: '12px' }; export const barChartWithLabelsTimeSeriesSettings = (settings: BarChartWithLabelsWidgetSettings): DeepPartial => ({ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html index 6d3b6d43db..63a549bb48 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html @@ -15,7 +15,8 @@ limitations under the License. --> -
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss index de2ca546fc..d956173281 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss @@ -19,8 +19,10 @@ position: relative; display: flex; flex-direction: column; - gap: 16px; - padding: 20px 24px 24px 24px; + gap: 8px; + &.overlay { + padding: 20px 24px 24px 24px; + } > div:not(.tb-range-chart-overlay) { z-index: 1; } @@ -40,7 +42,7 @@ min-height: 0; display: flex; flex-direction: column; - gap: 16px; + gap: 8px; &.legend-top { flex-direction: column-reverse; } @@ -88,8 +90,9 @@ } } &.legend-right, &.legend-left { - gap: 24px; + gap: 16px; .tb-range-chart-legend { + padding-top: 8px; flex-direction: column-reverse; justify-content: flex-end; align-items: stretch; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index 42c72d6526..9d89154111 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -73,6 +73,8 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; + overlayEnabled: boolean; + padding: string; legendLabelStyle: ComponentStyle; disabledLegendLabelStyle: ComponentStyle; @@ -110,6 +112,8 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); this.overlayStyle = overlayStyle(this.settings.background.overlay); + this.overlayEnabled = this.settings.background.overlay.enabled; + this.padding = this.overlayEnabled ? undefined : this.settings.padding; this.rangeItems = toRangeItems(this.settings.rangeColors); this.visibleRangeItems = this.rangeItems.filter(item => item.visible); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index bff0c8fb1b..ee7b2835e6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -60,6 +60,7 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { legendLabelFont: Font; legendLabelColor: string; background: BackgroundSettings; + padding: string; } export const rangeChartDefaultSettings: RangeChartWidgetSettings = { @@ -118,7 +119,8 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { color: 'rgba(255,255,255,0.72)', blur: 3 } - } + }, + padding: '12px' }; export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 4abd10a6e3..df8b74f898 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -333,7 +333,10 @@ export class TbTimeSeriesChart { timeSeriesChartKeyDefaultSettings, dataKey.settings); if ((keySettings.type === TimeSeriesChartSeriesType.line && keySettings.lineSettings.showPointLabel && keySettings.lineSettings.pointLabelPosition === SeriesLabelPosition.top) || - (keySettings.type === TimeSeriesChartSeriesType.bar && keySettings.barSettings.showLabel)) { + (keySettings.type === TimeSeriesChartSeriesType.bar && + keySettings.barSettings.showLabel && + [SeriesLabelPosition.top, SeriesLabelPosition.bottom] + .includes(keySettings.barSettings.labelPosition as SeriesLabelPosition))) { this.topPointLabels = true; } dataKey.settings = keySettings; @@ -366,6 +369,11 @@ export class TbTimeSeriesChart { for (const thresholdSettings of this.settings.thresholds) { const threshold = mergeDeep({} as TimeSeriesChartThreshold, timeSeriesChartThresholdDefaultSettings, thresholdSettings); + if (!this.topPointLabels) { + if (threshold.showLabel && !threshold.labelPosition.endsWith('Bottom')) { + this.topPointLabels = true; + } + } let latestDataKey: DataKey = null; let entityDataKey: DataKey = null; let value = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index b20b941bd1..bc8044b957 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -154,5 +154,11 @@
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts index b69b023d34..f61a130e9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts @@ -88,7 +88,8 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], - background: [settings.background, []] + background: [settings.background, []], + padding: [settings.padding, []] }); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html index cf5bbcfdd5..4a7331d4c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html @@ -144,5 +144,11 @@
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts index f0870dbd80..4ef2971467 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts @@ -83,7 +83,8 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], - background: [settings.background, []] + background: [settings.background, []], + padding: [settings.padding, []] }); } From 604a35b2671ce9c38ddfe13268ee0cf3b190dd43 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2024 17:38:06 +0200 Subject: [PATCH 078/107] UI: Improve bar chart with labels width configuration. --- ...rt-with-labels-basic-config.component.html | 79 ++++++++-- ...hart-with-labels-basic-config.component.ts | 41 ++++- .../bar-chart-with-labels-widget.models.ts | 84 +++++++--- .../lib/chart/time-series-chart.models.ts | 122 ++++++++------- .../widget/lib/chart/time-series-chart.ts | 6 +- ...with-labels-widget-settings.component.html | 144 +++++++++++++----- ...t-with-labels-widget-settings.component.ts | 33 +++- ...-series-chart-axis-settings.component.html | 4 +- ...me-series-chart-axis-settings.component.ts | 8 + ...-series-chart-fill-settings.component.html | 0 ...me-series-chart-fill-settings.component.ts | 0 ...-series-chart-threshold-row.component.html | 2 +- ...me-series-chart-threshold-row.component.ts | 6 + ...rt-threshold-settings-panel.component.html | 2 +- ...hart-threshold-settings-panel.component.ts | 5 + ...ries-chart-thresholds-panel.component.html | 3 +- ...series-chart-thresholds-panel.component.ts | 5 + ...regation-bar-width-settings.component.html | 2 +- ...ggregation-bar-width-settings.component.ts | 5 + .../common/widget-settings-common.module.ts | 5 + .../lib/settings/widget-settings.module.ts | 5 - .../assets/locale/locale.constant-en_US.json | 2 +- .../assets/locale/locale.constant-pl_PL.json | 2 +- 23 files changed, 421 insertions(+), 144 deletions(-) rename ui-ngx/src/app/modules/home/components/widget/lib/settings/{ => common}/chart/time-series-chart-fill-settings.component.html (100%) rename ui-ngx/src/app/modules/home/components/widget/lib/settings/{ => common}/chart/time-series-chart-fill-settings.component.ts (100%) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index ca3d8a5b44..0b629e4cae 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -84,8 +84,28 @@
+
+
widgets.time-series-chart.chart
+
+ + {{ 'widgets.time-series-chart.data-zoom' | translate }} + +
+
widgets.bar-chart.bar-appearance
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
{{ 'widgets.bar-chart.label-on-bar' | translate }} @@ -118,19 +138,57 @@
-
-
widget-config.units-short
- - +
+ + {{ 'widgets.time-series-chart.series.bar.show-border' | translate }} +
-
-
widget-config.decimals-short
- - +
+
widgets.time-series-chart.series.bar.border-width
+ + + +
+
+
widgets.time-series-chart.series.bar.border-radius
+ +
+ + + + +
+
+
widgets.time-series-chart.axis.y-axis
+ + +
+
+
widgets.time-series-chart.axis.x-axis
+ +
+ +
@@ -230,6 +288,9 @@
+ +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts index 8cdd89f01c..ab5fc0a867 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts @@ -44,6 +44,7 @@ import { barChartWithLabelsDefaultSettings, BarChartWithLabelsWidgetSettings } from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.models'; +import { TimeSeriesChartType } from '@home/components/widget/lib/chart/time-series-chart.models'; @Component({ selector: 'tb-bar-chart-with-labels-basic-config', @@ -105,16 +106,30 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom icon: [configData.config.titleIcon, []], iconColor: [configData.config.iconColor, []], + dataZoom: [settings.dataZoom, []], + showBarLabel: [settings.showBarLabel, []], barLabelFont: [settings.barLabelFont, []], barLabelColor: [settings.barLabelColor, []], showBarValue: [settings.showBarValue, []], barValueFont: [settings.barValueFont, []], barValueColor: [settings.barValueColor, []], + showBarBorder: [settings.showBarBorder, []], + barBorderWidth: [settings.barBorderWidth, []], + barBorderRadius: [settings.barBorderRadius, []], + barBackgroundSettings: [settings.barBackgroundSettings, []], + noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], units: [configData.config.units, []], decimals: [configData.config.decimals, []], + yAxis: [settings.yAxis, []], + xAxis: [settings.xAxis, []], + + thresholds: [settings.thresholds, []], + + animation: [settings.animation, []], + showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], legendLabelFont: [settings.legendLabelFont, []], @@ -159,6 +174,8 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.showBarLabel = config.showBarLabel; this.widgetConfig.config.settings.barLabelFont = config.barLabelFont; this.widgetConfig.config.settings.barLabelColor = config.barLabelColor; @@ -166,9 +183,22 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.widgetConfig.config.settings.barValueFont = config.barValueFont; this.widgetConfig.config.settings.barValueColor = config.barValueColor; + this.widgetConfig.config.settings.showBarBorder = config.showBarBorder; + this.widgetConfig.config.settings.barBorderWidth = config.barBorderWidth; + this.widgetConfig.config.settings.barBorderRadius = config.barBorderRadius; + this.widgetConfig.config.settings.barBackgroundSettings = config.barBackgroundSettings; + this.widgetConfig.config.settings.noAggregationBarWidthSettings = config.noAggregationBarWidthSettings; + this.widgetConfig.config.units = config.units; this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.yAxis = config.yAxis; + this.widgetConfig.config.settings.xAxis = config.xAxis; + + this.widgetConfig.config.settings.thresholds = config.thresholds; + + this.widgetConfig.config.settings.animation = config.animation; + this.widgetConfig.config.settings.showLegend = config.showLegend; this.widgetConfig.config.settings.legendPosition = config.legendPosition; this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; @@ -196,7 +226,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom } protected validatorTriggers(): string[] { - return ['showTitle', 'showIcon', 'showBarLabel', 'showBarValue', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showTitle', 'showIcon', 'showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean, trigger?: string) { @@ -204,6 +234,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom const showIcon: boolean = this.barChartWidgetConfigForm.get('showIcon').value; const showBarLabel: boolean = this.barChartWidgetConfigForm.get('showBarLabel').value; const showBarValue: boolean = this.barChartWidgetConfigForm.get('showBarValue').value; + const showBarBorder: boolean = this.barChartWidgetConfigForm.get('showBarBorder').value; const showLegend: boolean = this.barChartWidgetConfigForm.get('showLegend').value; const showTooltip: boolean = this.barChartWidgetConfigForm.get('showTooltip').value; const tooltipShowDate: boolean = this.barChartWidgetConfigForm.get('tooltipShowDate').value; @@ -250,7 +281,11 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom this.barChartWidgetConfigForm.get('barValueFont').disable(); this.barChartWidgetConfigForm.get('barValueColor').disable(); } - + if (showBarBorder) { + this.barChartWidgetConfigForm.get('barBorderWidth').enable(); + } else { + this.barChartWidgetConfigForm.get('barBorderWidth').disable(); + } if (showLegend) { this.barChartWidgetConfigForm.get('legendPosition').enable(); this.barChartWidgetConfigForm.get('legendLabelFont').enable(); @@ -328,4 +363,6 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom processor.update(Date.now()); return processor.formatted; } + + protected readonly TimeSeriesChartType = TimeSeriesChartType; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index 8d99a7bd3f..73ecad5264 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -19,26 +19,49 @@ import { BackgroundType, ComponentStyle, customDateFormat, - Font, textStyle + Font, + textStyle } from '@shared/models/widget-settings.models'; import { LegendPosition } from '@shared/models/widget.models'; import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; import { DeepPartial } from '@shared/models/common'; import { - TimeSeriesChartKeySettings, TimeSeriesChartSeriesType, - TimeSeriesChartSettings + defaultTimeSeriesChartXAxisSettings, + defaultTimeSeriesChartYAxisSettings, + SeriesFillSettings, + SeriesFillType, + timeSeriesChartAnimationDefaultSettings, + TimeSeriesChartAnimationSettings, + TimeSeriesChartKeySettings, + timeSeriesChartNoAggregationBarWidthDefaultSettings, + TimeSeriesChartNoAggregationBarWidthSettings, + TimeSeriesChartSeriesType, + TimeSeriesChartSettings, + TimeSeriesChartThreshold, + TimeSeriesChartXAxisSettings, + TimeSeriesChartYAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; import { CallbackDataParams, LabelLayoutOptionCallbackParams } from 'echarts/types/dist/shared'; -import { formatValue } from '@core/utils'; +import { formatValue, mergeDeep } from '@core/utils'; import { LabelLayoutOption } from 'echarts/types/src/util/types'; export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSettings { + dataZoom: boolean; showBarLabel: boolean; barLabelFont: Font; barLabelColor: string; showBarValue: boolean; barValueFont: Font; barValueColor: string; + showBarBorder: boolean; + barBorderWidth: number; + barBorderRadius: number; + barBackgroundSettings: SeriesFillSettings; + noAggregationBarWidthSettings: TimeSeriesChartNoAggregationBarWidthSettings; + yAxis: TimeSeriesChartYAxisSettings; + xAxis: TimeSeriesChartXAxisSettings; + animation: TimeSeriesChartAnimationSettings; + thresholds: TimeSeriesChartThreshold[]; showLegend: boolean; legendPosition: LegendPosition; legendLabelFont: Font; @@ -48,6 +71,7 @@ export interface BarChartWithLabelsWidgetSettings extends EChartsTooltipWidgetSe } export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings = { + dataZoom: false, showBarLabel: true, barLabelFont: { family: 'Roboto', @@ -68,6 +92,28 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings lineHeight: '12px' }, barValueColor: 'rgba(0, 0, 0, 0.76)', + showBarBorder: false, + barBorderWidth: 2, + barBorderRadius: 0, + barBackgroundSettings: { + type: SeriesFillType.none, + opacity: 0.4, + gradient: { + start: 100, + end: 0 + } + }, + noAggregationBarWidthSettings: mergeDeep({} as TimeSeriesChartNoAggregationBarWidthSettings, + timeSeriesChartNoAggregationBarWidthDefaultSettings), + yAxis: mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings, + { id: 'default', order: 0, showLine: false, showTicks: false } as TimeSeriesChartYAxisSettings), + xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, + defaultTimeSeriesChartXAxisSettings, + {showTicks: false, showSplitLines: false} as TimeSeriesChartXAxisSettings), + animation: mergeDeep({} as TimeSeriesChartAnimationSettings, + timeSeriesChartAnimationDefaultSettings), + thresholds: [], showLegend: true, legendPosition: LegendPosition.top, legendLabelFont: { @@ -116,27 +162,18 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings }; export const barChartWithLabelsTimeSeriesSettings = (settings: BarChartWithLabelsWidgetSettings): DeepPartial => ({ - dataZoom: false, + dataZoom: settings.dataZoom, yAxes: { - default: { - show: true, - showLine: false, - showTicks: false, - showTickLabels: true, - showSplitLines: true, - } - }, - xAxis: { - show: true, - showLine: true, - showTicks: false, - showTickLabels: true, - showSplitLines: false + default: settings.yAxis }, + xAxis: settings.xAxis, barWidthSettings: { barGap: 0, intervalGap: 0.5 }, + noAggregationBarWidthSettings: settings.noAggregationBarWidthSettings, + animation: settings.animation, + thresholds: settings.thresholds, showTooltip: settings.showTooltip, tooltipValueFont: settings.tooltipValueFont, tooltipValueColor: settings.tooltipValueColor, @@ -164,10 +201,11 @@ export const barChartWithLabelsTimeSeriesKeySettings = (settings: BarChartWithLa return { type: TimeSeriesChartSeriesType.bar, barSettings: { - showBorder: false, - borderWidth: 0, - borderRadius: 0, - showLabel: settings.showBarLabel, + showBorder: settings.showBarBorder, + borderWidth: settings.barBorderWidth, + borderRadius: settings.barBorderRadius, + backgroundSettings: settings.barBackgroundSettings, + showLabel: settings.showBarLabel || settings.showBarValue, labelPosition: 'insideBottom', labelFormatter: (params: CallbackDataParams): string => { const labelParts: string[] = []; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index 02cd2bba75..df47a03692 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -408,6 +408,38 @@ export const defaultTimeSeriesChartYAxisSettings: TimeSeriesChartYAxisSettings = splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light }; +export const defaultTimeSeriesChartXAxisSettings: TimeSeriesChartXAxisSettings = { + show: true, + label: '', + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '600', + lineHeight: '1' + }, + labelColor: timeSeriesChartColorScheme['axis.label'].light, + position: AxisPosition.bottom, + showTickLabels: true, + tickLabelFont: { + family: 'Roboto', + size: 10, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light, + ticksFormat: {}, + showTicks: true, + ticksColor: timeSeriesChartColorScheme['axis.ticks'].light, + showLine: true, + lineColor: timeSeriesChartColorScheme['axis.line'].light, + showSplitLines: true, + splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light +}; + export type TimeSeriesChartYAxes = {[id: TimeSeriesChartYAxisId]: TimeSeriesChartYAxisSettings}; export interface TimeSeriesChartThreshold { @@ -521,6 +553,20 @@ export interface TimeSeriesChartNoAggregationBarWidthSettings { barWidth?: TimeSeriesChartBarWidth; } +export const timeSeriesChartNoAggregationBarWidthDefaultSettings: TimeSeriesChartNoAggregationBarWidthSettings = { + strategy: TimeSeriesChartNoAggregationBarWidthStrategy.group, + groupWidth: { + relative: true, + relativeWidth: 2, + absoluteWidth: 1000 + }, + barWidth: { + relative: true, + relativeWidth: 2, + absoluteWidth: 1000 + } +}; + export interface TimeSeriesChartBarWidthSettings { barGap: number; intervalGap: number; @@ -573,6 +619,17 @@ export interface TimeSeriesChartAnimationSettings { animationDelayUpdate: number; } +export const timeSeriesChartAnimationDefaultSettings: TimeSeriesChartAnimationSettings = { + animation: true, + animationThreshold: 2000, + animationDuration: 500, + animationEasing: TimeSeriesChartAnimationEasing.cubicOut, + animationDelay: 0, + animationDurationUpdate: 300, + animationEasingUpdate: TimeSeriesChartAnimationEasing.cubicOut, + animationDelayUpdate: 0 +}; + export interface TimeSeriesChartVisualMapPiece { lt?: number; gt?: number; @@ -629,64 +686,16 @@ export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { defaultTimeSeriesChartYAxisSettings, { id: 'default', order: 0 } as TimeSeriesChartYAxisSettings) }, - xAxis: { - show: true, - label: '', - labelFont: { - family: 'Roboto', - size: 12, - sizeUnit: 'px', - style: 'normal', - weight: '600', - lineHeight: '1' - }, - labelColor: timeSeriesChartColorScheme['axis.label'].light, - position: AxisPosition.bottom, - showTickLabels: true, - tickLabelFont: { - family: 'Roboto', - size: 10, - sizeUnit: 'px', - style: 'normal', - weight: '400', - lineHeight: '1' - }, - tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light, - ticksFormat: {}, - showTicks: true, - ticksColor: timeSeriesChartColorScheme['axis.ticks'].light, - showLine: true, - lineColor: timeSeriesChartColorScheme['axis.line'].light, - showSplitLines: true, - splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light - }, - animation: { - animation: true, - animationThreshold: 2000, - animationDuration: 500, - animationEasing: TimeSeriesChartAnimationEasing.cubicOut, - animationDelay: 0, - animationDurationUpdate: 300, - animationEasingUpdate: TimeSeriesChartAnimationEasing.cubicOut, - animationDelayUpdate: 0 - }, + xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, + defaultTimeSeriesChartXAxisSettings), + animation: mergeDeep({} as TimeSeriesChartAnimationSettings, + timeSeriesChartAnimationDefaultSettings), barWidthSettings: { barGap: 0.3, intervalGap: 0.6 }, - noAggregationBarWidthSettings: { - strategy: TimeSeriesChartNoAggregationBarWidthStrategy.group, - groupWidth: { - relative: true, - relativeWidth: 2, - absoluteWidth: 1000 - }, - barWidth: { - relative: true, - relativeWidth: 2, - absoluteWidth: 1000 - } - }, + noAggregationBarWidthSettings: mergeDeep({} as TimeSeriesChartNoAggregationBarWidthSettings, + timeSeriesChartNoAggregationBarWidthDefaultSettings), showTooltip: true, tooltipTrigger: EChartsTooltipTrigger.axis, tooltipValueFont: { @@ -1000,6 +1009,11 @@ export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettin fontFamily: xAxisTickLabelStyle.fontFamily, fontSize: xAxisTickLabelStyle.fontSize, hideOverlap: true, + /** Min/Max time label always visible **/ + /* alignMinLabel: 'left', + alignMaxLabel: 'right', + showMinLabel: true, + showMaxLabel: true, */ formatter: (value: number, _index: number, extra: {level: number}) => { const unit = tsToFormatTimeUnit(value); const format = ticksFormat[unit]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index df8b74f898..98cc1fd454 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -584,7 +584,7 @@ export class TbTimeSeriesChart { } this.timeSeriesChart.setOption(this.timeSeriesChartOptions); - this.updateAxes(); + this.updateAxes(false); if (this.settings.dataZoom) { this.timeSeriesChart.on('datazoom', () => { @@ -609,7 +609,7 @@ export class TbTimeSeriesChart { this.barRenderSharedContext, this.darkMode); } - private updateAxes() { + private updateAxes(lazy = true) { const leftAxisList = this.yAxisList.filter(axis => axis.option.position === 'left'); let res = this.updateYAxisOffset(leftAxisList); let leftOffset = res.offset + (!res.offset && this.settings.dataZoom ? 5 : 0); @@ -659,7 +659,7 @@ export class TbTimeSeriesChart { } if (changed) { this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); - this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: true}); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: lazy}); } if (this.yAxisList.length) { const extent = getAxisExtent(this.timeSeriesChart, this.yAxisList[0].id); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index bc8044b957..7db5a92746 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -17,39 +17,99 @@ -->
-
widgets.bar-chart.bar-chart-card-style
-
- - {{ 'widgets.bar-chart.label-on-bar' | translate }} +
widgets.bar-chart.bar-chart-style
+
+ + {{ 'widgets.time-series-chart.data-zoom' | translate }} -
- - - - -
-
- - {{ 'widgets.bar-chart.value-on-bar' | translate }} - -
- - - - +
+
widgets.bar-chart.bar-appearance
+
+ + {{ 'widgets.bar-chart.label-on-bar' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.bar-chart.value-on-bar' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.time-series-chart.series.bar.show-border' | translate }} + +
+
+
widgets.time-series-chart.series.bar.border-width
+ + + +
+
+
widgets.time-series-chart.series.bar.border-radius
+ + +
+ + + +
+
+
widgets.time-series-chart.axis.y-axis
+ + +
+
+
widgets.time-series-chart.axis.x-axis
+ + +
+ +
@@ -149,16 +209,22 @@
-
-
{{ 'widgets.background.background' | translate }}
- - -
-
-
{{ 'widget-config.card-padding' | translate }}
- - - + + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts index f61a130e9d..b221d09cf9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts @@ -16,6 +16,7 @@ import { Component, Injector } from '@angular/core'; import { + Datasource, legendPositions, legendPositionTranslationMap, WidgetSettings, @@ -37,6 +38,15 @@ import { }) export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsComponent { + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + legendPositions = legendPositions; legendPositionTranslationMap = legendPositionTranslationMap; @@ -64,12 +74,26 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom protected onSettingsSet(settings: WidgetSettings) { this.barChartWidgetSettingsForm = this.fb.group({ + dataZoom: [settings.dataZoom, []], + showBarLabel: [settings.showBarLabel, []], barLabelFont: [settings.barLabelFont, []], barLabelColor: [settings.barLabelColor, []], showBarValue: [settings.showBarValue, []], barValueFont: [settings.barValueFont, []], barValueColor: [settings.barValueColor, []], + showBarBorder: [settings.showBarBorder, []], + barBorderWidth: [settings.barBorderWidth, []], + barBorderRadius: [settings.barBorderRadius, []], + barBackgroundSettings: [settings.barBackgroundSettings, []], + noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], + + yAxis: [settings.yAxis, []], + xAxis: [settings.xAxis, []], + + thresholds: [settings.thresholds, []], + + animation: [settings.animation, []], showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], @@ -94,12 +118,13 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom } protected validatorTriggers(): string[] { - return ['showBarLabel', 'showBarValue', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean) { const showBarLabel: boolean = this.barChartWidgetSettingsForm.get('showBarLabel').value; const showBarValue: boolean = this.barChartWidgetSettingsForm.get('showBarValue').value; + const showBarBorder: boolean = this.barChartWidgetSettingsForm.get('showBarBorder').value; const showLegend: boolean = this.barChartWidgetSettingsForm.get('showLegend').value; const showTooltip: boolean = this.barChartWidgetSettingsForm.get('showTooltip').value; const tooltipShowDate: boolean = this.barChartWidgetSettingsForm.get('tooltipShowDate').value; @@ -120,6 +145,12 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom this.barChartWidgetSettingsForm.get('barValueColor').disable(); } + if (showBarBorder) { + this.barChartWidgetSettingsForm.get('barBorderWidth').enable(); + } else { + this.barChartWidgetSettingsForm.get('barBorderWidth').disable(); + } + if (showLegend) { this.barChartWidgetSettingsForm.get('legendPosition').enable(); this.barChartWidgetSettingsForm.get('legendLabelFont').enable(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html index 1d4bb95f5c..66b6c9896d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html @@ -56,13 +56,13 @@
-
+
widget-config.units-short
-
+
widget-config.decimals-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts index 8dafc7050f..ac49b8137d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts @@ -70,6 +70,14 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu @coerceBoolean() advanced = false; + @Input() + @coerceBoolean() + hideUnits = false; + + @Input() + @coerceBoolean() + hideDecimals = false; + private modelValue: TimeSeriesChartXAxisSettings | TimeSeriesChartYAxisSettings; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html index 7e2c9f74c2..386ea306a0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html @@ -73,7 +73,7 @@ [formControl]="entityKeyFormControl">
-
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts index baebf8c697..4f51c41c26 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts @@ -53,6 +53,7 @@ import { deepClone } from '@core/utils'; import { TimeSeriesChartThresholdSettingsPanelComponent } from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-chart-threshold-row', @@ -101,6 +102,10 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso @Input() yAxisIds: TimeSeriesChartYAxisId[]; + @Input() + @coerceBoolean() + hideYAxis = false; + @Output() thresholdRemoved = new EventEmitter(); @@ -220,6 +225,7 @@ export class TimeSeriesChartThresholdRowComponent implements ControlValueAccesso const ctx: any = { thresholdSettings: deepClone(this.modelValue), widgetConfig: this.widgetConfig, + hideYAxis: this.hideYAxis, yAxisIds: this.yAxisIds }; const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html index 3437a578d6..d7cc446b64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html @@ -18,7 +18,7 @@
{{ 'widgets.time-series-chart.threshold.threshold-settings' | translate }}
-
+
widgets.time-series-chart.axis.y-axis
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts index b9f16103bd..bc68042f9b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts @@ -30,6 +30,7 @@ import { import { merge } from 'rxjs'; import { WidgetConfig } from '@shared/models/widget.models'; import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-chart-threshold-settings-panel', @@ -66,6 +67,10 @@ export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit { @Input() popover: TbPopoverComponent; + @Input() + @coerceBoolean() + hideYAxis = false; + @Output() thresholdSettingsApplied = new EventEmitter>(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html index e975eb4653..ba0325da93 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html @@ -21,7 +21,7 @@
widgets.time-series-chart.threshold.source
widgets.time-series-chart.threshold.key-value
-
widgets.time-series-chart.axis.y-axis
+
widgets.time-series-chart.axis.y-axis
widgets.color.color
widget-config.units-short
widget-config.decimals-short
@@ -31,6 +31,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts index 9cb5e5ced2..95e3b242c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts @@ -38,6 +38,7 @@ import { IAliasController } from '@core/api/widget-api.models'; import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-chart-thresholds-panel', @@ -77,6 +78,10 @@ export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAcce @Input() yAxisIds: TimeSeriesChartYAxisId[]; + @Input() + @coerceBoolean() + hideYAxis = false; + thresholdsFormGroup: UntypedFormGroup; private propagateChange = (_val: any) => {}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html index 239c6ea18c..28c79a1310 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
widgets.time-series-chart.no-aggregation-bar-width-strategy
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts index 36d8cb917a..8601c48159 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts @@ -29,6 +29,7 @@ import { timeSeriesChartNoAggregationBarWidthStrategyTranslations } from '@home/components/widget/lib/chart/time-series-chart.models'; import { merge } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-time-series-no-aggregation-bar-width-settings', @@ -53,6 +54,10 @@ export class TimeSeriesNoAggregationBarWidthSettingsComponent implements OnInit, @Input() disabled: boolean; + @Input() + @coerceBoolean() + stroked = false; + private modelValue: TimeSeriesChartNoAggregationBarWidthSettings; private propagateChange = null; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 5ed50a226f..08d45775c5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -127,6 +127,9 @@ import { import { AutoDateFormatSettingsComponent } from '@home/components/widget/lib/settings/common/auto-date-format-settings.component'; +import { + TimeSeriesChartFillSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component'; @NgModule({ declarations: [ @@ -174,6 +177,7 @@ import { TimeSeriesChartYAxisRowComponent, TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, + TimeSeriesChartFillSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], @@ -227,6 +231,7 @@ import { TimeSeriesChartYAxisRowComponent, TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, + TimeSeriesChartFillSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 8522734eda..5b83f859e1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -336,9 +336,6 @@ import { import { TimeSeriesChartLineSettingsComponent } from '@home/components/widget/lib/settings/chart/time-series-chart-line-settings.component'; -import { - TimeSeriesChartFillSettingsComponent -} from '@home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component'; import { TimeSeriesChartBarSettingsComponent } from '@home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component'; @@ -467,7 +464,6 @@ import { TimeSeriesChartKeySettingsComponent, TimeSeriesChartLineSettingsComponent, TimeSeriesChartBarSettingsComponent, - TimeSeriesChartFillSettingsComponent, TimeSeriesChartWidgetSettingsComponent ], imports: [ @@ -596,7 +592,6 @@ import { TimeSeriesChartKeySettingsComponent, TimeSeriesChartLineSettingsComponent, TimeSeriesChartBarSettingsComponent, - TimeSeriesChartFillSettingsComponent, TimeSeriesChartWidgetSettingsComponent ] }) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f8f752b5b3..43e20a46ce 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5422,7 +5422,7 @@ "bar-appearance": "Bar appearance", "label-on-bar": "Label on bar", "value-on-bar": "Value on bar", - "bar-chart-card-style": "Bar chart card style" + "bar-chart-style": "Bar chart style" }, "battery-level": { "layout": "Layout", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 014666017c..820a616382 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -5172,7 +5172,7 @@ "bar-appearance":"Wygląd słupka", "label-on-bar":"Etykieta na słupku", "value-on-bar":"Wartość na słupku", - "bar-chart-card-style":"Styl karty wykresu słupkowego" + "bar-chart-style":"Styl wykresu słupkowego" }, "battery-level":{ "layout":"Układ", From c8b2a2a6cda31257258e6dceae45fa82d8414792 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 28 Mar 2024 19:26:25 +0200 Subject: [PATCH 079/107] UI: Range chart widget config improvements. Update range chart basic config. --- .../range-chart-basic-config.component.html | 153 ++++++++++++++++-- .../range-chart-basic-config.component.ts | 153 +++++++++++++++++- .../lib/chart/range-chart-widget.models.ts | 109 ++++++++++--- .../lib/chart/time-series-chart.models.ts | 44 +++-- .../assets/locale/locale.constant-en_US.json | 3 + 5 files changed, 400 insertions(+), 62 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index bb64c59302..4bda5fa9b5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -75,6 +75,21 @@ {{ 'widgets.range-chart.data-zoom' | translate }}
+
+
+
widgets.range-chart.range-chart-appearance
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
{{ 'widgets.range-chart.range-colors' | translate }}
@@ -87,24 +102,139 @@ formControlName="outOfRangeColor">
+
+ + {{ 'widgets.range-chart.show-range-thresholds' | translate }} + +
{{ 'widgets.range-chart.fill-area' | translate }}
-
-
widget-config.units-short
- - -
-
-
widget-config.decimals-short
- - +
+
widgets.range-chart.fill-area-opacity
+ + +
+
+
widgets.time-series-chart.series.line.line
+
+ + {{ 'widgets.time-series-chart.series.line.show-line' | translate }} +
+
+ + {{ 'widgets.time-series-chart.series.line.step-line' | translate }} + + + + + {{ lineSeriesStepTypeTranslations.get(stepType) | translate }} + + + +
+
+ + {{ 'widgets.time-series-chart.series.line.smooth-line' | translate }} + +
+
+
widgets.time-series-chart.line-type
+ + + + {{ timeSeriesLineTypeTranslations.get(lineType) | translate }} + + + +
+
+
widgets.time-series-chart.line-width
+ + + +
+
+
+
widgets.time-series-chart.series.point.points
+
+ + {{ 'widgets.time-series-chart.series.point.show-points' | translate }} + +
+
+ +
+ {{ 'widgets.time-series-chart.series.point.point-label' | translate }} +
+
+
+ + + + {{ seriesLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+
+
widgets.time-series-chart.series.point.point-shape
+ + + + {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-size
+ + + +
+
+
+
+
widgets.time-series-chart.axis.y-axis
+ + +
+
+
widgets.time-series-chart.axis.x-axis
+ +
+ +
@@ -204,6 +334,9 @@
+ +
widget-config.card-appearance
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts index 566dd4d86e..fc006d05a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts @@ -14,13 +14,19 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { Component, Injector } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { DataKey, legendPositions, legendPositionTranslationMap, WidgetConfig, } from '@shared/models/widget.models'; +import { + DataKey, + Datasource, + legendPositions, + legendPositionTranslationMap, + WidgetConfig, +} from '@shared/models/widget.models'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { @@ -38,6 +44,16 @@ import { rangeChartDefaultSettings, RangeChartWidgetSettings } from '@home/components/widget/lib/chart/range-chart-widget.models'; +import { + lineSeriesStepTypes, + lineSeriesStepTypeTranslations, + seriesLabelPositions, + seriesLabelPositionTranslations, + timeSeriesChartShapes, + timeSeriesChartShapeTranslations, + timeSeriesLineTypes, + timeSeriesLineTypeTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; @Component({ selector: 'tb-range-chart-basic-config', @@ -46,12 +62,39 @@ import { }) export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { + public get datasource(): Datasource { + const datasources: Datasource[] = this.rangeChartWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + lineSeriesStepTypes = lineSeriesStepTypes; + + lineSeriesStepTypeTranslations = lineSeriesStepTypeTranslations; + + timeSeriesLineTypes = timeSeriesLineTypes; + + timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations; + + seriesLabelPositions = seriesLabelPositions; + + seriesLabelPositionTranslations = seriesLabelPositionTranslations; + + timeSeriesChartShapes = timeSeriesChartShapes; + + timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations; + legendPositions = legendPositions; legendPositionTranslationMap = legendPositionTranslationMap; rangeChartWidgetConfigForm: UntypedFormGroup; + pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this); + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); @@ -90,11 +133,36 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { iconColor: [configData.config.iconColor, []], dataZoom: [settings.dataZoom, []], + + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], rangeColors: [settings.rangeColors, []], outOfRangeColor: [settings.outOfRangeColor, []], + showRangeThresholds: [settings.showRangeThresholds, []], fillArea: [settings.fillArea, []], - units: [configData.config.units, []], - decimals: [configData.config.decimals, []], + fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(100)]], + + showLine: [settings.showLine, []], + step: [settings.step, []], + stepType: [settings.stepType, []], + smooth: [settings.smooth, []], + lineType: [settings.lineType, []], + lineWidth: [settings.lineWidth, [Validators.min(0)]], + + showPoints: [settings.showPoints, []], + showPointLabel: [settings.showPointLabel, []], + pointLabelPosition: [settings.pointLabelPosition, []], + pointLabelFont: [settings.pointLabelFont, []], + pointLabelColor: [settings.pointLabelColor, []], + pointShape: [settings.pointShape, []], + pointSize: [settings.pointSize, [Validators.min(0)]], + + yAxis: [settings.yAxis, []], + xAxis: [settings.xAxis, []], + + thresholds: [settings.thresholds, []], + + animation: [settings.animation, []], showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], @@ -139,12 +207,39 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.rangeColors = config.rangeColors; this.widgetConfig.config.settings.outOfRangeColor = config.outOfRangeColor; + this.widgetConfig.config.settings.showRangeThresholds = config.showRangeThresholds; this.widgetConfig.config.settings.fillArea = config.fillArea; - this.widgetConfig.config.units = config.units; - this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.fillAreaOpacity = config.fillAreaOpacity; + + this.widgetConfig.config.settings.showLine = config.showLine; + this.widgetConfig.config.settings.step = config.step; + this.widgetConfig.config.settings.stepType = config.stepType; + this.widgetConfig.config.settings.smooth = config.smooth; + this.widgetConfig.config.settings.lineType = config.lineType; + this.widgetConfig.config.settings.lineWidth = config.lineWidth; + + this.widgetConfig.config.settings.showPoints = config.showPoints; + this.widgetConfig.config.settings.showPointLabel = config.showPointLabel; + this.widgetConfig.config.settings.pointLabelPosition = config.pointLabelPosition; + this.widgetConfig.config.settings.pointLabelFont = config.pointLabelFont; + this.widgetConfig.config.settings.pointLabelColor = config.pointLabelColor; + this.widgetConfig.config.settings.pointShape = config.pointShape; + this.widgetConfig.config.settings.pointSize = config.pointSize; + + this.widgetConfig.config.settings.yAxis = config.yAxis; + this.widgetConfig.config.settings.xAxis = config.xAxis; + + this.widgetConfig.config.settings.thresholds = config.thresholds; + + this.widgetConfig.config.settings.animation = config.animation; this.widgetConfig.config.settings.showLegend = config.showLegend; this.widgetConfig.config.settings.legendPosition = config.legendPosition; @@ -173,12 +268,16 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { } protected validatorTriggers(): string[] { - return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showTitle', 'showIcon', 'fillArea', 'showLine', 'step', 'showPointLabel', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean, trigger?: string) { const showTitle: boolean = this.rangeChartWidgetConfigForm.get('showTitle').value; const showIcon: boolean = this.rangeChartWidgetConfigForm.get('showIcon').value; + const fillArea: boolean = this.rangeChartWidgetConfigForm.get('fillArea').value; + const showLine: boolean = this.rangeChartWidgetConfigForm.get('showLine').value; + const step: boolean = this.rangeChartWidgetConfigForm.get('step').value; + const showPointLabel: boolean = this.rangeChartWidgetConfigForm.get('showPointLabel').value; const showLegend: boolean = this.rangeChartWidgetConfigForm.get('showLegend').value; const showTooltip: boolean = this.rangeChartWidgetConfigForm.get('showTooltip').value; const tooltipShowDate: boolean = this.rangeChartWidgetConfigForm.get('tooltipShowDate').value; @@ -210,6 +309,40 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.rangeChartWidgetConfigForm.get('iconColor').disable(); } + if (fillArea) { + this.rangeChartWidgetConfigForm.get('fillAreaOpacity').enable(); + } else { + this.rangeChartWidgetConfigForm.get('fillAreaOpacity').disable(); + } + + if (showLine) { + this.rangeChartWidgetConfigForm.get('step').enable({emitEvent: false}); + if (step) { + this.rangeChartWidgetConfigForm.get('stepType').enable(); + this.rangeChartWidgetConfigForm.get('smooth').disable(); + } else { + this.rangeChartWidgetConfigForm.get('stepType').disable(); + this.rangeChartWidgetConfigForm.get('smooth').enable(); + } + this.rangeChartWidgetConfigForm.get('lineType').enable(); + this.rangeChartWidgetConfigForm.get('lineWidth').enable(); + } else { + this.rangeChartWidgetConfigForm.get('step').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('stepType').disable(); + this.rangeChartWidgetConfigForm.get('smooth').disable(); + this.rangeChartWidgetConfigForm.get('lineType').disable(); + this.rangeChartWidgetConfigForm.get('lineWidth').disable(); + } + if (showPointLabel) { + this.rangeChartWidgetConfigForm.get('pointLabelPosition').enable(); + this.rangeChartWidgetConfigForm.get('pointLabelFont').enable(); + this.rangeChartWidgetConfigForm.get('pointLabelColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('pointLabelPosition').disable(); + this.rangeChartWidgetConfigForm.get('pointLabelFont').disable(); + this.rangeChartWidgetConfigForm.get('pointLabelColor').disable(); + } + if (showLegend) { this.rangeChartWidgetConfigForm.get('legendPosition').enable(); this.rangeChartWidgetConfigForm.get('legendLabelFont').enable(); @@ -262,6 +395,12 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { config.enableFullscreen = buttons.includes('fullscreen'); } + private _pointLabelPreviewFn(): string { + const units: string = this.rangeChartWidgetConfigForm.get('units').value; + const decimals: number = this.rangeChartWidgetConfigForm.get('decimals').value; + return formatValue(22, decimals, units, false); + } + private _tooltipValuePreviewFn(): string { const units: string = this.rangeChartWidgetConfigForm.get('units').value; const decimals: number = this.rangeChartWidgetConfigForm.get('decimals').value; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index ee7b2835e6..eb6ea4a397 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -27,16 +27,25 @@ import { LegendPosition } from '@shared/models/widget.models'; import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; import { createTimeSeriesChartVisualMapPiece, + defaultTimeSeriesChartXAxisSettings, + defaultTimeSeriesChartYAxisSettings, + LineSeriesStepType, SeriesFillType, + SeriesLabelPosition, timeSeriesChartAnimationDefaultSettings, + TimeSeriesChartAnimationSettings, + timeSeriesChartColorScheme, TimeSeriesChartKeySettings, + TimeSeriesChartLineType, TimeSeriesChartSeriesType, TimeSeriesChartSettings, TimeSeriesChartShape, TimeSeriesChartThreshold, TimeSeriesChartThresholdType, - TimeSeriesChartVisualMapPiece + TimeSeriesChartVisualMapPiece, + TimeSeriesChartXAxisSettings, + TimeSeriesChartYAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; -import { isNumber } from '@core/utils'; +import { isNumber, mergeDeep } from '@core/utils'; import { DeepPartial } from '@shared/models/common'; export interface RangeItem { @@ -54,7 +63,26 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { dataZoom: boolean; rangeColors: Array; outOfRangeColor: string; + showRangeThresholds: boolean; fillArea: boolean; + fillAreaOpacity: number; + showLine: boolean; + step: boolean; + stepType: LineSeriesStepType; + smooth: boolean; + lineType: TimeSeriesChartLineType; + lineWidth: number; + showPoints: boolean; + showPointLabel: boolean; + pointLabelPosition: SeriesLabelPosition; + pointLabelFont: Font; + pointLabelColor: string; + pointShape: TimeSeriesChartShape; + pointSize: number; + yAxis: TimeSeriesChartYAxisSettings; + xAxis: TimeSeriesChartXAxisSettings; + animation: TimeSeriesChartAnimationSettings; + thresholds: TimeSeriesChartThreshold[]; showLegend: boolean; legendPosition: LegendPosition; legendLabelFont: Font; @@ -75,7 +103,38 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { {from: 40, color: '#D81838'} ], outOfRangeColor: '#ccc', + showRangeThresholds: true, fillArea: true, + fillAreaOpacity: 0.7, + showLine: true, + step: false, + stepType: LineSeriesStepType.start, + smooth: false, + lineType: TimeSeriesChartLineType.solid, + lineWidth: 2, + showPoints: false, + showPointLabel: false, + pointLabelPosition: SeriesLabelPosition.top, + pointLabelFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + pointLabelColor: timeSeriesChartColorScheme['series.label'].light, + pointShape: TimeSeriesChartShape.emptyCircle, + pointSize: 4, + yAxis: mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings, + { id: 'default', order: 0, showLine: false, showTicks: false } as TimeSeriesChartYAxisSettings), + xAxis: mergeDeep({} as TimeSeriesChartXAxisSettings, + defaultTimeSeriesChartXAxisSettings, + {showSplitLines: false} as TimeSeriesChartXAxisSettings), + animation: mergeDeep({} as TimeSeriesChartAnimationSettings, + timeSeriesChartAnimationDefaultSettings), + thresholds: [], showLegend: true, legendPosition: LegendPosition.top, legendLabelFont: { @@ -125,7 +184,7 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[], decimals: number, units: string): DeepPartial => { - const thresholds: DeepPartial[] = getMarkPoints(rangeItems).map(item => ({ + let thresholds: DeepPartial[] = settings.showRangeThresholds ? getMarkPoints(rangeItems).map(item => ({ type: TimeSeriesChartThresholdType.constant, yAxisId: 'default', units, @@ -146,28 +205,24 @@ export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, borderRadius: 4, }, value: item - } as DeepPartial)); + } as DeepPartial)) : []; + if (settings.thresholds?.length) { + thresholds = thresholds.concat(settings.thresholds); + } return { dataZoom: settings.dataZoom, thresholds, yAxes: { default: { - show: true, - showLine: false, - showTicks: false, - showTickLabels: true, - showSplitLines: true, - decimals, - units + ...settings.yAxis, + ...{ + decimals, + units + } } }, - xAxis: { - show: true, - showLine: true, - showTicks: true, - showTickLabels: true, - showSplitLines: false - }, + xAxis: settings.xAxis, + animation: settings.animation, visualMapSettings: { outOfRangeColor: settings.outOfRangeColor, pieces: rangeItems.map(item => item.piece) @@ -188,12 +243,22 @@ export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettings): DeepPartial => ({ type: TimeSeriesChartSeriesType.line, lineSettings: { - showLine: true, - smooth: false, - showPoints: false, + showLine: settings.showLine, + step: settings.step, + stepType: settings.stepType, + smooth: settings.smooth, + lineType: settings.lineType, + lineWidth: settings.lineWidth, + showPoints: settings.showPoints, + showPointLabel: settings.showPointLabel, + pointLabelPosition: settings.pointLabelPosition, + pointLabelFont: settings.pointLabelFont, + pointLabelColor: settings.pointLabelColor, + pointShape: settings.pointShape, + pointSize: settings.pointSize, fillAreaSettings: { type: settings.fillArea ? SeriesFillType.opacity : SeriesFillType.none, - opacity: 0.7 + opacity: settings.fillAreaOpacity } } }); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index df47a03692..4dc36b6682 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -81,7 +81,7 @@ export const timeSeriesChartTypeTranslations = new Map { - let result: string; - try { - result = formatFunction(params.value[1]); - } catch (_e) { - } - if (isUndefined(result)) { - result = formatValue(params.value[1], item.decimals, item.units, false); - } - return `{value|${result}}`; - }; - } else { - formatter = (params): string => { - const value = formatValue(params.value[1], item.decimals, item.units, false); - return `{value|${value}}`; - }; - } + if (isFunction(labelFormatter)) { + formatter = labelFormatter as LabelFormatterCallback; + } else if (labelFormatter?.length) { + const formatFunction = parseFunction(labelFormatter, ['value']); + formatter = (params): string => { + let result: string; + try { + result = formatFunction(params.value[1]); + } catch (_e) { + } + if (isUndefined(result)) { + result = formatValue(params.value[1], item.decimals, item.units, false); + } + return `{value|${result}}`; + }; + } else { + formatter = (params): string => { + const value = formatValue(params.value[1], item.decimals, item.units, false); + return `{value|${value}}`; + }; } return { show, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 43e20a46ce..b2dbc2b29f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6172,9 +6172,12 @@ "range-chart": { "chart": "Chart", "data-zoom": "Data zoom", + "range-chart-appearance": "Range chart appearance", "range-colors": "Range colors", "out-of-range-color": "Out of range color", + "show-range-thresholds": "Show range thresholds", "fill-area": "Fill area", + "fill-area-opacity": "Fill area opacity", "range-chart-card-style": "Range chart card style" }, "rpc": { From 0107bc29ada1eec41f9abe24966a9842e8489399 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 29 Mar 2024 16:37:49 +0200 Subject: [PATCH 080/107] Add yaml parameter to set max size for telemetry message --- .../service/edge/rpc/EdgeGrpcSession.java | 39 +---- .../edge/rpc/processor/BaseEdgeProcessor.java | 164 ++++++------------ .../telemetry/TelemetryEdgeProcessor.java | 27 ++- .../processor/tenant/TenantEdgeProcessor.java | 1 + .../src/main/resources/thingsboard.yml | 2 + .../rpc/processor/BaseEdgeProcessorTest.java | 13 +- .../telemetry/TelemetryEdgeProcessorTest.java | 40 ++++- .../server/common/util/ProtoUtils.java | 65 +++---- 8 files changed, 148 insertions(+), 203 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 1453f1a85c..75bb8e10e9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -498,9 +498,9 @@ public final class EdgeGrpcSession implements Closeable { log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size()); for (DownlinkMsg downlinkMsg : copy) { if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) { - String error = String.format("Client max inbound message size [{%s}] is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " + + String error = String.format("Client max inbound message size %s is exceeded. Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE " + "env variable on the edge and restart it.", this.clientMaxInboundMessageSize); - String message = String.format("Downlink msg size [{%s}] exceeds client max inbound message size [{%s}]. " + + String message = String.format("Downlink msg size %s exceeds client max inbound message size %s. " + "Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it.", downlinkMsg.getSerializedSize(), this.clientMaxInboundMessageSize); log.error("[{}][{}][{}] {} Message {}", this.tenantId, edge.getId(), this.sessionId, message, downlinkMsg); ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId) @@ -551,35 +551,14 @@ public final class EdgeGrpcSession implements Closeable { DownlinkMsg downlinkMsg = null; try { switch (edgeEvent.getAction()) { - case UPDATED: - case ADDED: - case DELETED: - case ASSIGNED_TO_EDGE: - case UNASSIGNED_FROM_EDGE: - case ALARM_ACK: - case ALARM_CLEAR: - case ALARM_DELETE: - case CREDENTIALS_UPDATED: - case RELATION_ADD_OR_UPDATE: - case RELATION_DELETED: - case CREDENTIALS_REQUEST: - case RPC_CALL: - case ASSIGNED_TO_CUSTOMER: - case UNASSIGNED_FROM_CUSTOMER: - case ADDED_COMMENT: - case UPDATED_COMMENT: - case DELETED_COMMENT: + case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE, CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, CREDENTIALS_REQUEST, RPC_CALL, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> { downlinkMsg = convertEntityEventToDownlink(edgeEvent); log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg); - break; - case ATTRIBUTES_UPDATED: - case POST_ATTRIBUTES: - case ATTRIBUTES_DELETED: - case TIMESERIES_UPDATED: - downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent); - break; - default: - log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction()); + } + case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED -> + downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent); + default -> + log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction()); } } catch (Exception e) { log.error("[{}][{}] Exception during converting edge event to downlink msg", this.tenantId, this.sessionId, e); @@ -857,7 +836,7 @@ public final class EdgeGrpcSession implements Closeable { .build(); } String error = "Failed to validate the edge!"; - String failureMsg = String.format("{%s} Provided request secret: %s", error, request.getEdgeSecret()); + String failureMsg = String.format("%s Provided request secret: %s", error, request.getEdgeSecret()); ctx.getNotificationRuleProcessor().process(EdgeCommunicationFailureTrigger.builder().tenantId(tenantId).edgeId(edge.getId()) .customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(failureMsg).error(error).build()); return ConnectResponseMsg.newBuilder() diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index cc86826d41..db0bbd8aa5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -25,7 +25,6 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EdgeUtils; @@ -93,7 +92,6 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; -import org.thingsboard.server.service.entitiy.TbLogEntityActionService; import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory; import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory; @@ -115,6 +113,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConst import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; +import org.thingsboard.server.service.entitiy.TbLogEntityActionService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; @@ -356,35 +355,15 @@ public abstract class BaseEdgeProcessor { private boolean doSaveIfEdgeIsOffline(EdgeEventType type, EdgeEventActionType action) { - switch (action) { - case TIMESERIES_UPDATED: - case ALARM_ACK: - case ALARM_CLEAR: - case ALARM_ASSIGNED: - case ALARM_UNASSIGNED: - case CREDENTIALS_REQUEST: - case ADDED_COMMENT: - case UPDATED_COMMENT: - return true; - } - switch (type) { - case ALARM: - case ALARM_COMMENT: - case RULE_CHAIN: - case RULE_CHAIN_METADATA: - case USER: - case CUSTOMER: - case TENANT: - case TENANT_PROFILE: - case WIDGETS_BUNDLE: - case WIDGET_TYPE: - case ADMIN_SETTINGS: - case OTA_PACKAGE: - case QUEUE: - case RELATION: - return true; - } - return false; + return switch (action) { + case TIMESERIES_UPDATED, ALARM_ACK, ALARM_CLEAR, ALARM_ASSIGNED, ALARM_UNASSIGNED, CREDENTIALS_REQUEST, ADDED_COMMENT, UPDATED_COMMENT -> + true; + default -> switch (type) { + case ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_CHAIN_METADATA, USER, CUSTOMER, TENANT, TENANT_PROFILE, WIDGETS_BUNDLE, WIDGET_TYPE, ADMIN_SETTINGS, OTA_PACKAGE, QUEUE, RELATION -> + true; + default -> false; + }; + }; } private ListenableFuture doSaveEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventType type, EdgeEventActionType action, EntityId entityId, JsonNode body) { @@ -435,31 +414,17 @@ public abstract class BaseEdgeProcessor { } protected UpdateMsgType getUpdateMsgType(EdgeEventActionType actionType) { - switch (actionType) { - case UPDATED: - case CREDENTIALS_UPDATED: - case ASSIGNED_TO_CUSTOMER: - case UNASSIGNED_FROM_CUSTOMER: - case UPDATED_COMMENT: - return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; - case ADDED: - case ASSIGNED_TO_EDGE: - case RELATION_ADD_OR_UPDATE: - case ADDED_COMMENT: - return UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; - case DELETED: - case UNASSIGNED_FROM_EDGE: - case RELATION_DELETED: - case DELETED_COMMENT: - case ALARM_DELETE: - return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; - case ALARM_ACK: - return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; - case ALARM_CLEAR: - return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; - default: - throw new RuntimeException("Unsupported actionType [" + actionType + "]"); - } + return switch (actionType) { + case UPDATED, CREDENTIALS_UPDATED, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, UPDATED_COMMENT -> + UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; + case ADDED, ASSIGNED_TO_EDGE, RELATION_ADD_OR_UPDATE, ADDED_COMMENT -> + UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE; + case DELETED, UNASSIGNED_FROM_EDGE, RELATION_DELETED, DELETED_COMMENT, ALARM_DELETE -> + UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; + case ALARM_ACK -> UpdateMsgType.ALARM_ACK_RPC_MESSAGE; + case ALARM_CLEAR -> UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; + default -> throw new RuntimeException("Unsupported actionType [" + actionType + "]"); + }; } public ListenableFuture processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { @@ -554,15 +519,11 @@ public abstract class BaseEdgeProcessor { } private ListenableFuture processEntityNotificationForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId, EdgeId sourceEdgeId) { - switch (actionType) { - case ADDED: - case UPDATED: - case DELETED: - case CREDENTIALS_UPDATED: // used by USER entity - return processActionForAllEdges(tenantId, type, actionType, entityId, null, sourceEdgeId); - default: - return Futures.immediateFuture(null); - } + return switch (actionType) { + case ADDED, UPDATED, DELETED, CREDENTIALS_UPDATED -> // used by USER entity + processActionForAllEdges(tenantId, type, actionType, entityId, null, sourceEdgeId); + default -> Futures.immediateFuture(null); + }; } protected EntityId constructEntityId(String entityTypeStr, long entityIdMSB, long entityIdLSB) { @@ -605,26 +566,18 @@ public abstract class BaseEdgeProcessor { } protected boolean isEntityExists(TenantId tenantId, EntityId entityId) { - switch (entityId.getEntityType()) { - case TENANT: - return tenantService.findTenantById(tenantId) != null; - case DEVICE: - return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null; - case ASSET: - return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null; - case ENTITY_VIEW: - return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null; - case CUSTOMER: - return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null; - case USER: - return userService.findUserById(tenantId, new UserId(entityId.getId())) != null; - case DASHBOARD: - return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null; - case EDGE: - return edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null; - default: - return false; - } + return switch (entityId.getEntityType()) { + case TENANT -> tenantService.findTenantById(tenantId) != null; + case DEVICE -> deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null; + case ASSET -> assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null; + case ENTITY_VIEW -> + entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null; + case CUSTOMER -> customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null; + case USER -> userService.findUserById(tenantId, new UserId(entityId.getId())) != null; + case DASHBOARD -> dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null; + case EDGE -> edgeService.findEdgeById(tenantId, new EdgeId(entityId.getId())) != null; + default -> false; + }; } protected void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) { @@ -663,37 +616,25 @@ public abstract class BaseEdgeProcessor { } protected AssetProfile checkIfAssetProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, AssetProfile assetProfile, EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_3: - case V_3_3_0: - case V_3_4_0: - if (assetProfile.getDefaultDashboardId() != null - && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) { - assetProfile.setDefaultDashboardId(null); - } - if (assetProfile.getDefaultEdgeRuleChainId() != null - && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) { - assetProfile.setDefaultEdgeRuleChainId(null); - } - break; + if (EdgeVersion.V_3_3_0.equals(edgeVersion) || EdgeVersion.V_3_3_3.equals(edgeVersion) || EdgeVersion.V_3_4_0.equals(edgeVersion)) { + if (assetProfile.getDefaultDashboardId() != null && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) { + assetProfile.setDefaultDashboardId(null); + } + if (assetProfile.getDefaultEdgeRuleChainId() != null && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) { + assetProfile.setDefaultEdgeRuleChainId(null); + } } return assetProfile; } protected DeviceProfile checkIfDeviceProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { - switch (edgeVersion) { - case V_3_3_3: - case V_3_3_0: - case V_3_4_0: - if (deviceProfile.getDefaultDashboardId() != null - && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) { - deviceProfile.setDefaultDashboardId(null); - } - if (deviceProfile.getDefaultEdgeRuleChainId() != null - && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) { - deviceProfile.setDefaultEdgeRuleChainId(null); - } - break; + if (EdgeVersion.V_3_3_0.equals(edgeVersion) || EdgeVersion.V_3_3_3.equals(edgeVersion) || EdgeVersion.V_3_4_0.equals(edgeVersion)) { + if (deviceProfile.getDefaultDashboardId() != null && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) { + deviceProfile.setDefaultDashboardId(null); + } + if (deviceProfile.getDefaultEdgeRuleChainId() != null && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) { + deviceProfile.setDefaultEdgeRuleChainId(null); + } } return deviceProfile; } @@ -708,4 +649,5 @@ public abstract class BaseEdgeProcessor { } return true; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java index e69c479d93..4e9a0b82d8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java @@ -16,12 +16,18 @@ package org.thingsboard.server.service.edge.rpc.processor.telemetry; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -31,18 +37,28 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @TbCoreComponent public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { + @Value("${edges.rpc.max_telemetry_message_size:0}") + private int maxTelemetryMessageSize; + + @Lazy + @Autowired + private NotificationRuleProcessor notificationRuleProcessor; + @Override protected String getMsgSourceKey() { return DataConstants.EDGE_MSG_SOURCE; } - public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertTelemetryEventToDownlink(Edge edge, EdgeEvent edgeEvent) { if (edgeEvent.getBody() != null) { String bodyStr = edgeEvent.getBody().toString(); - if (bodyStr.length() > 1000) { - log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " + - "Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), - edgeEvent.getEntityId(), bodyStr.length(), StringUtils.truncate(bodyStr, 100)); + if (maxTelemetryMessageSize > 0 && bodyStr.length() > maxTelemetryMessageSize) { + String error = "Conversion to a DownlinkMsg telemetry event failed due to a size limit violation."; + String message = String.format("%s Current size is %s, but the limit is %s", error, bodyStr.length(), maxTelemetryMessageSize); + log.debug("[{}][{}][{}] {}. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), + edgeEvent.getEntityId(), message, StringUtils.truncate(bodyStr, 100)); + notificationRuleProcessor.process(EdgeCommunicationFailureTrigger.builder().tenantId(edgeEvent.getTenantId()) + .edgeId(edgeEvent.getEdgeId()).customerId(edge.getCustomerId()).edgeName(edge.getName()).failureMsg(message).error(error).build()); return null; } } @@ -55,4 +71,5 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { .addEntityData(entityDataProto) .build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index 2c192fab1d..515dccb987 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -60,4 +60,5 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } + } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index daf9f5d1c1..7616fa7805 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1304,6 +1304,8 @@ edges: private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}" # Maximum size (in bytes) of inbound messages the cloud can handle from the edge. By default, it can handle messages up to 4 Megabytes max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}" + # Maximum length of telemetry (time-series and attributes) message the cloud sends to the edge. By default, there is no limitation. + max_telemetry_message_size: "${EDGES_RPC_MAX_TELEMETRY_MESSAGE_SIZE:300}" storage: # Max records of edge event to read from DB and sent to the edge max_read_records_count: "${EDGES_STORAGE_MAX_READ_RECORDS_COUNT:50}" diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java index 4e5b080b65..f3ed8e12e6 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java @@ -526,17 +526,18 @@ public abstract class BaseEdgeProcessorTest { } protected static Stream provideParameters() { - UUID dashoboardUUID = UUID.randomUUID(); - UUID ruleChaindUUID = UUID.randomUUID(); + UUID dashboardUUID = UUID.randomUUID(); + UUID ruleChainUUID = UUID.randomUUID(); return Stream.of( Arguments.of(EdgeVersion.V_3_3_0, 0, 0, 0, 0), Arguments.of(EdgeVersion.V_3_3_3, 0, 0, 0, 0), Arguments.of(EdgeVersion.V_3_4_0, 0, 0, 0, 0), Arguments.of(EdgeVersion.V_3_6_0, - dashoboardUUID.getMostSignificantBits(), - dashoboardUUID.getLeastSignificantBits(), - ruleChaindUUID.getMostSignificantBits(), - ruleChaindUUID.getLeastSignificantBits()) + dashboardUUID.getMostSignificantBits(), + dashboardUUID.getLeastSignificantBits(), + ruleChainUUID.getMostSignificantBits(), + ruleChainUUID.getLeastSignificantBits()) ); } + } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java index a6e28e7968..94714af463 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java @@ -16,27 +16,51 @@ package org.thingsboard.server.service.edge.rpc.processor.telemetry; import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorTest; -@Slf4j -@RunWith(MockitoJUnitRunner.class) -public class TelemetryEdgeProcessorTest { +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; + +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = {TelemetryEdgeProcessor.class}) +@TestPropertySource(properties = { + "edges.rpc.max_telemetry_message_size=1000" +}) +public class TelemetryEdgeProcessorTest extends BaseEdgeProcessorTest { + + @SpyBean + private TelemetryEdgeProcessor telemetryEdgeProcessor; + + @MockBean + private NotificationRuleProcessor notificationRuleProcessor; @Test - public void testConvert_maxSizeLimit() throws Exception { + public void testConvert_maxSizeLimit() { + Edge edge = new Edge(); EdgeEvent edgeEvent = new EdgeEvent(); ObjectNode body = JacksonUtil.newObjectNode(); - body.put("value", StringUtils.randomAlphanumeric(10000)); + body.put("value", StringUtils.randomAlphanumeric(1000)); edgeEvent.setBody(body); - DownlinkMsg downlinkMsg = new TelemetryEdgeProcessor().convertTelemetryEventToDownlink(edgeEvent); + + DownlinkMsg downlinkMsg = telemetryEdgeProcessor.convertTelemetryEventToDownlink(edge, edgeEvent); Assert.assertNull(downlinkMsg); + + verify(notificationRuleProcessor, Mockito.times(1)).process(any()); } + } diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java index ca6e826cac..38f84bb4a1 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -434,36 +434,28 @@ public class ProtoUtils { } public static TransportProtos.ToDeviceActorNotificationMsgProto toProto(ToDeviceActorNotificationMsg msg) { - if (msg instanceof DeviceEdgeUpdateMsg) { - DeviceEdgeUpdateMsg updateMsg = (DeviceEdgeUpdateMsg) msg; + if (msg instanceof DeviceEdgeUpdateMsg updateMsg) { TransportProtos.DeviceEdgeUpdateMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceEdgeUpdateMsg(proto).build(); - } else if (msg instanceof DeviceNameOrTypeUpdateMsg) { - DeviceNameOrTypeUpdateMsg updateMsg = (DeviceNameOrTypeUpdateMsg) msg; + } else if (msg instanceof DeviceNameOrTypeUpdateMsg updateMsg) { TransportProtos.DeviceNameOrTypeUpdateMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceNameOrTypeMsg(proto).build(); - } else if (msg instanceof DeviceAttributesEventNotificationMsg) { - DeviceAttributesEventNotificationMsg updateMsg = (DeviceAttributesEventNotificationMsg) msg; + } else if (msg instanceof DeviceAttributesEventNotificationMsg updateMsg) { TransportProtos.DeviceAttributesEventMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceAttributesEventMsg(proto).build(); - } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg) { - DeviceCredentialsUpdateNotificationMsg updateMsg = (DeviceCredentialsUpdateNotificationMsg) msg; + } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg updateMsg) { TransportProtos.DeviceCredentialsUpdateMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceCredentialsUpdateMsg(proto).build(); - } else if (msg instanceof ToDeviceRpcRequestActorMsg) { - ToDeviceRpcRequestActorMsg updateMsg = (ToDeviceRpcRequestActorMsg) msg; + } else if (msg instanceof ToDeviceRpcRequestActorMsg updateMsg) { TransportProtos.ToDeviceRpcRequestActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setToDeviceRpcRequestMsg(proto).build(); - } else if (msg instanceof FromDeviceRpcResponseActorMsg) { - FromDeviceRpcResponseActorMsg updateMsg = (FromDeviceRpcResponseActorMsg) msg; + } else if (msg instanceof FromDeviceRpcResponseActorMsg updateMsg) { TransportProtos.FromDeviceRpcResponseActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setFromDeviceRpcResponseMsg(proto).build(); - } else if (msg instanceof RemoveRpcActorMsg) { - RemoveRpcActorMsg updateMsg = (RemoveRpcActorMsg) msg; + } else if (msg instanceof RemoveRpcActorMsg updateMsg) { TransportProtos.RemoveRpcActorMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setRemoveRpcActorMsg(proto).build(); - } else if (msg instanceof DeviceDeleteMsg) { - DeviceDeleteMsg updateMsg = (DeviceDeleteMsg) msg; + } else if (msg instanceof DeviceDeleteMsg updateMsg) { TransportProtos.DeviceDeleteMsgProto proto = toProto(updateMsg); return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceDeleteMsg(proto).build(); } @@ -507,24 +499,14 @@ public class ProtoUtils { List result = new ArrayList<>(); for (TransportProtos.AttributeValueProto kvEntry : valuesList) { boolean hasValue = kvEntry.getHasV(); - KvEntry entry = null; - switch (kvEntry.getType()) { - case BOOLEAN_V: - entry = new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null); - break; - case LONG_V: - entry = new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null); - break; - case DOUBLE_V: - entry = new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null); - break; - case STRING_V: - entry = new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null); - break; - case JSON_V: - entry = new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null); - break; - } + KvEntry entry = switch (kvEntry.getType()) { + case BOOLEAN_V -> new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null); + case LONG_V -> new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null); + case DOUBLE_V -> new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null); + case STRING_V -> new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null); + case JSON_V -> new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null); + default -> null; + }; result.add(new BaseAttributeKvEntry(kvEntry.getLastUpdateTs(), entry)); } return result; @@ -1029,15 +1011,11 @@ public class ProtoUtils { .setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()) .setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); - PowerSavingConfiguration psmConfiguration = null; - switch (device.getDeviceData().getTransportConfiguration().getType()) { - case LWM2M: - psmConfiguration = (Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); - break; - case COAP: - psmConfiguration = (CoapDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); - break; - } + PowerSavingConfiguration psmConfiguration = switch (device.getDeviceData().getTransportConfiguration().getType()) { + case LWM2M -> (Lwm2mDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); + case COAP -> (CoapDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); + default -> null; + }; if (psmConfiguration != null) { PowerMode powerMode = psmConfiguration.getPowerMode(); @@ -1079,4 +1057,5 @@ public class ProtoUtils { private static Long checkLong(Long l) { return isNotNull(l) ? l : 0; } + } From be611d0aec045bf49f7333e65a1457052b344a7b Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Fri, 29 Mar 2024 16:49:48 +0200 Subject: [PATCH 081/107] Set limitation for max telemetry for edge to 0 --- application/src/main/resources/thingsboard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 7616fa7805..fdfcf8754b 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -1305,7 +1305,7 @@ edges: # Maximum size (in bytes) of inbound messages the cloud can handle from the edge. By default, it can handle messages up to 4 Megabytes max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}" # Maximum length of telemetry (time-series and attributes) message the cloud sends to the edge. By default, there is no limitation. - max_telemetry_message_size: "${EDGES_RPC_MAX_TELEMETRY_MESSAGE_SIZE:300}" + max_telemetry_message_size: "${EDGES_RPC_MAX_TELEMETRY_MESSAGE_SIZE:0}" storage: # Max records of edge event to read from DB and sent to the edge max_read_records_count: "${EDGES_STORAGE_MAX_READ_RECORDS_COUNT:50}" From 1a9e69c762982400deb06a9b06efd2bb207e1aef Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 29 Mar 2024 18:58:16 +0200 Subject: [PATCH 082/107] UI: Update range chart advanced settings. Add label background option to series/threshold labels. Minor improvements and fixes. --- .../range-chart-basic-config.component.html | 26 ++- .../range-chart-basic-config.component.ts | 34 ++- .../widget/lib/chart/echarts-widget.models.ts | 101 ++++++++- .../lib/chart/range-chart-widget.models.ts | 49 +++-- .../lib/chart/time-series-chart-bar.models.ts | 3 + .../lib/chart/time-series-chart.models.ts | 99 +++++---- .../widget/lib/chart/time-series-chart.ts | 7 +- ...range-chart-widget-settings.component.html | 202 +++++++++++++++--- .../range-chart-widget-settings.component.ts | 127 ++++++++++- ...e-series-chart-bar-settings.component.html | 9 + ...ime-series-chart-bar-settings.component.ts | 14 +- ...-series-chart-line-settings.component.html | 13 +- ...me-series-chart-line-settings.component.ts | 22 +- ...-series-chart-fill-settings.component.html | 4 +- ...me-series-chart-fill-settings.component.ts | 2 +- ...-series-chart-threshold-row.component.html | 15 +- ...me-series-chart-threshold-row.component.ts | 74 +++---- ...rt-threshold-settings-panel.component.html | 19 +- ...hart-threshold-settings-panel.component.ts | 40 +++- ...es-chart-threshold-settings.component.html | 39 ++++ ...ries-chart-threshold-settings.component.ts | 124 +++++++++++ .../common/widget-settings-common.module.ts | 5 + .../assets/locale/locale.constant-en_US.json | 10 +- .../assets/locale/locale.constant-pl_PL.json | 2 +- .../assets/locale/locale.constant-zh_CN.json | 2 +- 25 files changed, 834 insertions(+), 208 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index 4bda5fa9b5..eb94f9c977 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -102,10 +102,17 @@ formControlName="outOfRangeColor">
-
+
{{ 'widgets.range-chart.show-range-thresholds' | translate }} + +
@@ -115,8 +122,8 @@
widgets.range-chart.fill-area-opacity
- +
@@ -193,12 +200,21 @@
+
+ + {{ 'widgets.time-series-chart.series.point.point-label-background' | translate }} + + + +
widgets.time-series-chart.series.point.point-shape
- - {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + {{ echartsShapeTranslations.get(shape) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts index fc006d05a3..40c9bb9d12 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts @@ -49,11 +49,10 @@ import { lineSeriesStepTypeTranslations, seriesLabelPositions, seriesLabelPositionTranslations, - timeSeriesChartShapes, - timeSeriesChartShapeTranslations, timeSeriesLineTypes, timeSeriesLineTypeTranslations } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { echartsShapes, echartsShapeTranslations } from '@home/components/widget/lib/chart/echarts-widget.models'; @Component({ selector: 'tb-range-chart-basic-config', @@ -83,9 +82,9 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { seriesLabelPositionTranslations = seriesLabelPositionTranslations; - timeSeriesChartShapes = timeSeriesChartShapes; + echartsShapes = echartsShapes; - timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations; + echartsShapeTranslations = echartsShapeTranslations; legendPositions = legendPositions; @@ -139,8 +138,9 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { rangeColors: [settings.rangeColors, []], outOfRangeColor: [settings.outOfRangeColor, []], showRangeThresholds: [settings.showRangeThresholds, []], + rangeThreshold: [settings.rangeThreshold, []], fillArea: [settings.fillArea, []], - fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(100)]], + fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(1)]], showLine: [settings.showLine, []], step: [settings.step, []], @@ -154,6 +154,8 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { pointLabelPosition: [settings.pointLabelPosition, []], pointLabelFont: [settings.pointLabelFont, []], pointLabelColor: [settings.pointLabelColor, []], + enablePointLabelBackground: [settings.enablePointLabelBackground, []], + pointLabelBackground: [settings.pointLabelBackground, []], pointShape: [settings.pointShape, []], pointSize: [settings.pointSize, [Validators.min(0)]], @@ -216,6 +218,7 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.settings.rangeColors = config.rangeColors; this.widgetConfig.config.settings.outOfRangeColor = config.outOfRangeColor; this.widgetConfig.config.settings.showRangeThresholds = config.showRangeThresholds; + this.widgetConfig.config.settings.rangeThreshold = config.rangeThreshold; this.widgetConfig.config.settings.fillArea = config.fillArea; this.widgetConfig.config.settings.fillAreaOpacity = config.fillAreaOpacity; @@ -231,6 +234,8 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.widgetConfig.config.settings.pointLabelPosition = config.pointLabelPosition; this.widgetConfig.config.settings.pointLabelFont = config.pointLabelFont; this.widgetConfig.config.settings.pointLabelColor = config.pointLabelColor; + this.widgetConfig.config.settings.enablePointLabelBackground = config.enablePointLabelBackground; + this.widgetConfig.config.settings.pointLabelBackground = config.pointLabelBackground; this.widgetConfig.config.settings.pointShape = config.pointShape; this.widgetConfig.config.settings.pointSize = config.pointSize; @@ -268,16 +273,19 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { } protected validatorTriggers(): string[] { - return ['showTitle', 'showIcon', 'fillArea', 'showLine', 'step', 'showPointLabel', 'showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showTitle', 'showIcon', 'showRangeThresholds', 'fillArea', 'showLine', + 'step', 'showPointLabel', 'enablePointLabelBackground', 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean, trigger?: string) { const showTitle: boolean = this.rangeChartWidgetConfigForm.get('showTitle').value; const showIcon: boolean = this.rangeChartWidgetConfigForm.get('showIcon').value; + const showRangeThresholds: boolean = this.rangeChartWidgetConfigForm.get('showRangeThresholds').value; const fillArea: boolean = this.rangeChartWidgetConfigForm.get('fillArea').value; const showLine: boolean = this.rangeChartWidgetConfigForm.get('showLine').value; const step: boolean = this.rangeChartWidgetConfigForm.get('step').value; const showPointLabel: boolean = this.rangeChartWidgetConfigForm.get('showPointLabel').value; + const enablePointLabelBackground: boolean = this.rangeChartWidgetConfigForm.get('enablePointLabelBackground').value; const showLegend: boolean = this.rangeChartWidgetConfigForm.get('showLegend').value; const showTooltip: boolean = this.rangeChartWidgetConfigForm.get('showTooltip').value; const tooltipShowDate: boolean = this.rangeChartWidgetConfigForm.get('tooltipShowDate').value; @@ -309,6 +317,12 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.rangeChartWidgetConfigForm.get('iconColor').disable(); } + if (showRangeThresholds) { + this.rangeChartWidgetConfigForm.get('rangeThreshold').enable(); + } else { + this.rangeChartWidgetConfigForm.get('rangeThreshold').disable(); + } + if (fillArea) { this.rangeChartWidgetConfigForm.get('fillAreaOpacity').enable(); } else { @@ -337,10 +351,18 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { this.rangeChartWidgetConfigForm.get('pointLabelPosition').enable(); this.rangeChartWidgetConfigForm.get('pointLabelFont').enable(); this.rangeChartWidgetConfigForm.get('pointLabelColor').enable(); + this.rangeChartWidgetConfigForm.get('enablePointLabelBackground').enable({emitEvent: false}); + if (enablePointLabelBackground) { + this.rangeChartWidgetConfigForm.get('pointLabelBackground').enable(); + } else { + this.rangeChartWidgetConfigForm.get('pointLabelBackground').disable(); + } } else { this.rangeChartWidgetConfigForm.get('pointLabelPosition').disable(); this.rangeChartWidgetConfigForm.get('pointLabelFont').disable(); this.rangeChartWidgetConfigForm.get('pointLabelColor').disable(); + this.rangeChartWidgetConfigForm.get('enablePointLabelBackground').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('pointLabelBackground').disable(); } if (showLegend) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts index c9c4b319cc..81ffb457b9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts @@ -17,7 +17,7 @@ import * as echarts from 'echarts/core'; import AxisModel from 'echarts/types/src/coord/cartesian/AxisModel'; import { estimateLabelUnionRect } from 'echarts/lib/coord/axisHelper'; -import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { formatValue, isDefinedAndNotNull, isNumber } from '@core/utils'; import { DataZoomComponent, DataZoomComponentOption, @@ -55,6 +55,7 @@ import { DateFormatProcessor, DateFormatSettings, Font } from '@shared/models/wi import GlobalModel from 'echarts/types/src/model/Global'; import Axis2D from 'echarts/types/src/coord/cartesian/Axis2D'; import SeriesModel from 'echarts/types/src/model/Series'; +import { MarkLine2DDataItemOption } from 'echarts/types/src/component/marker/MarkLineModel'; class EChartsModule { private initialized = false; @@ -110,6 +111,51 @@ export type EChartsSeriesItem = { decimals?: number; }; +export enum EChartsShape { + emptyCircle = 'emptyCircle', + circle = 'circle', + rect = 'rect', + roundRect = 'roundRect', + triangle = 'triangle', + diamond = 'diamond', + pin = 'pin', + arrow = 'arrow', + none = 'none' +} + +export const echartsShapes = Object.keys(EChartsShape) as EChartsShape[]; + +export const echartsShapeTranslations = new Map( + [ + [EChartsShape.emptyCircle, 'widgets.time-series-chart.shape-empty-circle'], + [EChartsShape.circle, 'widgets.time-series-chart.shape-circle'], + [EChartsShape.rect, 'widgets.time-series-chart.shape-rect'], + [EChartsShape.roundRect, 'widgets.time-series-chart.shape-round-rect'], + [EChartsShape.triangle, 'widgets.time-series-chart.shape-triangle'], + [EChartsShape.diamond, 'widgets.time-series-chart.shape-diamond'], + [EChartsShape.pin, 'widgets.time-series-chart.shape-pin'], + [EChartsShape.arrow, 'widgets.time-series-chart.shape-arrow'], + [EChartsShape.none, 'widgets.time-series-chart.shape-none'] + ] +); + +type EChartsShapeOffsetFunction = (size: number) => number; + +export const timeSeriesChartShapeOffsetFunctions = new Map( + [ + [EChartsShape.emptyCircle, size => size / 2 + 1], + [EChartsShape.circle, size => size / 2], + [EChartsShape.rect, size => size / 2], + [EChartsShape.roundRect, size => size / 2], + [EChartsShape.triangle, size => size / 2], + [EChartsShape.diamond, size => size / 2], + [EChartsShape.pin, size => size], + [EChartsShape.arrow, () => 0], + [EChartsShape.none, () => 0], + ] +); + + export const timeAxisBandWidthCalculator: TimeAxisBandWidthCalculator = (model) => { let interval: number; const axisOption = model.option; @@ -211,7 +257,23 @@ export const measureXAxisNameHeight = (chart: ECharts, name: string): number => return 0; }; -export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thresholdId: string, value: any): [number, number] => { +const measureSymbolOffset = (symbol: string, symbolSize: any): number => { + if (isNumber(symbolSize)) { + if (symbol) { + const offsetFunction = timeSeriesChartShapeOffsetFunctions.get(symbol as EChartsShape); + if (offsetFunction) { + return offsetFunction(symbolSize); + } else { + return symbolSize / 2; + } + } + } else { + return 0; + } +} + +export const measureThresholdOffset = (chart: ECharts, axisId: string, thresholdId: string, value: any): [number, number] => { + const offset: [number, number] = [0,0]; const axis = getYAxis(chart, axisId); if (axis && !axis.scale.isBlank()) { const extent = axis.scale.getExtent(); @@ -220,6 +282,16 @@ export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thre if (models?.length) { const lineSeriesModel = models[0] as SeriesModel; const markLineModel = lineSeriesModel.getModel('markLine'); + const dataOption = markLineModel.get('data'); + for (const dataItemOption of dataOption) { + const dataItem = dataItemOption as MarkLine2DDataItemOption; + const start = dataItem[0]; + const startOffset = measureSymbolOffset(start.symbol, start.symbolSize); + offset[0] = Math.max(offset[0], startOffset); + const end = dataItem[1]; + const endOffset = measureSymbolOffset(end.symbol, end.symbolSize); + offset[1] = Math.max(offset[1], endOffset); + } const labelPosition = markLineModel.get(['label', 'position']); if (labelPosition === 'start' || labelPosition === 'end') { const labelModel = markLineModel.getModel('label'); @@ -239,23 +311,38 @@ export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thre } } if (!textWidth) { - return [0,0]; + return offset; } const distanceOpt = markLineModel.get(['label', 'distance']); let distance = 5; if (distanceOpt) { distance = typeof distanceOpt === 'number' ? distanceOpt : distanceOpt[0]; } - const offset = distance + textWidth; + const paddingOpt = markLineModel.get(['label', 'padding']); + let leftPadding = 0; + let rightPadding = 0; + if (paddingOpt) { + if (Array.isArray(paddingOpt)) { + if (paddingOpt.length === 4) { + leftPadding = paddingOpt[3]; + rightPadding = paddingOpt[1]; + } else if (paddingOpt.length === 2) { + leftPadding = rightPadding = paddingOpt[1]; + } + } else { + leftPadding = rightPadding = paddingOpt; + } + } + const textOffset = distance + textWidth + leftPadding + rightPadding; if (labelPosition === 'start') { - return [offset, 0]; + offset[0] = Math.max(offset[0], textOffset); } else { - return [0, offset]; + offset[1] = Math.max(offset[1], textOffset); } } } } - return [0,0]; + return offset; }; export const getAxisExtent = (chart: ECharts, axisId: string): [number, number] => { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index eb6ea4a397..e04dccf74d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -24,22 +24,21 @@ import { sortedColorRange } from '@shared/models/widget-settings.models'; import { LegendPosition } from '@shared/models/widget.models'; -import { EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; +import { EChartsShape, EChartsTooltipWidgetSettings } from '@home/components/widget/lib/chart/echarts-widget.models'; import { createTimeSeriesChartVisualMapPiece, defaultTimeSeriesChartXAxisSettings, defaultTimeSeriesChartYAxisSettings, LineSeriesStepType, SeriesFillType, - SeriesLabelPosition, timeSeriesChartAnimationDefaultSettings, + SeriesLabelPosition, ThresholdLabelPosition, timeSeriesChartAnimationDefaultSettings, TimeSeriesChartAnimationSettings, timeSeriesChartColorScheme, TimeSeriesChartKeySettings, TimeSeriesChartLineType, TimeSeriesChartSeriesType, TimeSeriesChartSettings, - TimeSeriesChartShape, - TimeSeriesChartThreshold, + TimeSeriesChartThreshold, timeSeriesChartThresholdDefaultSettings, TimeSeriesChartThresholdType, TimeSeriesChartVisualMapPiece, TimeSeriesChartXAxisSettings, @@ -64,6 +63,7 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { rangeColors: Array; outOfRangeColor: string; showRangeThresholds: boolean; + rangeThreshold: Partial; fillArea: boolean; fillAreaOpacity: number; showLine: boolean; @@ -77,7 +77,9 @@ export interface RangeChartWidgetSettings extends EChartsTooltipWidgetSettings { pointLabelPosition: SeriesLabelPosition; pointLabelFont: Font; pointLabelColor: string; - pointShape: TimeSeriesChartShape; + enablePointLabelBackground: boolean; + pointLabelBackground: string; + pointShape: EChartsShape; pointSize: number; yAxis: TimeSeriesChartYAxisSettings; xAxis: TimeSeriesChartXAxisSettings; @@ -104,6 +106,17 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { ], outOfRangeColor: '#ccc', showRangeThresholds: true, + rangeThreshold: mergeDeep({} as Partial, + timeSeriesChartThresholdDefaultSettings, + { lineColor: '#37383b', + lineType: TimeSeriesChartLineType.dashed, + startSymbol: EChartsShape.circle, + startSymbolSize: 5, + endSymbol: EChartsShape.arrow, + endSymbolSize: 7, + labelPosition: ThresholdLabelPosition.insideEndTop, + labelColor: '#37383b', + enableLabelBackground: true}), fillArea: true, fillAreaOpacity: 0.7, showLine: true, @@ -124,7 +137,9 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { lineHeight: '1' }, pointLabelColor: timeSeriesChartColorScheme['series.label'].light, - pointShape: TimeSeriesChartShape.emptyCircle, + enablePointLabelBackground: false, + pointLabelBackground: 'rgba(255,255,255,0.56)', + pointShape: EChartsShape.emptyCircle, pointSize: 4, yAxis: mergeDeep({} as TimeSeriesChartYAxisSettings, defaultTimeSeriesChartYAxisSettings, @@ -185,26 +200,12 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { export const rangeChartTimeSeriesSettings = (settings: RangeChartWidgetSettings, rangeItems: RangeItem[], decimals: number, units: string): DeepPartial => { let thresholds: DeepPartial[] = settings.showRangeThresholds ? getMarkPoints(rangeItems).map(item => ({ - type: TimeSeriesChartThresholdType.constant, + ...{type: TimeSeriesChartThresholdType.constant, yAxisId: 'default', units, decimals, - lineWidth: 1, - lineColor: '#37383b', - lineType: [3, 3], - startSymbol: TimeSeriesChartShape.circle, - startSymbolSize: 5, - endSymbol: TimeSeriesChartShape.arrow, - endSymbolSize: 7, - showLabel: true, - labelPosition: 'insideEndTop', - labelColor: '#37383b', - additionalLabelOption: { - backgroundColor: 'rgba(255,255,255,0.56)', - padding: [4, 5], - borderRadius: 4, - }, - value: item + value: item}, + ...settings.rangeThreshold } as DeepPartial)) : []; if (settings.thresholds?.length) { thresholds = thresholds.concat(settings.thresholds); @@ -254,6 +255,8 @@ export const rangeChartTimeSeriesKeySettings = (settings: RangeChartWidgetSettin pointLabelPosition: settings.pointLabelPosition, pointLabelFont: settings.pointLabelFont, pointLabelColor: settings.pointLabelColor, + enablePointLabelBackground: settings.enablePointLabelBackground, + pointLabelBackground: settings.pointLabelBackground, pointShape: settings.pointShape, pointSize: settings.pointSize, fillAreaSettings: { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts index 8ebe189ee0..ee3c703a17 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts @@ -152,6 +152,9 @@ export const renderTimeSeriesBar = (params: CustomSeriesRenderItemParams, api: C } as CallbackDataParams); style.textDistance = 5; style.textPosition = position; + style.textBackgroundColor = renderCtx.labelOption.backgroundColor; + style.textPadding = renderCtx.labelOption.padding; + style.textBorderRadius = renderCtx.labelOption.borderRadius; style.rich = renderCtx.labelOption.rich; if (renderCtx.additionalLabelOption) { style = {...style, ...renderCtx.additionalLabelOption}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts index 4dc36b6682..3589d69d4f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -17,10 +17,10 @@ import { ECharts, EChartsOption, - EChartsSeriesItem, + EChartsSeriesItem, EChartsShape, EChartsTooltipTrigger, EChartsTooltipWidgetSettings, - measureThresholdLabelOffset, + measureThresholdOffset, timeAxisBandWidthCalculator } from '@home/components/widget/lib/chart/echarts-widget.models'; import { @@ -134,34 +134,6 @@ export const timeSeriesAxisPositionTranslations = new Map( ] ); -export enum TimeSeriesChartShape { - emptyCircle = 'emptyCircle', - circle = 'circle', - rect = 'rect', - roundRect = 'roundRect', - triangle = 'triangle', - diamond = 'diamond', - pin = 'pin', - arrow = 'arrow', - none = 'none' -} - -export const timeSeriesChartShapes = Object.keys(TimeSeriesChartShape) as TimeSeriesChartShape[]; - -export const timeSeriesChartShapeTranslations = new Map( - [ - [TimeSeriesChartShape.emptyCircle, 'widgets.time-series-chart.shape-empty-circle'], - [TimeSeriesChartShape.circle, 'widgets.time-series-chart.shape-circle'], - [TimeSeriesChartShape.rect, 'widgets.time-series-chart.shape-rect'], - [TimeSeriesChartShape.roundRect, 'widgets.time-series-chart.shape-round-rect'], - [TimeSeriesChartShape.triangle, 'widgets.time-series-chart.shape-triangle'], - [TimeSeriesChartShape.diamond, 'widgets.time-series-chart.shape-diamond'], - [TimeSeriesChartShape.pin, 'widgets.time-series-chart.shape-pin'], - [TimeSeriesChartShape.arrow, 'widgets.time-series-chart.shape-arrow'], - [TimeSeriesChartShape.none, 'widgets.time-series-chart.shape-none'] - ] -); - export enum TimeSeriesChartLineType { solid = 'solid', dashed = 'dashed', @@ -456,14 +428,16 @@ export interface TimeSeriesChartThreshold { lineColor: string; lineType: TimeSeriesChartLineType | number | number[]; lineWidth: number; - startSymbol: TimeSeriesChartShape; + startSymbol: EChartsShape; startSymbolSize: number; - endSymbol: TimeSeriesChartShape; + endSymbol: EChartsShape; endSymbolSize: number; showLabel: boolean; labelPosition: ThresholdLabelPosition; labelFont: Font; labelColor: string; + enableLabelBackground: boolean; + labelBackground: string; additionalLabelOption?: {[key: string]: any}; } @@ -509,9 +483,9 @@ export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold = lineColor: timeSeriesChartColorScheme['threshold.line'].light, lineType: TimeSeriesChartLineType.solid, lineWidth: 1, - startSymbol: TimeSeriesChartShape.none, + startSymbol: EChartsShape.none, startSymbolSize: 5, - endSymbol: TimeSeriesChartShape.arrow, + endSymbol: EChartsShape.arrow, endSymbolSize: 5, showLabel: true, labelPosition: ThresholdLabelPosition.end, @@ -523,7 +497,9 @@ export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold = weight: '400', lineHeight: '1' }, - labelColor: timeSeriesChartColorScheme['threshold.label'].light + labelColor: timeSeriesChartColorScheme['threshold.label'].light, + enableLabelBackground: false, + labelBackground: 'rgba(255,255,255,0.56)' }; export enum TimeSeriesChartNoAggregationBarWidthStrategy { @@ -744,8 +720,10 @@ export interface LineSeriesSettings { pointLabelPosition: SeriesLabelPosition; pointLabelFont: Font; pointLabelColor: string; + enablePointLabelBackground: boolean; + pointLabelBackground: string; pointLabelFormatter?: string | LabelFormatterCallback; - pointShape: TimeSeriesChartShape; + pointShape: EChartsShape; pointSize: number; fillAreaSettings: SeriesFillSettings; } @@ -758,6 +736,8 @@ export interface BarSeriesSettings { labelPosition: SeriesLabelPosition | BuiltinTextPosition; labelFont: Font; labelColor: string; + enableLabelBackground: boolean; + labelBackground: string; labelFormatter?: string | LabelFormatterCallback; labelLayout?: LabelLayoutOption | LabelLayoutOptionCallback; additionalLabelOption?: {[key: string]: any}; @@ -797,7 +777,9 @@ export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = { lineHeight: '1' }, pointLabelColor: timeSeriesChartColorScheme['series.label'].light, - pointShape: TimeSeriesChartShape.emptyCircle, + enablePointLabelBackground: false, + pointLabelBackground: 'rgba(255,255,255,0.56)', + pointShape: EChartsShape.emptyCircle, pointSize: 4, fillAreaSettings: { type: SeriesFillType.none, @@ -823,6 +805,8 @@ export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = { lineHeight: '1' }, labelColor: timeSeriesChartColorScheme['series.label'].light, + enableLabelBackground: false, + labelBackground: 'rgba(255,255,255,0.56)', backgroundSettings: { type: SeriesFillType.none, opacity: 0.4, @@ -1080,7 +1064,7 @@ export const calculateThresholdsOffset = (chart: ECharts, const result: [number, number] = [0, 0]; for (const item of thresholdItems) { const yAxis = yAxisList[item.yAxisIndex]; - const offset = measureThresholdLabelOffset(chart, yAxis.id, item.id, item.value); + const offset = measureThresholdOffset(chart, yAxis.id, item.id, item.value); result[0] = Math.max(result[0], offset[0]); result[1] = Math.max(result[1], offset[1]); } @@ -1141,6 +1125,11 @@ const generateChartThresholds = (thresholdItems: TimeSeriesChartThresholdItem[]) } } }; + if (item.settings.enableLabelBackground) { + seriesOption.markLine.label.backgroundColor = item.settings.labelBackground; + seriesOption.markLine.label.padding = [4, 5]; + seriesOption.markLine.label.borderRadius = 4; + } if (item.settings.additionalLabelOption) { seriesOption.markLine.label = {...seriesOption.markLine.label, ...item.settings.additionalLabelOption}; } @@ -1259,7 +1248,7 @@ export const updateDarkMode = (options: EChartsOption, settings: TimeSeriesChart } else { if (item.barRenderContext?.labelOption?.show) { const barSettings = item.dataKey.settings as BarSeriesSettings; - item.barRenderContext.labelOption.rich.value.color = prepareChartThemeColor(barSettings.labelColor, + (item.barRenderContext.labelOption.rich.value as any).fill = prepareChartThemeColor(barSettings.labelColor, darkMode, 'series.label'); } } @@ -1303,8 +1292,10 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, const lineSeriesOption = seriesOption as LineSeriesOption; lineSeriesOption.type = 'line'; lineSeriesOption.label = createSeriesLabelOption(item, lineSettings.showPointLabel, - lineSettings.pointLabelFont, lineSettings.pointLabelColor, lineSettings.pointLabelPosition, - lineSettings.pointLabelFormatter, darkMode); + lineSettings.pointLabelFont, lineSettings.pointLabelColor, + lineSettings.enablePointLabelBackground, lineSettings.pointLabelBackground, + lineSettings.pointLabelPosition, + lineSettings.pointLabelFormatter, false, darkMode); lineSeriesOption.step = lineSettings.step ? lineSettings.stepType : false; lineSeriesOption.smooth = lineSettings.smooth; if (lineSettings.smooth) { @@ -1345,7 +1336,8 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, } item.barRenderContext.visualSettings = barVisualSettings; item.barRenderContext.labelOption = createSeriesLabelOption(item, barSettings.showLabel, - barSettings.labelFont, barSettings.labelColor, barSettings.labelPosition, barSettings.labelFormatter, darkMode); + barSettings.labelFont, barSettings.labelColor, barSettings.enableLabelBackground, barSettings.labelBackground, + barSettings.labelPosition, barSettings.labelFormatter, true, darkMode); item.barRenderContext.additionalLabelOption = barSettings.additionalLabelOption; barSeriesOption.renderItem = (params, api) => renderTimeSeriesBar(params, api, item.barRenderContext); @@ -1357,12 +1349,15 @@ const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, }; const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean, - labelFont: Font, labelColor: string, position: SeriesLabelPosition | BuiltinTextPosition, + labelFont: Font, labelColor: string, + enableBackground: boolean, labelBackground: string, + position: SeriesLabelPosition | BuiltinTextPosition, labelFormatter: string | LabelFormatterCallback, + labelColorFill: boolean, darkMode: boolean): SeriesLabelOption => { let labelStyle: ComponentStyle = {}; if (show) { - labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label'); + labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label', labelColorFill); } let formatter: LabelFormatterCallback; if (isFunction(labelFormatter)) { @@ -1386,7 +1381,7 @@ const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean, return `{value|${value}}`; }; } - return { + const labelOption: SeriesLabelOption = { show, position, formatter, @@ -1394,13 +1389,23 @@ const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean, value: labelStyle } }; + if (enableBackground) { + labelOption.backgroundColor = labelBackground; + labelOption.padding = [4, 5]; + labelOption.borderRadius = 4; + } + return labelOption; }; -const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string): ComponentStyle => { +const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string, fill = false): ComponentStyle => { const style = textStyle(font); delete style.lineHeight; style.fontSize = font.size; - style.color = prepareChartThemeColor(color, darkMode, colorKey); + if (fill) { + style.fill = prepareChartThemeColor(color, darkMode, colorKey); + } else { + style.color = prepareChartThemeColor(color, darkMode, colorKey); + } return style; }; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index 98cc1fd454..00f2c55d3f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -32,7 +32,6 @@ import { TimeSeriesChartNoAggregationBarWidthStrategy, TimeSeriesChartSeriesType, TimeSeriesChartSettings, - TimeSeriesChartShape, TimeSeriesChartThreshold, timeSeriesChartThresholdDefaultSettings, TimeSeriesChartThresholdItem, @@ -49,7 +48,7 @@ import { calculateYAxisWidth, ECharts, echartsModule, - EChartsOption, + EChartsOption, EChartsShape, echartsTooltipFormatter, EChartsTooltipTrigger, getAxisExtent, @@ -88,7 +87,7 @@ export class TbTimeSeriesChart { settings.type = TimeSeriesChartSeriesType.line; settings.lineSettings.showLine = false; settings.lineSettings.showPoints = true; - settings.lineSettings.pointShape = TimeSeriesChartShape.circle; + settings.lineSettings.pointShape = EChartsShape.circle; settings.lineSettings.pointSize = 8; } return settings; @@ -738,7 +737,7 @@ export class TbTimeSeriesChart { private minTopOffset(): number { const showTickLabels = !!this.yAxisList.find(yAxis => yAxis.settings.show && yAxis.settings.showTickLabels); - return (this.topPointLabels) ? 20 : + return (this.topPointLabels) ? 25 : (showTickLabels ? 10 : 5); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html index 4a7331d4c2..a19b8eb845 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.html @@ -17,29 +17,175 @@ -->
-
widgets.range-chart.range-chart-card-style
+
widgets.range-chart.range-chart-style
{{ 'widgets.range-chart.data-zoom' | translate }}
-
-
{{ 'widgets.range-chart.range-colors' | translate }}
- - +
+
widgets.range-chart.range-chart-appearance
+
+
{{ 'widgets.range-chart.range-colors' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.out-of-range-color' | translate }}
+ + +
+
+ + {{ 'widgets.range-chart.show-range-thresholds' | translate }} + + + +
+
+ + {{ 'widgets.range-chart.fill-area' | translate }} + +
+
+
widgets.range-chart.fill-area-opacity
+ + + +
+
+
widgets.time-series-chart.series.line.line
+
+ + {{ 'widgets.time-series-chart.series.line.show-line' | translate }} + +
+
+ + {{ 'widgets.time-series-chart.series.line.step-line' | translate }} + + + + + {{ lineSeriesStepTypeTranslations.get(stepType) | translate }} + + + +
+
+ + {{ 'widgets.time-series-chart.series.line.smooth-line' | translate }} + +
+
+
widgets.time-series-chart.line-type
+ + + + {{ timeSeriesLineTypeTranslations.get(lineType) | translate }} + + + +
+
+
widgets.time-series-chart.line-width
+ + + +
+
+
+
widgets.time-series-chart.series.point.points
+
+ + {{ 'widgets.time-series-chart.series.point.show-points' | translate }} + +
+
+ +
+ {{ 'widgets.time-series-chart.series.point.point-label' | translate }} +
+
+
+ + + + {{ seriesLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+
+ + {{ 'widgets.time-series-chart.series.point.point-label-background' | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-shape
+ + + + {{ echartsShapeTranslations.get(shape) | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-size
+ + + +
+
-
-
{{ 'widgets.range-chart.out-of-range-color' | translate }}
- - +
+
widgets.time-series-chart.axis.y-axis
+ +
-
- - {{ 'widgets.range-chart.fill-area' | translate }} - +
+
widgets.time-series-chart.axis.x-axis
+ +
+ +
@@ -139,16 +285,22 @@
-
-
{{ 'widgets.background.background' | translate }}
- - -
-
-
{{ 'widget-config.card-padding' | translate }}
- - - + + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts index 4ef2971467..b9a058af24 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts @@ -16,17 +16,24 @@ import { Component, Injector } from '@angular/core'; import { + Datasource, legendPositions, legendPositionTranslationMap, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { formatValue } from '@core/utils'; import { rangeChartDefaultSettings } from '@home/components/widget/lib/chart/range-chart-widget.models'; import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; +import { + lineSeriesStepTypes, lineSeriesStepTypeTranslations, + seriesLabelPositions, seriesLabelPositionTranslations, + timeSeriesLineTypes, timeSeriesLineTypeTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { echartsShapes, echartsShapeTranslations } from '@home/components/widget/lib/chart/echarts-widget.models'; @Component({ selector: 'tb-range-chart-widget-settings', @@ -35,12 +42,39 @@ import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-s }) export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + lineSeriesStepTypes = lineSeriesStepTypes; + + lineSeriesStepTypeTranslations = lineSeriesStepTypeTranslations; + + timeSeriesLineTypes = timeSeriesLineTypes; + + timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations; + + seriesLabelPositions = seriesLabelPositions; + + seriesLabelPositionTranslations = seriesLabelPositionTranslations; + + echartsShapes = echartsShapes; + + echartsShapeTranslations = echartsShapeTranslations; + legendPositions = legendPositions; legendPositionTranslationMap = legendPositionTranslationMap; rangeChartWidgetSettingsForm: UntypedFormGroup; + pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this); + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); @@ -64,7 +98,34 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { dataZoom: [settings.dataZoom, []], rangeColors: [settings.rangeColors, []], outOfRangeColor: [settings.outOfRangeColor, []], + showRangeThresholds: [settings.showRangeThresholds, []], + rangeThreshold: [settings.rangeThreshold, []], fillArea: [settings.fillArea, []], + fillAreaOpacity: [settings.fillAreaOpacity, [Validators.min(0), Validators.max(1)]], + + showLine: [settings.showLine, []], + step: [settings.step, []], + stepType: [settings.stepType, []], + smooth: [settings.smooth, []], + lineType: [settings.lineType, []], + lineWidth: [settings.lineWidth, [Validators.min(0)]], + + showPoints: [settings.showPoints, []], + showPointLabel: [settings.showPointLabel, []], + pointLabelPosition: [settings.pointLabelPosition, []], + pointLabelFont: [settings.pointLabelFont, []], + pointLabelColor: [settings.pointLabelColor, []], + enablePointLabelBackground: [settings.enablePointLabelBackground, []], + pointLabelBackground: [settings.pointLabelBackground, []], + pointShape: [settings.pointShape, []], + pointSize: [settings.pointSize, [Validators.min(0)]], + + yAxis: [settings.yAxis, []], + xAxis: [settings.xAxis, []], + + thresholds: [settings.thresholds, []], + + animation: [settings.animation, []], showLegend: [settings.showLegend, []], legendPosition: [settings.legendPosition, []], @@ -89,14 +150,69 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { } protected validatorTriggers(): string[] { - return ['showLegend', 'showTooltip', 'tooltipShowDate']; + return ['showRangeThresholds', 'fillArea', 'showLine', 'step', 'showPointLabel', 'enablePointLabelBackground', + 'showLegend', 'showTooltip', 'tooltipShowDate']; } protected updateValidators(emitEvent: boolean) { + const showRangeThresholds: boolean = this.rangeChartWidgetSettingsForm.get('showRangeThresholds').value; + const fillArea: boolean = this.rangeChartWidgetSettingsForm.get('fillArea').value; + const showLine: boolean = this.rangeChartWidgetSettingsForm.get('showLine').value; + const step: boolean = this.rangeChartWidgetSettingsForm.get('step').value; + const showPointLabel: boolean = this.rangeChartWidgetSettingsForm.get('showPointLabel').value; + const enablePointLabelBackground: boolean = this.rangeChartWidgetSettingsForm.get('enablePointLabelBackground').value; const showLegend: boolean = this.rangeChartWidgetSettingsForm.get('showLegend').value; const showTooltip: boolean = this.rangeChartWidgetSettingsForm.get('showTooltip').value; const tooltipShowDate: boolean = this.rangeChartWidgetSettingsForm.get('tooltipShowDate').value; + if (showRangeThresholds) { + this.rangeChartWidgetSettingsForm.get('rangeThreshold').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('rangeThreshold').disable(); + } + + if (fillArea) { + this.rangeChartWidgetSettingsForm.get('fillAreaOpacity').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('fillAreaOpacity').disable(); + } + + if (showLine) { + this.rangeChartWidgetSettingsForm.get('step').enable({emitEvent: false}); + if (step) { + this.rangeChartWidgetSettingsForm.get('stepType').enable(); + this.rangeChartWidgetSettingsForm.get('smooth').disable(); + } else { + this.rangeChartWidgetSettingsForm.get('stepType').disable(); + this.rangeChartWidgetSettingsForm.get('smooth').enable(); + } + this.rangeChartWidgetSettingsForm.get('lineType').enable(); + this.rangeChartWidgetSettingsForm.get('lineWidth').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('step').disable({emitEvent: false}); + this.rangeChartWidgetSettingsForm.get('stepType').disable(); + this.rangeChartWidgetSettingsForm.get('smooth').disable(); + this.rangeChartWidgetSettingsForm.get('lineType').disable(); + this.rangeChartWidgetSettingsForm.get('lineWidth').disable(); + } + if (showPointLabel) { + this.rangeChartWidgetSettingsForm.get('pointLabelPosition').enable(); + this.rangeChartWidgetSettingsForm.get('pointLabelFont').enable(); + this.rangeChartWidgetSettingsForm.get('pointLabelColor').enable(); + this.rangeChartWidgetSettingsForm.get('enablePointLabelBackground').enable({emitEvent: false}); + if (enablePointLabelBackground) { + this.rangeChartWidgetSettingsForm.get('pointLabelBackground').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('pointLabelBackground').disable(); + } + } else { + this.rangeChartWidgetSettingsForm.get('pointLabelPosition').disable(); + this.rangeChartWidgetSettingsForm.get('pointLabelFont').disable(); + this.rangeChartWidgetSettingsForm.get('pointLabelColor').disable(); + this.rangeChartWidgetSettingsForm.get('enablePointLabelBackground').disable({emitEvent: false}); + this.rangeChartWidgetSettingsForm.get('pointLabelBackground').disable(); + } + if (showLegend) { this.rangeChartWidgetSettingsForm.get('legendPosition').enable(); this.rangeChartWidgetSettingsForm.get('legendLabelFont').enable(); @@ -137,6 +253,12 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { } } + private _pointLabelPreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, false); + } + private _tooltipValuePreviewFn(): string { const units: string = this.widgetConfig.config.units; const decimals: number = this.widgetConfig.config.decimals; @@ -149,5 +271,4 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { processor.update(Date.now()); return processor.formatted; } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html index 447784091e..ddbeaf2480 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.html @@ -59,6 +59,15 @@
+
+ + {{ 'widgets.time-series-chart.series.bar.label-background' | translate }} + + + +
{ this.updateModel(); }); merge(this.barSettingsFormGroup.get('showBorder').valueChanges, - this.barSettingsFormGroup.get('showLabel').valueChanges) + this.barSettingsFormGroup.get('showLabel').valueChanges, + this.barSettingsFormGroup.get('enableLabelBackground').valueChanges) .subscribe(() => { this.updateValidators(); }); @@ -116,6 +119,7 @@ export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValue private updateValidators() { const showBorder: boolean = this.barSettingsFormGroup.get('showBorder').value; const showLabel: boolean = this.barSettingsFormGroup.get('showLabel').value; + const enableLabelBackground: boolean = this.barSettingsFormGroup.get('enableLabelBackground').value; if (showBorder) { this.barSettingsFormGroup.get('borderWidth').enable({emitEvent: false}); } else { @@ -125,10 +129,18 @@ export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValue this.barSettingsFormGroup.get('labelPosition').enable({emitEvent: false}); this.barSettingsFormGroup.get('labelFont').enable({emitEvent: false}); this.barSettingsFormGroup.get('labelColor').enable({emitEvent: false}); + this.barSettingsFormGroup.get('enableLabelBackground').enable({emitEvent: false}); + if (enableLabelBackground) { + this.barSettingsFormGroup.get('labelBackground').enable({emitEvent: false}); + } else { + this.barSettingsFormGroup.get('labelBackground').disable({emitEvent: false}); + } } else { this.barSettingsFormGroup.get('labelPosition').disable({emitEvent: false}); this.barSettingsFormGroup.get('labelFont').disable({emitEvent: false}); this.barSettingsFormGroup.get('labelColor').disable({emitEvent: false}); + this.barSettingsFormGroup.get('enableLabelBackground').disable({emitEvent: false}); + this.barSettingsFormGroup.get('labelBackground').disable({emitEvent: false}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html index 78f1129727..cc3102c475 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html @@ -97,12 +97,21 @@
+
+ + {{ 'widgets.time-series-chart.series.point.point-label-background' | translate }} + + + +
widgets.time-series-chart.series.point.point-shape
- - {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + {{ echartsShapeTranslations.get(shape) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts index 84533b26e4..5b4701bf07 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts @@ -28,11 +28,11 @@ import { lineSeriesStepTypeTranslations, seriesLabelPositions, seriesLabelPositionTranslations, - timeSeriesChartShapes, - timeSeriesChartShapeTranslations, TimeSeriesChartType, + TimeSeriesChartType, timeSeriesLineTypes, timeSeriesLineTypeTranslations } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { echartsShapes, echartsShapeTranslations } from '@home/components/widget/lib/chart/echarts-widget.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { merge } from 'rxjs'; @@ -67,9 +67,9 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu seriesLabelPositionTranslations = seriesLabelPositionTranslations; - timeSeriesChartShapes = timeSeriesChartShapes; + echartsShapes = echartsShapes; - timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations; + echartsShapeTranslations = echartsShapeTranslations; pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this); @@ -103,6 +103,8 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu pointLabelPosition: [null, []], pointLabelFont: [null, []], pointLabelColor: [null, []], + enablePointLabelBackground: [null, []], + pointLabelBackground: [null, []], pointShape: [null, []], pointSize: [null, [Validators.min(0)]], fillAreaSettings: [null, []] @@ -112,7 +114,8 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu }); merge(this.lineSettingsFormGroup.get('showLine').valueChanges, this.lineSettingsFormGroup.get('step').valueChanges, - this.lineSettingsFormGroup.get('showPointLabel').valueChanges) + this.lineSettingsFormGroup.get('showPointLabel').valueChanges, + this.lineSettingsFormGroup.get('enablePointLabelBackground').valueChanges) .subscribe(() => { this.updateValidators(); }); @@ -147,6 +150,7 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu const showLine: boolean = this.lineSettingsFormGroup.get('showLine').value; const step: boolean = this.lineSettingsFormGroup.get('step').value; const showPointLabel: boolean = this.lineSettingsFormGroup.get('showPointLabel').value; + const enablePointLabelBackground: boolean = this.lineSettingsFormGroup.get('enablePointLabelBackground').value; if (showLine) { this.lineSettingsFormGroup.get('step').enable({emitEvent: false}); if (step) { @@ -169,10 +173,18 @@ export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValu this.lineSettingsFormGroup.get('pointLabelPosition').enable({emitEvent: false}); this.lineSettingsFormGroup.get('pointLabelFont').enable({emitEvent: false}); this.lineSettingsFormGroup.get('pointLabelColor').enable({emitEvent: false}); + this.lineSettingsFormGroup.get('enablePointLabelBackground').enable({emitEvent: false}); + if (enablePointLabelBackground) { + this.lineSettingsFormGroup.get('pointLabelBackground').enable({emitEvent: false}); + } else { + this.lineSettingsFormGroup.get('pointLabelBackground').disable({emitEvent: false}); + } } else { this.lineSettingsFormGroup.get('pointLabelPosition').disable({emitEvent: false}); this.lineSettingsFormGroup.get('pointLabelFont').disable({emitEvent: false}); this.lineSettingsFormGroup.get('pointLabelColor').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('enablePointLabelBackground').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('pointLabelBackground').disable({emitEvent: false}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html index 934f985483..99e3c1be8f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.html @@ -27,8 +27,8 @@
widgets.time-series-chart.series.opacity
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts index 44cc870f5b..9fbc464348 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts @@ -73,7 +73,7 @@ export class TimeSeriesChartFillSettingsComponent implements OnInit, ControlValu ngOnInit(): void { this.fillSettingsFormGroup = this.fb.group({ type: [null, []], - opacity: [null, [Validators.min(0), Validators.max(100)]], + opacity: [null, [Validators.min(0), Validators.max(1)]], gradient: this.fb.group({ start: [null, [Validators.min(0), Validators.max(100)]], end: [null, [Validators.min(0), Validators.max(100)]] diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html index 386ea306a0..d0ea8f0ef3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html @@ -98,14 +98,13 @@
- + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts new file mode 100644 index 0000000000..6908b086af --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component.ts @@ -0,0 +1,124 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { deepClone } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { WidgetConfig } from '@shared/models/widget.models'; +import { + TimeSeriesChartThreshold, + TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { + TimeSeriesChartThresholdSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component'; + +@Component({ + selector: 'tb-time-series-chart-threshold-settings', + templateUrl: './time-series-chart-threshold-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartThresholdSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartThresholdSettingsComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + widgetConfig: WidgetConfig; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + + @Input() + @coerceBoolean() + hideYAxis = false; + + @Input() + @coerceBoolean() + boxButton = false; + + @Input() + icon = 'settings'; + + @Input() + title = 'widgets.time-series-chart.threshold.threshold-settings'; + + private modelValue: Partial; + + private propagateChange = null; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: Partial): void { + this.modelValue = value; + } + + openThresholdSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + thresholdSettings: deepClone(this.modelValue), + panelTitle: this.title, + widgetConfig: this.widgetConfig, + hideYAxis: this.hideYAxis, + yAxisIds: this.yAxisIds + }; + const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, TimeSeriesChartThresholdSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + thresholdSettingsPanelPopover.tbComponentRef.instance.popover = thresholdSettingsPanelPopover; + thresholdSettingsPanelPopover.tbComponentRef.instance.thresholdSettingsApplied.subscribe((thresholdSettings) => { + thresholdSettingsPanelPopover.hide(); + this.modelValue = thresholdSettings; + this.propagateChange(this.modelValue); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index 08d45775c5..86e460ff9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -130,6 +130,9 @@ import { import { TimeSeriesChartFillSettingsComponent } from '@home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component'; +import { + TimeSeriesChartThresholdSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings.component'; @NgModule({ declarations: [ @@ -178,6 +181,7 @@ import { TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, TimeSeriesChartFillSettingsComponent, + TimeSeriesChartThresholdSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], @@ -232,6 +236,7 @@ import { TimeSeriesChartYAxisSettingsPanelComponent, TimeSeriesChartAnimationSettingsComponent, TimeSeriesChartFillSettingsComponent, + TimeSeriesChartThresholdSettingsComponent, DataKeyInputComponent, EntityAliasInputComponent ], diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b2dbc2b29f..43bd687886 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6176,9 +6176,10 @@ "range-colors": "Range colors", "out-of-range-color": "Out of range color", "show-range-thresholds": "Show range thresholds", + "range-thresholds-settings": "Range thresholds settings", "fill-area": "Fill area", "fill-area-opacity": "Fill area opacity", - "range-chart-card-style": "Range chart card style" + "range-chart-style": "Range chart style" }, "rpc": { "value-settings": "Value settings", @@ -6772,7 +6773,8 @@ "label-position-inside-middle-bottom": "Inside middle bottom", "label-position-inside-end": "Inside end", "label-position-inside-end-top": "Inside end top", - "label-position-inside-end-bottom": "Inside end bottom" + "label-position-inside-end-bottom": "Inside end bottom", + "label-background": "Label background" }, "axis": { "axes": "Axes", @@ -6853,6 +6855,7 @@ "show-points": "Show points", "point-label": "Point label", "point-label-hint": "Display label with value over the series point.", + "point-label-background": "Point label background", "point-shape": "Point shape", "point-size": "Point size" }, @@ -6861,7 +6864,8 @@ "border-width": "Border width", "border-radius": "Border radius", "label": "Label", - "label-hint": "Display label with value over the bar." + "label-hint": "Display label with value over the bar.", + "label-background": "Label background" } } }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 820a616382..58d4a0168f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -5924,7 +5924,7 @@ "range-colors":"Kolory zakresu", "out-of-range-color":"Kolor poza zakresem", "fill-area":"Wypełnij obszar", - "range-chart-card-style":"Styl karty wykresu zakresu" + "range-chart-style":"Styl wykresu zakresu" }, "rpc":{ "value-settings":"Ustawienia wartości", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index fdc0274cdf..886151fdf2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -5491,7 +5491,7 @@ "range-colors": "范围颜色", "out-of-range-color": "超出范围颜色", "fill-area": "填充区域", - "range-chart-card-style": "范围图表卡片样式" + "range-chart-style": "范围图样式" }, "rpc": { "value-settings": "值设置", From e761b1ec4754f14841f4a2a61c481505e0aacd36 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 29 Mar 2024 19:37:41 +0200 Subject: [PATCH 083/107] UI: Fix scss style path. --- .../common/chart/time-series-chart-fill-settings.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts index 9fbc464348..dd7f4dd7f6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-fill-settings.component.ts @@ -34,7 +34,7 @@ import { AppState } from '@core/core.state'; @Component({ selector: 'tb-time-series-chart-fill-settings', templateUrl: './time-series-chart-fill-settings.component.html', - styleUrls: ['./../widget-settings.scss'], + styleUrls: ['./../../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, From 2888ac5db97fde51097714e70a9fac10c5970b7a Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 1 Apr 2024 12:32:32 +0300 Subject: [PATCH 084/107] Improve EntityDataQuery using 2 query: filter and sort to find entityIds and using entityIds as List to map with telemetry and entity fields --- .../controller/EntityQueryController.java | 1 - .../server/dao/entity/BaseEntityService.java | 56 ++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java index e6e57e8b8c..505244539b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java @@ -20,7 +20,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index b91e2ed96d..2b426ad686 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasEmail; import org.thingsboard.server.common.data.HasLabel; @@ -34,13 +35,18 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityData; +import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityFilterType; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityListFilter; import org.thingsboard.server.common.data.query.RelationsQueryFilter; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import java.util.List; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Collectors; import static org.thingsboard.server.common.data.id.EntityId.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateEntityDataPageLink; @@ -79,7 +85,51 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateEntityDataQuery(query); - return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); + + if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType())) { + return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); + } + + // 1 step - find entity data by filter and sort columns + PageData entityDataByQuery = findEntityIdsByFilterAndSorterColumns(tenantId, customerId, query); + if (entityDataByQuery == null || entityDataByQuery.getData().isEmpty()) { + return entityDataByQuery; + } + // 2 step - find entity data by entity ids from the 1st step + PageData result = findEntityDataByEntityIds(tenantId, customerId, query, entityDataByQuery.getData()); + return new PageData<>(result.getData(), entityDataByQuery.getTotalPages(), entityDataByQuery.getTotalElements(), entityDataByQuery.hasNext()); + } + + private PageData findEntityIdsByFilterAndSorterColumns(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { + List entityFields = null; + List latestValues = null; + if (query.getPageLink().getSortOrder() != null) { + if (query.getEntityFields() != null) { + entityFields = query.getEntityFields().stream() + .filter(entityKey -> entityKey.getKey().equals(query.getPageLink().getSortOrder().getKey().getKey())) + .collect(Collectors.toList()); + } + if (query.getLatestValues() != null) { + latestValues = query.getLatestValues().stream() + .filter(entityKey -> entityKey.getKey().equals(query.getPageLink().getSortOrder().getKey().getKey())) + .collect(Collectors.toList()); + } + } + EntityDataQuery entityQuery = new EntityDataQuery(query.getEntityFilter(), query.getPageLink(), entityFields, latestValues, query.getKeyFilters()); + return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, entityQuery); + } + + private PageData findEntityDataByEntityIds(TenantId tenantId, CustomerId customerId, EntityDataQuery query, List data) { + List entityIds = data.stream().map(d -> d.getEntityId().getId().toString()).toList(); + EntityType entityType = data.isEmpty() ? null : data.get(0).getEntityId().getEntityType(); + + EntityListFilter filter = new EntityListFilter(); + filter.setEntityType(entityType); + filter.setEntityList(entityIds); + + EntityDataPageLink pageLink = new EntityDataPageLink(query.getPageLink().getPageSize(), 0, null, query.getPageLink().getSortOrder()); + EntityDataQuery entityQuery = new EntityDataQuery(filter, pageLink, query.getEntityFields(), query.getLatestValues(), null); + return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, entityQuery); } @Override @@ -133,8 +183,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe } private CustomerId getCustomerId(HasId entity) { - if (entity instanceof HasCustomerId) { - HasCustomerId hasCustomerId = (HasCustomerId) entity; + if (entity instanceof HasCustomerId hasCustomerId) { CustomerId customerId = hasCustomerId.getCustomerId(); if (customerId == null) { customerId = NULL_CUSTOMER_ID; @@ -176,4 +225,5 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe throw new IncorrectParameterException("Relation query filter root entity should not be blank"); } } + } From 596d0f113543551fc4c928457ea435c967426908 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 1 Apr 2024 15:46:01 +0300 Subject: [PATCH 085/107] Improvement optimization is not perfroming while text search is not null --- .../org/thingsboard/server/dao/entity/BaseEntityService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 2b426ad686..eac63276eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -86,7 +86,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateEntityDataQuery(query); - if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType())) { + if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType()) || query.getPageLink().getTextSearch() != null) { return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); } From 87e564f8c0107df9475d7e9d681c85b4c5700477 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 1 Apr 2024 16:39:44 +0300 Subject: [PATCH 086/107] Use stringUtils to check if search text is not null and is not empty --- .../org/thingsboard/server/controller/UserControllerTest.java | 3 ++- .../org/thingsboard/server/dao/entity/BaseEntityService.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java index d9c35826d4..72930e0327 100644 --- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java @@ -1109,13 +1109,14 @@ public class UserControllerTest extends AbstractControllerTest { private List getUsersInfo(PageLink pageLink) throws Exception { List loadedCustomerUsers = new ArrayList<>(); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/users/info?", new TypeReference<>() { }, pageLink); loadedCustomerUsers.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); + Assert.assertEquals(pageLink.getPageSize(), pageData.getData().size()); } } while (pageData.hasNext()); return loadedCustomerUsers; diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index eac63276eb..a0067a07ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -86,7 +86,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateEntityDataQuery(query); - if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType()) || query.getPageLink().getTextSearch() != null) { + if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType()) || StringUtils.isNotEmpty(query.getPageLink().getTextSearch())) { return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); } From fe5b4606eb82cc71a86929161c763017c4334fe1 Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Mon, 1 Apr 2024 16:51:31 +0300 Subject: [PATCH 087/107] Improvement: do not use 2 queries for single_entity --- .../org/thingsboard/server/dao/entity/BaseEntityService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index a0067a07ca..05ed5c7971 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -86,7 +86,9 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); validateEntityDataQuery(query); - if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType()) || StringUtils.isNotEmpty(query.getPageLink().getTextSearch())) { + if (EntityFilterType.RELATIONS_QUERY.equals(query.getEntityFilter().getType()) + || EntityFilterType.SINGLE_ENTITY.equals(query.getEntityFilter().getType()) + || StringUtils.isNotEmpty(query.getPageLink().getTextSearch())) { return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); } From 5e39af738b33e1c502146555b986058524a60e9c Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Sat, 9 Mar 2024 12:22:29 +0100 Subject: [PATCH 088/107] In Validator a Function provided to build an error message only if necessary. Signed-off-by: Oleksandra Matviienko --- .../server/dao/service/Validator.java | 67 +++++++++++++++++-- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index b3a121baf6..1cf94f2081 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -38,7 +38,7 @@ public class Validator { * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception * - * @param entityId the entityId + * @param entityId the entityId * @param errorMessage the error message for exception */ public static void validateEntityId(EntityId entityId, String errorMessage) { @@ -51,8 +51,8 @@ public class Validator { * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception * - * @param entityId the entityId - * @param errorMessageFunction the error message for exception that apply entityId + * @param entityId the entityId + * @param errorMessageFunction the error message for exception that applies entityId */ public static void validateEntityId(EntityId entityId, Function errorMessageFunction) { if (entityId == null || entityId.getId() == null) { @@ -73,12 +73,12 @@ public class Validator { } } - /* + /** * This method validate String string. If string is invalid than throw * IncorrectParameterException exception * - * @param val the value - * @param errorMessageFunction the error message function that apply value + * @param val the value + * @param errorMessageFunction the error message function that applies value */ public static void validateString(String val, Function errorMessageFunction) { if (val == null || val.isEmpty()) { @@ -112,6 +112,18 @@ public class Validator { } } + /** + * This method validate UUID id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param errorMessageFunction the error message function for exception that applies id + */ + public static void validateId(UUID id, Function errorMessageFunction) { + if (id == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(id)); + } + } /** * This method validate UUIDBased id. If id is null than throw @@ -126,6 +138,19 @@ public class Validator { } } + /** + * This method validate UUIDBased id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param errorMessageFunction the error message for exception that applies id + */ + public static void validateId(UUIDBased id, Function errorMessageFunction) { + if (id == null || id.getId() == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(id)); + } + } + /** * This method validate list of UUIDBased ids. If at least one of the ids is null than throw * IncorrectParameterException exception @@ -143,6 +168,23 @@ public class Validator { } } + /** + * This method validate list of UUIDBased ids. If at least one of the ids is null than throw + * IncorrectParameterException exception + * + * @param ids the list of ids + * @param errorMessageFunction the error message for exception that applies ids + */ + public static void validateIds(List ids, Function, String> errorMessageFunction) { + if (ids == null || ids.isEmpty()) { + throw new IncorrectParameterException(errorMessageFunction.apply(ids)); + } else { + for (UUIDBased id : ids) { + validateId(id, errorMessageFunction.apply(ids)); + } + } + } + /** * This method validate PageLink page link. If pageLink is invalid than throw * IncorrectParameterException exception @@ -189,4 +231,17 @@ public class Validator { } } + /** + * This method validate list of UUIDBased ids. If at least one of the ids is null than throw + * IncorrectParameterException exception + * + * @param reference the list of ids + * @param errorMessageFunction the error message for exception that applies reference + */ + public static void checkNotNull(Object reference, Function errorMessageFunction) { + if (reference == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(reference)); + } + } + } From 85e80a56aeb05a53d56ba299b4c0caf13601892c Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Wed, 13 Mar 2024 14:40:58 +0100 Subject: [PATCH 089/107] Tests for Validator are added. Signed-off-by: Oleksandra Matviienko --- .../server/common/data/id/UUIDBased.java | 2 +- .../server/dao/service/Validator.java | 17 +- .../server/dao/service/ValidatorTest.java | 164 ++++++++++++++++++ 3 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java index ac56ccef12..b9fd9d30ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java @@ -68,7 +68,7 @@ public abstract class UUIDBased implements HasUUID, Serializable { @Override public String toString() { - return id.toString(); + return String.valueOf(id); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index 1cf94f2081..be56414a33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -28,6 +28,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import java.util.List; import java.util.UUID; import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Pattern; public class Validator { @@ -151,6 +152,19 @@ public class Validator { } } + /** + * This method validate UUIDBased id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param errorMessageSupplier the error message for exception supplier + */ + static void validateId(UUIDBased id, Supplier errorMessageSupplier) { + if (id == null || id.getId() == null) { + throw new IncorrectParameterException(errorMessageSupplier.get()); + } + } + /** * This method validate list of UUIDBased ids. If at least one of the ids is null than throw * IncorrectParameterException exception @@ -179,8 +193,9 @@ public class Validator { if (ids == null || ids.isEmpty()) { throw new IncorrectParameterException(errorMessageFunction.apply(ids)); } else { + Supplier errorMessageSupplier = () -> errorMessageFunction.apply(ids); for (UUIDBased id : ids) { - validateId(id, errorMessageFunction.apply(ids)); + validateId(id, errorMessageSupplier); } } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java new file mode 100644 index 0000000000..0372d6ba80 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -0,0 +1,164 @@ +/** + * 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.dao.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.dao.exception.IncorrectParameterException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + + +class ValidatorTest { + + final DeviceId goodDeviceId = new DeviceId(UUID.fromString("18594c15-9f05-4cda-b58e-70172467c3e5")); + final UserId nullUserId = new UserId(null); + + @BeforeEach + void setUp() { + } + + @Test + void validateEntityIdTest() { + Validator.validateEntityId(TenantId.SYS_TENANT_ID, id -> "Incorrect entityId " + id); + Validator.validateEntityId((goodDeviceId), id -> "Incorrect entityId " + id); + + assertThatThrownBy(() -> Validator.validateEntityId( null, id -> "Incorrect entityId " + id)) + .as("EntityId is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect entityId null"); + + assertThatThrownBy(() -> Validator.validateEntityId(nullUserId, id -> "Incorrect entityId " + id)) + .as("EntityId with null UUID") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect entityId null"); + + } + + @Test + void validateStringTest() { + Validator.validateString("Hello", s -> "Incorrect string " + s); + Validator.validateString(" ", s -> "Incorrect string " + s); + Validator.validateString("\n", s -> "Incorrect string " + s); + + assertThatThrownBy(() -> Validator.validateString(null, s -> "Incorrect string " + s)) + .as("String is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect string null"); + + assertThatThrownBy(() -> Validator.validateString("", s -> "Incorrect string " + s)) + .as("String is empty") + .isInstanceOf(IncorrectParameterException.class) + .hasMessage("Incorrect string "); + + assertThatThrownBy(() -> Validator.validateString("", s -> "Incorrect string [" + s + "]")) + .as("String is empty []") + .isInstanceOf(IncorrectParameterException.class) + .hasMessage("Incorrect string []"); + } + + @Test + void validateUUIDIdTest() { + Validator.validateId(UUID.randomUUID(), id -> "Incorrect Id " + id); + + assertThatThrownBy(() -> Validator.validateId((UUID) null, id -> "Incorrect Id " + id)) + .as("Id is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + + } + + @Test + void validateUUIDBasedIdTest() { + Validator.validateId(TenantId.SYS_TENANT_ID, id -> "Incorrect Id " + id); + Validator.validateId((goodDeviceId), id -> "Incorrect Id " + id); + + assertThatThrownBy(() -> Validator.validateId((UUIDBased) null, id -> "Incorrect Id " + id)) + .as("Id is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + + assertThatThrownBy(() -> Validator.validateId(nullUserId, id -> "Incorrect Id " + id)) + .as("Id with null UUIDBased") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + + } + + @Test + void validateIds() { + + List list = List.of(goodDeviceId); + Validator.validateIds( list, ids -> "Incorrect Id " + ids); + Validator.validateId(list.get(0), id -> "Incorrect Id " + id); + + assertThatThrownBy(() -> Validator.validateIds(null, id -> "Incorrect Ids " + id)) + .as("Ids are null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Ids null"); + + assertThatThrownBy(() -> Validator.validateIds(Collections.emptyList(), ids -> "Incorrect Ids " + ids)) + .as("List is empty") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Ids []"); + + ArrayList badList = new ArrayList<>(2); + badList.add(goodDeviceId); + badList.add(null); + + // Incorrect Ids [18594c15-9f05-4cda-b58e-70172467c3e5, null] + assertThatThrownBy(() -> Validator.validateIds(badList, ids -> "Incorrect Ids " + ids)) + .as("List contains null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Ids ") + .hasMessageContaining(goodDeviceId.getId().toString()) + .hasMessageContaining("null"); + + } + @Test + void validateIdSupplier() { + Validator.validateId(TenantId.SYS_TENANT_ID, () -> "Incorrect Id null"); + Validator.validateId((goodDeviceId), () -> "Incorrect Id null"); + + assertThatThrownBy(() -> Validator.validateId((UUIDBased) null, () -> "Incorrect Id null")) + .as("Id is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + + assertThatThrownBy(() -> Validator.validateId(nullUserId, () -> "Incorrect Id null")) + .as("Id with null UUIDBased") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect Id null"); + } + + @Test + void checkNotNullTest() { + Validator.checkNotNull("notnull", reference -> "Incorrect reference " + reference); + + assertThatThrownBy(() -> Validator.checkNotNull(null, reference -> "Incorrect reference " + reference)) + .as("Reference is null") + .isInstanceOf(IncorrectParameterException.class) + .hasMessageContaining("Incorrect reference null"); + + } + +} From c2b53479fbebf744c3b191e60835cd9522043e94 Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Fri, 15 Mar 2024 12:08:45 +0100 Subject: [PATCH 090/107] UPD. Refactoring and changes according to comments. Signed-off-by: Oleksandra Matviienko --- .../server/dao/service/Validator.java | 17 +---------------- .../server/dao/service/ValidatorTest.java | 17 ++--------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index be56414a33..ff11c3de81 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -193,9 +193,8 @@ public class Validator { if (ids == null || ids.isEmpty()) { throw new IncorrectParameterException(errorMessageFunction.apply(ids)); } else { - Supplier errorMessageSupplier = () -> errorMessageFunction.apply(ids); for (UUIDBased id : ids) { - validateId(id, errorMessageSupplier); + validateId(id, errorMessageFunction.apply(ids)); } } } @@ -245,18 +244,4 @@ public class Validator { throw new IncorrectParameterException(errorMessage); } } - - /** - * This method validate list of UUIDBased ids. If at least one of the ids is null than throw - * IncorrectParameterException exception - * - * @param reference the list of ids - * @param errorMessageFunction the error message for exception that applies reference - */ - public static void checkNotNull(Object reference, Function errorMessageFunction) { - if (reference == null) { - throw new IncorrectParameterException(errorMessageFunction.apply(reference)); - } - } - } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java index 0372d6ba80..7a6fa231b2 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -105,11 +105,9 @@ class ValidatorTest { } @Test - void validateIds() { - + void validateIdsTest() { List list = List.of(goodDeviceId); Validator.validateIds( list, ids -> "Incorrect Id " + ids); - Validator.validateId(list.get(0), id -> "Incorrect Id " + id); assertThatThrownBy(() -> Validator.validateIds(null, id -> "Incorrect Ids " + id)) .as("Ids are null") @@ -134,6 +132,7 @@ class ValidatorTest { .hasMessageContaining("null"); } + @Test void validateIdSupplier() { Validator.validateId(TenantId.SYS_TENANT_ID, () -> "Incorrect Id null"); @@ -149,16 +148,4 @@ class ValidatorTest { .isInstanceOf(IncorrectParameterException.class) .hasMessageContaining("Incorrect Id null"); } - - @Test - void checkNotNullTest() { - Validator.checkNotNull("notnull", reference -> "Incorrect reference " + reference); - - assertThatThrownBy(() -> Validator.checkNotNull(null, reference -> "Incorrect reference " + reference)) - .as("Reference is null") - .isInstanceOf(IncorrectParameterException.class) - .hasMessageContaining("Incorrect reference null"); - - } - } From 4eeb0b288352be691811c8d0f0d60ba52f6ddb2f Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Fri, 15 Mar 2024 12:13:08 +0100 Subject: [PATCH 091/107] UPD. Refactoring and changes according to comments. Signed-off-by: Oleksandra Matviienko --- .../server/dao/service/ValidatorTest.java | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java index 7a6fa231b2..9bfb0b6c8f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -42,7 +42,7 @@ class ValidatorTest { Validator.validateEntityId(TenantId.SYS_TENANT_ID, id -> "Incorrect entityId " + id); Validator.validateEntityId((goodDeviceId), id -> "Incorrect entityId " + id); - assertThatThrownBy(() -> Validator.validateEntityId( null, id -> "Incorrect entityId " + id)) + assertThatThrownBy(() -> Validator.validateEntityId(null, id -> "Incorrect entityId " + id)) .as("EntityId is null") .isInstanceOf(IncorrectParameterException.class) .hasMessageContaining("Incorrect entityId null"); @@ -107,7 +107,7 @@ class ValidatorTest { @Test void validateIdsTest() { List list = List.of(goodDeviceId); - Validator.validateIds( list, ids -> "Incorrect Id " + ids); + Validator.validateIds(list, ids -> "Incorrect Id " + ids); assertThatThrownBy(() -> Validator.validateIds(null, id -> "Incorrect Ids " + id)) .as("Ids are null") @@ -132,20 +132,4 @@ class ValidatorTest { .hasMessageContaining("null"); } - - @Test - void validateIdSupplier() { - Validator.validateId(TenantId.SYS_TENANT_ID, () -> "Incorrect Id null"); - Validator.validateId((goodDeviceId), () -> "Incorrect Id null"); - - assertThatThrownBy(() -> Validator.validateId((UUIDBased) null, () -> "Incorrect Id null")) - .as("Id is null") - .isInstanceOf(IncorrectParameterException.class) - .hasMessageContaining("Incorrect Id null"); - - assertThatThrownBy(() -> Validator.validateId(nullUserId, () -> "Incorrect Id null")) - .as("Id with null UUIDBased") - .isInstanceOf(IncorrectParameterException.class) - .hasMessageContaining("Incorrect Id null"); - } } From 816f6e35c5e3f931cc5a077118d1850823132169 Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Tue, 19 Mar 2024 12:13:49 +0100 Subject: [PATCH 092/107] Changed the usage of validate methods in Validator from methods with an errorMessage to methods with functions. Signed-off-by: Oleksandra Matviienko --- .../resource/DefaultTbResourceService.java | 4 +- .../dao/asset/AssetProfileServiceImpl.java | 24 +++---- .../server/dao/attributes/AttributeUtils.java | 2 +- .../attributes/CachedAttributesService.java | 4 +- .../server/dao/audit/AuditLogServiceImpl.java | 14 ++-- .../dao/customer/CustomerServiceImpl.java | 14 ++-- .../dao/dashboard/DashboardServiceImpl.java | 32 ++++----- .../device/DeviceConnectivityServiceImpl.java | 2 +- .../device/DeviceCredentialsServiceImpl.java | 4 +- .../dao/device/DeviceProfileServiceImpl.java | 26 +++---- .../server/dao/device/DeviceServiceImpl.java | 68 +++++++++---------- .../server/dao/edge/EdgeServiceImpl.java | 64 ++++++++--------- .../dao/entityview/EntityViewServiceImpl.java | 64 ++++++++--------- .../OAuth2ConfigTemplateServiceImpl.java | 6 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 2 +- .../server/dao/service/Validator.java | 13 ---- .../settings/AdminSettingsServiceImpl.java | 6 +- .../dao/tenant/TenantProfileServiceImpl.java | 6 +- .../server/dao/tenant/TenantServiceImpl.java | 8 +-- .../usagerecord/ApiUsageStateServiceImpl.java | 12 ++-- .../server/dao/user/UserServiceImpl.java | 42 ++++++------ .../dao/user/UserSettingsServiceImpl.java | 8 +-- .../dao/widget/WidgetTypeServiceImpl.java | 40 +++++------ .../dao/widget/WidgetsBundleServiceImpl.java | 18 ++--- 24 files changed, 235 insertions(+), 248 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 2f5f2abd38..fbbace44bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -95,7 +95,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List findLwM2mObject(TenantId tenantId, String sortOrder, String sortProperty, String[] objectIds) { log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, t -> INCORRECT_TENANT_ID + t); List resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL, objectIds); return resources.stream() @@ -107,7 +107,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) { log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, t -> INCORRECT_TENANT_ID + t); PageData resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink); return resourcePageData.getData().stream() .flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false))) diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index 5f298d5e76..fb5b799ae7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -103,7 +103,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_ID + id); return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId), () -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache); } @@ -116,7 +116,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_NAME + s); return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName), () -> assetProfileDao.findByName(tenantId, profileName), false, putInCache); } @@ -124,7 +124,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_ID + id); return toAssetProfileInfo(findAssetProfileById(tenantId, assetProfileId)); } @@ -179,7 +179,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_ASSET_PROFILE_ID + id); AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId()); if (assetProfile != null && assetProfile.isDefault()) { throw new DataValidationException("Deletion of Default Asset Profile is prohibited!"); @@ -208,7 +208,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService findAssetProfiles(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return assetProfileDao.findAssetProfiles(tenantId, pageLink); } @@ -216,7 +216,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService findAssetProfileInfos(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return assetProfileDao.findAssetProfileInfos(tenantId, pageLink); } @@ -246,7 +246,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); AssetProfile assetProfile = new AssetProfile(); assetProfile.setTenantId(tenantId); assetProfile.setDefault(defaultProfile); @@ -258,7 +258,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(AssetProfileCacheKey.defaultProfile(tenantId), () -> assetProfileDao.findDefaultAssetProfile(tenantId), true); } @@ -266,14 +266,14 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return toAssetProfileInfo(findDefaultAssetProfile(tenantId)); } @Override public boolean setDefaultAssetProfile(TenantId tenantId, AssetProfileId assetProfileId) { log.trace("Executing setDefaultAssetProfile [{}]", assetProfileId); - Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); + Validator.validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id); AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId()); if (!assetProfile.isDefault()) { assetProfile.setDefault(true); @@ -299,7 +299,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantAssetProfilesRemover.removeEntities(tenantId, tenantId); } @@ -322,7 +322,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService findAssetProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) { log.trace("Executing findAssetProfileNamesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return assetProfileDao.findTenantAssetProfileNames(tenantId.getId(), activeOnly) .stream().sorted(Comparator.comparing(EntityInfo::getName)) .collect(Collectors.toList()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java index 65e52e06d2..3fa6f7121d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java @@ -29,7 +29,7 @@ public class AttributeUtils { @Deprecated(since = "3.7.0") public static void validate(EntityId id, String scope) { Validator.validateId(id.getId(), "Incorrect id " + id); - Validator.validateString(scope, "Incorrect scope " + scope); + Validator.validateString(scope, sc -> "Incorrect scope " + sc); } public static void validate(EntityId id, AttributeScope scope) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 1aafdf9583..20e9c8b619 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -117,7 +117,7 @@ public class CachedAttributesService implements AttributesService { @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + Validator.validateString(attributeKey, k ->"Incorrect attribute key " + k); return cacheExecutor.submit(() -> { AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey); @@ -152,7 +152,7 @@ public class CachedAttributesService implements AttributesService { public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, final Collection attributeKeysNonUnique) { validate(entityId, scope); final var attributeKeys = new LinkedHashSet<>(attributeKeysNonUnique); // deduplicate the attributes - attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k ->"Incorrect attribute key " + k)); //CacheExecutor for Redis or DirectExecutor for local Caffeine return Futures.transformAsync(cacheExecutor.submit(() -> findCachedAttributes(entityId, scope, attributeKeys)), diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index ddaebef6e8..5fcdfdf20f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -85,31 +85,31 @@ public class AuditLogServiceImpl implements AuditLogService { @Override public PageData findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndCustomerId [{}], [{}], [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> "Incorrect customerId " + id); return auditLogDao.findAuditLogsByTenantIdAndCustomerId(tenantId.getId(), customerId, actionTypes, pageLink); } @Override public PageData findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndUserId [{}], [{}], [{}]", tenantId, userId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(userId, "Incorrect userId" + userId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(userId, id -> "Incorrect userId" + id); return auditLogDao.findAuditLogsByTenantIdAndUserId(tenantId.getId(), userId, actionTypes, pageLink); } @Override public PageData findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateEntityId(entityId, INCORRECT_TENANT_ID + entityId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateEntityId(entityId, id -> INCORRECT_TENANT_ID + id); return auditLogDao.findAuditLogsByTenantIdAndEntityId(tenantId.getId(), entityId, actionTypes, pageLink); } @Override public PageData findAuditLogsByTenantId(TenantId tenantId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogs [{}]", pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return auditLogDao.findAuditLogsByTenantId(tenantId.getId(), actionTypes, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 633333dcbd..04308cbb2e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -84,21 +84,21 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public Customer findCustomerById(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerById [{}]", customerId); - Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + Validator.validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); return customerDao.findById(tenantId, customerId.getId()); } @Override public Optional findCustomerByTenantIdAndTitle(TenantId tenantId, String title) { log.trace("Executing findCustomerByTenantIdAndTitle [{}] [{}]", tenantId, title); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), title); } @Override public ListenableFuture findCustomerByIdAsync(TenantId tenantId, CustomerId customerId) { log.trace("Executing findCustomerByIdAsync [{}]", customerId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); return customerDao.findByIdAsync(tenantId, customerId.getId()); } @@ -126,7 +126,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Transactional public void deleteCustomer(TenantId tenantId, CustomerId customerId) { log.trace("Executing deleteCustomer [{}]", customerId); - Validator.validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + Validator.validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); Customer customer = findCustomerById(tenantId, customerId); if (customer == null) { throw new IncorrectParameterException("Unable to delete non-existent customer."); @@ -147,7 +147,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public Customer findOrCreatePublicCustomer(TenantId tenantId) { log.trace("Executing findOrCreatePublicCustomer, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_CUSTOMER_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_CUSTOMER_ID + id); Optional publicCustomerOpt = customerDao.findCustomersByTenantIdAndTitle(tenantId.getId(), PUBLIC_CUSTOMER_TITLE); if (publicCustomerOpt.isPresent()) { return publicCustomerOpt.get(); @@ -169,7 +169,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public PageData findCustomersByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findCustomersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(tenantId, id -> "Incorrect tenantId " + id); Validator.validatePageLink(pageLink); return customerDao.findCustomersByTenantId(tenantId.getId(), pageLink); } @@ -177,7 +177,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @Override public void deleteCustomersByTenantId(TenantId tenantId) { log.trace("Executing deleteCustomersByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(tenantId, id -> "Incorrect tenantId " + id); customersByTenantRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 5d2e6658fa..7f90d82af7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -110,21 +110,21 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); - Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardDao.findById(tenantId, dashboardId.getId()); } @Override public ListenableFuture findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardByIdAsync [{}]", dashboardId); - validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardDao.findByIdAsync(tenantId, dashboardId.getId()); } @Override public DashboardInfo findDashboardInfoById(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardInfoById [{}]", dashboardId); - Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardInfoDao.findById(tenantId, dashboardId.getId()); } @@ -137,7 +137,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public ListenableFuture findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId); - validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); return dashboardInfoDao.findByIdAsync(tenantId, dashboardId.getId()); } @@ -229,7 +229,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Transactional public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) { log.trace("Executing deleteDashboard [{}]", dashboardId); - Validator.validateId(dashboardId, INCORRECT_DASHBOARD_ID + dashboardId); + Validator.validateId(dashboardId, id -> INCORRECT_DASHBOARD_ID + id); deleteEntityRelations(tenantId, dashboardId); try { dashboardDao.removeById(tenantId, dashboardId.getId()); @@ -249,7 +249,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findDashboardsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findDashboardsByTenantId(tenantId.getId(), pageLink); } @@ -257,7 +257,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findMobileDashboardsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findMobileDashboardsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findMobileDashboardsByTenantId(tenantId.getId(), pageLink); } @@ -265,15 +265,15 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public void deleteDashboardsByTenantId(TenantId tenantId) { log.trace("Executing deleteDashboardsByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantDashboardsRemover.removeEntities(tenantId, tenantId); } @Override public PageData findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -281,8 +281,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findMobileDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findMobileDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findMobileDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -290,7 +290,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) { log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Customer customer = customerDao.findById(tenantId, customerId.getId()); if (customer == null) { throw new DataValidationException("Can't unassign dashboards from non-existent customer!"); @@ -301,7 +301,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId) { log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId); - Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateId(customerId, id -> "Incorrect customerId " + id); Customer customer = customerDao.findById(tenantId, customerId.getId()); if (customer == null) { throw new DataValidationException("Can't update dashboards for non-existent customer!"); @@ -351,8 +351,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public PageData findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(edgeId, id -> INCORRECT_EDGE_ID + id); Validator.validatePageLink(pageLink); return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java index 4367163ded..67af118d0b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -88,7 +88,7 @@ public class DeviceConnectivityServiceImpl implements DeviceConnectivityService public JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException { DeviceId deviceId = device.getId(); log.trace("Executing findDevicePublishTelemetryCommands [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + validateId(deviceId, id -> INCORRECT_DEVICE_ID + id); DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 60167f04f2..6064908717 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -72,14 +72,14 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService "Incorrect deviceId " + id); return deviceCredentialsDao.findByDeviceId(tenantId, deviceId.getId()); } @Override public DeviceCredentials findDeviceCredentialsByCredentialsId(String credentialsId) { log.trace("Executing findDeviceCredentialsByCredentialsId [{}]", credentialsId); - validateString(credentialsId, "Incorrect credentialsId " + credentialsId); + validateString(credentialsId, id ->"Incorrect credentialsId " + id); return cache.getAndPutInTransaction(credentialsId, () -> deviceCredentialsDao.findByCredentialsId(TenantId.SYS_TENANT_ID, credentialsId), true); // caching null values is essential for permanently invalid requests diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 608da9d28d..7e6d79d9a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -126,7 +126,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_ID + id); return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId), () -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache); } @@ -139,7 +139,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_NAME + pn); return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName), () -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache); } @@ -147,7 +147,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_PROVISION_DEVICE_KEY + dk); return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromProvisionDeviceKey(provisionDeviceKey), () -> deviceProfileDao.findByProvisionDeviceKey(provisionDeviceKey), false); } @@ -155,7 +155,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_ID + id); return toDeviceProfileInfo(findDeviceProfileById(tenantId, deviceProfileId)); } @@ -222,7 +222,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_PROFILE_ID + id); DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId()); if (deviceProfile != null && deviceProfile.isDefault()) { throw new DataValidationException("Deletion of Default Device Profile is prohibited!"); @@ -252,7 +252,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService findDeviceProfiles(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDeviceProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return deviceProfileDao.findDeviceProfiles(tenantId, pageLink); } @@ -260,7 +260,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType) { log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType); } @@ -290,7 +290,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setTenantId(tenantId); deviceProfile.setDefault(defaultProfile); @@ -313,7 +313,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(DeviceProfileCacheKey.defaultProfile(tenantId), () -> deviceProfileDao.findDefaultDeviceProfile(tenantId), true); } @@ -321,14 +321,14 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return toDeviceProfileInfo(findDefaultDeviceProfile(tenantId)); } @Override public boolean setDefaultDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId) { log.trace("Executing setDefaultDeviceProfile [{}]", deviceProfileId); - validateId(deviceProfileId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); + validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); DeviceProfile deviceProfile = deviceProfileDao.findById(tenantId, deviceProfileId.getId()); if (!deviceProfile.isDefault()) { deviceProfile.setDefault(true); @@ -354,7 +354,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantDeviceProfilesRemover.removeEntities(tenantId, tenantId); } @@ -377,7 +377,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService findDeviceProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) { log.trace("Executing findDeviceProfileNamesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return deviceProfileDao.findTenantDeviceProfileNames(tenantId.getId(), activeOnly) .stream().sorted(Comparator.comparing(EntityInfo::getName)) .collect(Collectors.toList()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index da25bf91d5..33bf7f34a0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -126,14 +126,14 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_ID + id); return deviceDao.findDeviceInfoById(tenantId, deviceId.getId()); } @Override public Device findDeviceById(TenantId tenantId, DeviceId deviceId) { log.trace("Executing findDeviceById [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + validateId(deviceId, id -> INCORRECT_DEVICE_ID + id); if (TenantId.SYS_TENANT_ID.equals(tenantId)) { return cache.getAndPutInTransaction(new DeviceCacheKey(deviceId), () -> deviceDao.findById(tenantId, deviceId.getId()), true); @@ -146,7 +146,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) { log.trace("Executing findDeviceById [{}]", deviceId); - validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + validateId(deviceId, id -> INCORRECT_DEVICE_ID + id); if (TenantId.SYS_TENANT_ID.equals(tenantId)) { return deviceDao.findByIdAsync(tenantId, deviceId.getId()); } else { @@ -157,7 +157,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(new DeviceCacheKey(tenantId, name), () -> deviceDao.findDeviceByTenantIdAndName(tenantId.getId(), name).orElse(null), true); } @@ -321,7 +321,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_DEVICE_ID + id); if (entityViewService.existsByTenantIdAndEntityId(tenantId, deviceId)) { throw new DataValidationException("Can't delete device that has entity views!"); } @@ -347,7 +347,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findDevicesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id ->INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink); } @@ -359,7 +359,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return deviceDao.findDeviceInfosByFilter(filter, pageLink); @@ -375,8 +375,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -388,8 +388,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(tenantId, id -> INCORRECT_DEVICE_PROFILE_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type, pageLink); } @@ -397,30 +397,30 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(tenantId, id -> INCORRECT_DEVICE_PROFILE_ID + id); return deviceDao.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type); } @Override public ListenableFuture> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List deviceIds) { log.trace("Executing findDevicesByTenantIdAndIdsAsync, tenantId [{}], deviceIds [{}]", tenantId, deviceIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateIds(deviceIds, ids -> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(deviceIds)); } @Override public List findDevicesByIds(List deviceIds) { log.trace("Executing findDevicesByIdsAsync, deviceIds [{}]", deviceIds); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateIds(deviceIds, ids-> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByIds(toUUIDs(deviceIds)); } @Override public ListenableFuture> findDevicesByIdsAsync(List deviceIds) { log.trace("Executing findDevicesByIdsAsync, deviceIds [{}]", deviceIds); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateIds(deviceIds, ids-> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByIdsAsync(toUUIDs(deviceIds)); } @@ -428,15 +428,15 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantDevicesRemover.removeEntities(tenantId, tenantId); } @Override public PageData findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -444,9 +444,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -454,9 +454,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List deviceIds) { log.trace("Executing findDevicesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], deviceIds [{}]", tenantId, customerId, deviceIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateIds(deviceIds, "Incorrect deviceIds " + deviceIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateIds(deviceIds, ids -> "Incorrect deviceIds " + ids); return deviceDao.findDevicesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(deviceIds)); } @@ -464,8 +464,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerDevicesRemover.removeEntities(tenantId, customerId); } @@ -491,7 +491,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService> findDeviceTypesByTenantId(TenantId tenantId) { log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return deviceDao.findTenantDeviceTypesAsync(tenantId.getId()); } @@ -627,8 +627,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -636,9 +636,9 @@ public class DeviceServiceImpl extends AbstractCachedEntityService findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 2db4d2ba92..79411559db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -138,28 +138,28 @@ public class EdgeServiceImpl extends AbstractCachedEntityService INCORRECT_EDGE_ID + id); return edgeDao.findById(tenantId, edgeId.getId()); } @Override public EdgeInfo findEdgeInfoById(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeInfoById [{}]", edgeId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); return edgeDao.findEdgeInfoById(tenantId, edgeId.getId()); } @Override public ListenableFuture findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId) { log.trace("Executing findEdgeById [{}]", edgeId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); return edgeDao.findByIdAsync(tenantId, edgeId.getId()); } @Override public Edge findEdgeByTenantIdAndName(TenantId tenantId, String name) { log.trace("Executing findEdgeByTenantIdAndName [{}][{}]", tenantId, name); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(new EdgeCacheKey(tenantId, name), () -> edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name) .orElse(null), true); @@ -221,7 +221,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService INCORRECT_EDGE_ID + id); Edge edge = edgeDao.findById(tenantId, edgeId.getId()); @@ -236,7 +236,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantId(tenantId.getId(), pageLink); } @@ -244,8 +244,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -253,8 +253,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -262,7 +262,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantId(tenantId.getId(), pageLink); } @@ -270,23 +270,23 @@ public class EdgeServiceImpl extends AbstractCachedEntityService> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List edgeIds) { log.trace("Executing findEdgesByTenantIdAndIdsAsync, tenantId [{}], edgeIds [{}]", tenantId, edgeIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateIds(edgeIds, "Incorrect edgeIds " + edgeIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateIds(edgeIds, ids -> "Incorrect edgeIds " + ids); return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds)); } @Override public void deleteEdgesByTenantId(TenantId tenantId) { log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantEdgesRemover.removeEntities(tenantId, tenantId); } @Override public PageData findEdgesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -294,9 +294,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -304,8 +304,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -313,9 +313,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgeInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findEdgeInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return edgeDao.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -323,9 +323,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List edgeIds) { log.trace("Executing findEdgesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], edgeIds [{}]", tenantId, customerId, edgeIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateIds(edgeIds, "Incorrect edgeIds " + edgeIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateIds(edgeIds, ids -> "Incorrect edgeIds " + ids); return edgeDao.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(edgeIds)); } @@ -333,8 +333,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerEdgeUnassigner.removeEntities(tenantId, customerId); } @@ -370,7 +370,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService> findEdgeTypesByTenantId(TenantId tenantId) { log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); ListenableFuture> tenantEdgeTypes = edgeDao.findTenantEdgeTypesAsync(tenantId.getId()); return Futures.transform(tenantEdgeTypes, edgeTypes -> { @@ -400,7 +400,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndEntityId, tenantId [{}], entityId [{}], pageLink [{}]", tenantId, entityId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); + Validator.validateId(tenantId, id -> "Incorrect tenantId " + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink); } @@ -408,7 +408,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink) { log.trace("Executing findEdgesByTenantProfileId, tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink); - Validator.validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId); + Validator.validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantProfileId(tenantProfileId.getId(), pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 5804650ec6..65a3f47acb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -145,15 +145,15 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerEntityViewsUnAssigner.removeEntities(tenantId, customerId); } @Override public EntityViewInfo findEntityViewInfoById(TenantId tenantId, EntityViewId entityViewId) { log.trace("Executing findEntityViewInfoById [{}]", entityViewId); - validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); + validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id); return entityViewDao.findEntityViewInfoById(tenantId, entityViewId.getId()); } @@ -165,7 +165,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_ENTITY_VIEW_ID + id); return cache.getOrFetchFromDB(EntityViewCacheKey.byId(entityViewId), () -> entityViewDao.findById(tenantId, entityViewId.getId()) , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true, putInCache); @@ -174,7 +174,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(EntityViewCacheKey.byName(tenantId, name), () -> entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name).orElse(null) , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true); @@ -184,7 +184,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantId(tenantId.getId(), pageLink); } @@ -192,7 +192,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewInfosByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewInfosByTenantId(tenantId.getId(), pageLink); } @@ -200,18 +200,18 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewByTenantIdAndType(TenantId tenantId, PageLink pageLink, String type) { log.trace("Executing findEntityViewByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewsByTenantIdAndType(tenantId.getId(), type, pageLink); } @Override public PageData findEntityViewInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewInfosByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -220,8 +220,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityServiceINCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); @@ -231,8 +231,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + " pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); @@ -242,10 +242,10 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, PageLink pageLink, String type) { log.trace("Executing findEntityViewsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + " pageLink [{}], type [{}]", tenantId, customerId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -254,10 +254,10 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + " pageLink [{}], type [{}]", tenantId, customerId, pageLink, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); return entityViewDao.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -291,14 +291,14 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewByIdAsync(TenantId tenantId, EntityViewId entityViewId) { log.trace("Executing findEntityViewById [{}]", entityViewId); - validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); + validateId(entityViewId, id -> INCORRECT_ENTITY_VIEW_ID + id); return entityViewDao.findByIdAsync(tenantId, entityViewId.getId()); } @Override public ListenableFuture> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId) { log.trace("Executing findEntityViewsByTenantIdAndEntityIdAsync, tenantId [{}], entityId [{}]", tenantId, entityId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateId(entityId.getId(), "Incorrect entityId" + entityId); return service.submit(() -> cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId), @@ -309,7 +309,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId) { log.trace("Executing findEntityViewsByTenantIdAndEntityId, tenantId [{}], entityId [{}]", tenantId, entityId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateId(entityId.getId(), "Incorrect entityId" + entityId); return cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId), @@ -326,7 +326,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_ENTITY_VIEW_ID + id); deleteEntityRelations(tenantId, entityViewId); EntityView entityView = entityViewDao.findById(tenantId, entityViewId.getId()); entityViewDao.removeById(tenantId, entityViewId.getId()); @@ -337,14 +337,14 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantEntityViewRemover.removeEntities(tenantId, tenantId); } @Override public ListenableFuture> findEntityViewTypesByTenantId(TenantId tenantId) { log.trace("Executing findEntityViewTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); ListenableFuture> tenantEntityViewTypes = entityViewDao.findTenantEntityViewTypesAsync(tenantId.getId()); return Futures.transform(tenantEntityViewTypes, entityViewTypes -> { @@ -402,8 +402,8 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -411,9 +411,9 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java index ed456a93cf..8d0faaca92 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java @@ -64,14 +64,14 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple @Override public Optional findClientRegistrationTemplateByProviderId(String providerId) { log.trace("Executing findClientRegistrationTemplateByProviderId [{}]", providerId); - validateString(providerId, INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID + providerId); + validateString(providerId, id -> INCORRECT_CLIENT_REGISTRATION_PROVIDER_ID + id); return clientRegistrationTemplateDao.findByProviderId(providerId); } @Override public OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { log.trace("Executing findClientRegistrationTemplateById [{}]", templateId); - validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); + validateId(templateId, id -> INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + id); return clientRegistrationTemplateDao.findById(TenantId.SYS_TENANT_ID, templateId.getId()); } @@ -84,7 +84,7 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple @Override public void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { log.trace("Executing deleteClientRegistrationTemplateById [{}]", templateId); - validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); + validateId(templateId, id -> INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + id); clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index d28830165d..7e94355039 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -86,7 +86,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } catch (IllegalArgumentException e){ throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); } - validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); + validateString(domainName, dn -> INCORRECT_DOMAIN_NAME + dn); return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType( Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName, platformType) .stream() diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index ff11c3de81..3d671385e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -152,19 +152,6 @@ public class Validator { } } - /** - * This method validate UUIDBased id. If id is null than throw - * IncorrectParameterException exception - * - * @param id the id - * @param errorMessageSupplier the error message for exception supplier - */ - static void validateId(UUIDBased id, Supplier errorMessageSupplier) { - if (id == null || id.getId() == null) { - throw new IncorrectParameterException(errorMessageSupplier.get()); - } - } - /** * This method validate list of UUIDBased ids. If at least one of the ids is null than throw * IncorrectParameterException exception diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index bfc99d1b54..13aaba8751 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -39,14 +39,14 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { @Override public AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId) { log.trace("Executing findAdminSettingsById [{}]", adminSettingsId); - Validator.validateId(adminSettingsId, "Incorrect adminSettingsId " + adminSettingsId); + Validator.validateId(adminSettingsId, id -> "Incorrect adminSettingsId " + id); return adminSettingsDao.findById(tenantId, adminSettingsId.getId()); } @Override public AdminSettings findAdminSettingsByKey(TenantId tenantId, String key) { log.trace("Executing findAdminSettingsByKey [{}]", key); - Validator.validateString(key, "Incorrect key " + key); + Validator.validateString(key, k -> "Incorrect key " + k); return findAdminSettingsByTenantIdAndKey(TenantId.SYS_TENANT_ID, key); } @@ -82,7 +82,7 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { @Override public boolean deleteAdminSettingsByTenantIdAndKey(TenantId tenantId, String key) { log.trace("Executing deleteAdminSettings, tenantId [{}], key [{}]", tenantId, key); - Validator.validateString(key, "Incorrect key " + key); + Validator.validateString(key, k -> "Incorrect key " + k); return adminSettingsDao.removeByTenantIdAndKey(tenantId.getId(), key); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index ac7fb90024..9a4ab7fa1e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -74,7 +74,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_PROFILE_ID + id); return cache.getAndPutInTransaction(TenantProfileCacheKey.fromId(tenantProfileId), () -> tenantProfileDao.findById(tenantId, tenantProfileId.getId()), true); } @@ -111,7 +111,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_PROFILE_ID + id); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (tenantProfile != null && tenantProfile.isDefault()) { throw new DataValidationException("Deletion of Default Tenant Profile is prohibited!"); @@ -186,7 +186,7 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_PROFILE_ID + id); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (!tenantProfile.isDefault()) { tenantProfile.setDefault(true); 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 bb116cf035..18ae36cdc7 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 @@ -170,7 +170,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(tenantId, () -> tenantDao.findById(tenantId, tenantId.getId()), true); } @@ -178,14 +178,14 @@ public class TenantServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return tenantDao.findTenantInfoById(tenantId, tenantId.getId()); } @Override public ListenableFuture findTenantByIdAsync(TenantId callerId, TenantId tenantId) { log.trace("Executing findTenantByIdAsync [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return tenantDao.findByIdAsync(callerId, tenantId.getId()); } @@ -234,7 +234,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); entityViewService.deleteEntityViewsByTenantId(tenantId); widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); widgetTypeService.deleteWidgetTypesByTenantId(tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java index f4f24346d5..0f98fc945e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java @@ -72,7 +72,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public void deleteApiUsageStateByTenantId(TenantId tenantId) { log.trace("Executing deleteUsageRecordsByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); apiUsageStateDao.deleteApiUsageStateByTenantId(tenantId); } @@ -87,7 +87,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A public ApiUsageState createDefaultApiUsageState(TenantId tenantId, EntityId entityId) { entityId = Objects.requireNonNullElse(entityId, tenantId); log.trace("Executing createDefaultUsageRecord [{}]", entityId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); ApiUsageState apiUsageState = new ApiUsageState(); apiUsageState.setTenantId(tenantId); apiUsageState.setEntityId(entityId); @@ -142,7 +142,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public ApiUsageState update(ApiUsageState apiUsageState) { log.trace("Executing save [{}]", apiUsageState.getTenantId()); - validateId(apiUsageState.getTenantId(), INCORRECT_TENANT_ID + apiUsageState.getTenantId()); + validateId(apiUsageState.getTenantId(), id -> INCORRECT_TENANT_ID + id); validateId(apiUsageState.getId(), "Can't save new usage state. Only update is allowed!"); ApiUsageState savedState = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState); eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedState.getTenantId()).entityId(savedState.getId()) @@ -153,7 +153,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public ApiUsageState findTenantApiUsageState(TenantId tenantId) { log.trace("Executing findTenantUsageRecord, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return apiUsageStateDao.findTenantApiUsageState(tenantId.getId()); } @@ -166,8 +166,8 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id) { log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(id, "Incorrect apiUsageStateId " + id); + validateId(tenantId, t -> INCORRECT_TENANT_ID + t); + validateId(id, usId -> "Incorrect apiUsageStateId " + usId); return apiUsageStateDao.findById(tenantId, id.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index e989b02caa..b80c9be38f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -100,7 +100,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); - validateString(email, "Incorrect email " + email); + validateString(email, e -> "Incorrect email " + e); if (userLoginCaseSensitive) { return userDao.findByEmail(tenantId, email); } else { @@ -111,22 +111,22 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public User findUserByTenantIdAndEmail(TenantId tenantId, String email) { log.trace("Executing findUserByTenantIdAndEmail [{}][{}]", tenantId, email); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(email, "Incorrect email " + email); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateString(email, e -> "Incorrect email " + e); return userDao.findByTenantIdAndEmail(tenantId, email); } @Override public User findUserById(TenantId tenantId, UserId userId) { log.trace("Executing findUserById [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); return userDao.findById(tenantId, userId.getId()); } @Override public ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId) { log.trace("Executing findUserByIdAsync [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); return userDao.findByIdAsync(tenantId, userId.getId()); } @@ -159,21 +159,21 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId) { log.trace("Executing findUserCredentialsByUserId [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); return userCredentialsDao.findByUserId(tenantId, userId.getId()); } @Override public UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken) { log.trace("Executing findUserCredentialsByActivateToken [{}]", activateToken); - validateString(activateToken, "Incorrect activateToken " + activateToken); + validateString(activateToken, t -> "Incorrect activateToken " + t); return userCredentialsDao.findByActivateToken(tenantId, activateToken); } @Override public UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken) { log.trace("Executing findUserCredentialsByResetToken [{}]", resetToken); - validateString(resetToken, "Incorrect resetToken " + resetToken); + validateString(resetToken, t -> "Incorrect activateToken " + t); return userCredentialsDao.findByResetToken(tenantId, resetToken); } @@ -192,8 +192,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password) { log.trace("Executing activateUserCredentials activateToken [{}], password [{}]", activateToken, password); - validateString(activateToken, "Incorrect activateToken " + activateToken); - validateString(password, "Incorrect password " + password); + validateString(activateToken, t -> "Incorrect activateToken " + t); + validateString(password, p -> "Incorrect password " + p); UserCredentials userCredentials = userCredentialsDao.findByActivateToken(tenantId, activateToken); if (userCredentials == null) { throw new IncorrectParameterException(String.format("Unable to find user credentials by activateToken [%s]", activateToken)); @@ -259,7 +259,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic Objects.requireNonNull(user, "User is null"); UserId userId = user.getId(); log.trace("[{}] Executing deleteUser [{}]", tenantId, userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); userCredentialsDao.removeByUserId(tenantId, userId); userAuthSettingsDao.removeByUserId(userId); deleteEntityRelations(tenantId, userId); @@ -276,7 +276,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findUsersByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findUsersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return userDao.findByTenantId(tenantId.getId(), pageLink); } @@ -284,7 +284,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findTenantAdmins(TenantId tenantId, PageLink pageLink) { log.trace("Executing findTenantAdmins, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return userDao.findTenantAdmins(tenantId.getId(), pageLink); } @@ -317,15 +317,15 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public void deleteTenantAdmins(TenantId tenantId) { log.trace("Executing deleteTenantAdmins, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantAdminsRemover.removeEntities(tenantId, tenantId); } @Override public PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findCustomerUsers, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> "Incorrect customerId " + id); validatePageLink(pageLink); return userDao.findCustomerUsers(tenantId.getId(), customerId.getId(), pageLink); } @@ -333,24 +333,24 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public PageData findUsersByCustomerIds(TenantId tenantId, List customerIds, PageLink pageLink) { log.trace("Executing findTenantAndCustomerUsers, tenantId [{}], customerIds [{}], pageLink [{}]", tenantId, customerIds, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - customerIds.forEach(customerId -> {validateId(customerId, "Incorrect customerId " + customerId);}); + customerIds.forEach(customerId -> {validateId(customerId, id -> "Incorrect customerId " + id);}); return userDao.findUsersByCustomerIds(tenantId.getId(), customerIds, pageLink); } @Override public void deleteCustomerUsers(TenantId tenantId, CustomerId customerId) { log.trace("Executing deleteCustomerUsers, customerId [{}]", customerId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> "Incorrect customerId " + id); customerUsersRemover.removeEntities(tenantId, customerId); } @Override public void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled) { log.trace("Executing setUserCredentialsEnabled [{}], [{}]", userId, enabled); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); userCredentials.setEnabled(enabled); saveUserCredentials(tenantId, userCredentials); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java index adc1ce3a9a..9ed18eac38 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserSettingsServiceImpl.java @@ -48,14 +48,14 @@ public class UserSettingsServiceImpl extends AbstractCachedService INCORRECT_USER_ID + id); return doSaveUserSettings(tenantId, userSettings); } @Override public void updateUserSettings(TenantId tenantId, UserId userId, UserSettingsType type, JsonNode settings) { log.trace("Executing updateUserSettings for user [{}], [{}]", userId, settings); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); var key = new UserSettingsCompositeKey(userId.getId(), type.name()); UserSettings oldSettings = userSettingsDao.findById(tenantId, key); @@ -71,7 +71,7 @@ public class UserSettingsServiceImpl extends AbstractCachedService INCORRECT_USER_ID + id); var key = new UserSettingsCompositeKey(userId.getId(), type.name()); return cache.getAndPutInTransaction(key, @@ -81,7 +81,7 @@ public class UserSettingsServiceImpl extends AbstractCachedService jsonPaths) { log.trace("Executing deleteUserSettings for user [{}]", userId); - validateId(userId, INCORRECT_USER_ID + userId); + validateId(userId, id -> INCORRECT_USER_ID + id); var key = new UserSettingsCompositeKey(userId.getId(), type.name()); UserSettings userSettings = userSettingsDao.findById(tenantId, key); if (userSettings == null) { 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 7f7d4d5ae8..5321e4ba1c 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 @@ -71,21 +71,21 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public WidgetType findWidgetTypeById(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing findWidgetTypeById [{}]", widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); return widgetTypeDao.findWidgetTypeById(tenantId, widgetTypeId.getId()); } @Override public WidgetTypeDetails findWidgetTypeDetailsById(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing findWidgetTypeDetailsById [{}]", widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); 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); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); return widgetTypeDao.existsByTenantIdAndId(tenantId, widgetTypeId.getId()); } @@ -110,7 +110,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public void deleteWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing deleteWidgetType [{}]", widgetTypeId); - Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + Validator.validateId(widgetTypeId, id -> "Incorrect widgetTypeId " + id); widgetTypeDao.removeById(tenantId, widgetTypeId.getId()); eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(widgetTypeId).build()); } @@ -126,7 +126,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { public PageData findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { log.trace("Executing findAllTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetTypeDao.findAllTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @@ -135,7 +135,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { public PageData findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { log.trace("Executing findTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @@ -143,16 +143,16 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public List findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetTypesByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); 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); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); return widgetTypeDao.findWidgetTypesDetailsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @@ -162,8 +162,8 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", tenantId, widgetsBundleId, fullSearch, deprecatedFilter, widgetTypes, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); Validator.validatePageLink(pageLink); return widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @@ -171,27 +171,27 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public List findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); return widgetTypeDao.findWidgetFqnsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @Override public WidgetType findWidgetTypeByTenantIdAndFqn(TenantId tenantId, String fqn) { log.trace("Executing findWidgetTypeByTenantIdAndFqn, tenantId [{}], fqn [{}]", tenantId, fqn); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(fqn, "Incorrect fqn " + fqn); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateString(fqn, f -> "Incorrect fqn " + f); return widgetTypeDao.findByTenantIdAndFqn(tenantId.getId(), fqn); } @Override 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.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(widgetsBundleId, id -> INCORRECT_WIDGETS_BUNDLE_ID + id); Validator.checkNotNull(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds); if (!widgetTypeIds.isEmpty()) { - validateIds(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds); + validateIds(widgetTypeIds, ids -> "Incorrect widgetTypeIds " + ids); } List bundleWidgets = new ArrayList<>(); for (int index = 0; index < widgetTypeIds.size(); index++) { @@ -222,7 +222,7 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { @Override public void deleteWidgetTypesByTenantId(TenantId tenantId) { log.trace("Executing deleteWidgetTypesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantWidgetTypeRemover.removeEntities(tenantId, tenantId); } 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 9d6960a7d1..2e67406df1 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 @@ -66,7 +66,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public WidgetsBundle findWidgetsBundleById(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetsBundleById [{}]", widgetsBundleId); - Validator.validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); + Validator.validateId(widgetsBundleId, id -> "Incorrect widgetsBundleId " + id); return widgetsBundleDao.findById(tenantId, widgetsBundleId.getId()); } @@ -91,7 +91,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public void deleteWidgetsBundle(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing deleteWidgetsBundle [{}]", widgetsBundleId); - Validator.validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); + Validator.validateId(widgetsBundleId, id -> "Incorrect widgetsBundleId " + id); WidgetsBundle widgetsBundle = findWidgetsBundleById(tenantId, widgetsBundleId); if (widgetsBundle == null) { throw new IncorrectParameterException("Unable to delete non-existent widgets bundle."); @@ -103,8 +103,8 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public WidgetsBundle findWidgetsBundleByTenantIdAndAlias(TenantId tenantId, String alias) { log.trace("Executing findWidgetsBundleByTenantIdAndAlias, tenantId [{}], alias [{}]", tenantId, alias); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(alias, "Incorrect alias " + alias); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateString(alias, a -> "Incorrect alias " + a); return widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(tenantId.getId(), alias); } @@ -134,7 +134,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public PageData findTenantWidgetsBundlesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findTenantWidgetsBundlesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetsBundleDao.findTenantWidgetsBundlesByTenantId(tenantId.getId(), pageLink); } @@ -142,7 +142,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public PageData findAllTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { log.trace("Executing findAllTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], pageLink [{}]", tenantId, fullSearch, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId.getId(), fullSearch, pageLink); } @@ -150,7 +150,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public PageData findTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { log.trace("Executing findTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], pageLink [{}]", tenantId, fullSearch, pageLink); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); Validator.validatePageLink(pageLink); return widgetsBundleDao.findTenantWidgetsBundlesByTenantId(tenantId.getId(), fullSearch, pageLink); } @@ -158,7 +158,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public List findAllTenantWidgetsBundlesByTenantId(TenantId tenantId) { log.trace("Executing findAllTenantWidgetsBundlesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); List widgetsBundles = new ArrayList<>(); PageLink pageLink = new PageLink(DEFAULT_WIDGETS_BUNDLE_LIMIT); PageData pageData; @@ -175,7 +175,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Override public void deleteWidgetsBundlesByTenantId(TenantId tenantId) { log.trace("Executing deleteWidgetsBundlesByTenantId, tenantId [{}]", tenantId); - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantWidgetsBundleRemover.removeEntities(tenantId, tenantId); } From a993f2133a92c904b51606e8c9b1f54df7e826a7 Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Wed, 20 Mar 2024 10:21:57 +0100 Subject: [PATCH 093/107] Changes according to comments Signed-off-by: Oleksandra Matviienko --- .../server/service/resource/DefaultTbResourceService.java | 4 ++-- .../org/thingsboard/server/dao/audit/AuditLogServiceImpl.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index fbbace44bd..b42f57116c 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -95,7 +95,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List findLwM2mObject(TenantId tenantId, String sortOrder, String sortProperty, String[] objectIds) { log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, t -> INCORRECT_TENANT_ID + t); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); List resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL, objectIds); return resources.stream() @@ -107,7 +107,7 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public List findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) { log.trace("Executing findByTenantId [{}]", tenantId); - validateId(tenantId, t -> INCORRECT_TENANT_ID + t); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); PageData resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink); return resourcePageData.getData().stream() .flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false))) diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java index 5fcdfdf20f..753c89231b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java @@ -102,7 +102,7 @@ public class AuditLogServiceImpl implements AuditLogService { public PageData findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, List actionTypes, TimePageLink pageLink) { log.trace("Executing findAuditLogsByTenantIdAndEntityId [{}], [{}], [{}]", tenantId, entityId, pageLink); validateId(tenantId, id -> INCORRECT_TENANT_ID + id); - validateEntityId(entityId, id -> INCORRECT_TENANT_ID + id); + validateEntityId(entityId, id -> "Incorrect entityId" + id); return auditLogDao.findAuditLogsByTenantIdAndEntityId(tenantId.getId(), entityId, actionTypes, pageLink); } From c785c8acfdd071592cb63277b8ab021e1ad8cccf Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Thu, 28 Mar 2024 15:37:00 +0100 Subject: [PATCH 094/107] Minor changes of validation methods and code formatting. Signed-off-by: Oleksandra Matviienko --- .../dao/attributes/CachedAttributesService.java | 2 +- .../dao/device/DeviceCredentialsServiceImpl.java | 2 +- .../server/dao/device/DeviceServiceImpl.java | 4 ++-- .../server/dao/edge/EdgeServiceImpl.java | 2 +- .../thingsboard/server/dao/service/Validator.java | 6 +++--- .../dao/tenant/TenantProfileServiceImpl.java | 7 +++++-- .../dao/usagerecord/ApiUsageStateServiceImpl.java | 2 +- .../server/dao/user/UserServiceImpl.java | 4 ++-- .../server/dao/service/ValidatorTest.java | 14 +++----------- 9 files changed, 19 insertions(+), 24 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 20e9c8b619..dcd13fb168 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -117,7 +117,7 @@ public class CachedAttributesService implements AttributesService { @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, k ->"Incorrect attribute key " + k); + Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k); return cacheExecutor.submit(() -> { AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 6064908717..6025e7c820 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -79,7 +79,7 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService"Incorrect credentialsId " + id); + validateString(credentialsId, id -> "Incorrect credentialsId " + id); return cache.getAndPutInTransaction(credentialsId, () -> deviceCredentialsDao.findByCredentialsId(TenantId.SYS_TENANT_ID, credentialsId), true); // caching null values is essential for permanently invalid requests diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 33bf7f34a0..ff00db3564 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -389,7 +389,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); - validateId(tenantId, id -> INCORRECT_DEVICE_PROFILE_ID + id); + validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type, pageLink); } @@ -398,7 +398,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); - validateId(tenantId, id -> INCORRECT_DEVICE_PROFILE_ID + id); + validateId(deviceProfileId, id -> INCORRECT_DEVICE_PROFILE_ID + id); return deviceDao.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 79411559db..c1eb51be95 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -400,7 +400,7 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) { log.trace("Executing findEdgesByTenantIdAndEntityId, tenantId [{}], entityId [{}], pageLink [{}]", tenantId, entityId, pageLink); - Validator.validateId(tenantId, id -> "Incorrect tenantId " + id); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return edgeDao.findEdgesByTenantIdAndEntityId(tenantId.getId(), entityId.getId(), entityId.getEntityType(), pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index 3d671385e5..116d2beefa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -28,7 +28,6 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import java.util.List; import java.util.UUID; import java.util.function.Function; -import java.util.function.Supplier; import java.util.regex.Pattern; public class Validator { @@ -52,8 +51,8 @@ public class Validator { * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception * - * @param entityId the entityId - * @param errorMessageFunction the error message for exception that applies entityId + * @param entityId the entityId + * @param errorMessageFunction the error message for exception that applies entityId */ public static void validateEntityId(EntityId entityId, Function errorMessageFunction) { if (entityId == null || entityId.getId() == null) { @@ -231,4 +230,5 @@ public class Validator { throw new IncorrectParameterException(errorMessage); } } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 9a4ab7fa1e..7a6f5cd8f8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -51,6 +51,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; public class TenantProfileServiceImpl extends AbstractCachedEntityService implements TenantProfileService { private static final String INCORRECT_TENANT_PROFILE_ID = "Incorrect tenantProfileId "; + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @Autowired private TenantProfileDao tenantProfileDao; @@ -111,7 +112,8 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_PROFILE_ID + id); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (tenantProfile != null && tenantProfile.isDefault()) { throw new DataValidationException("Deletion of Default Tenant Profile is prohibited!"); @@ -186,7 +188,8 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService INCORRECT_TENANT_PROFILE_ID + id); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(tenantProfileId, id -> INCORRECT_TENANT_PROFILE_ID + id); TenantProfile tenantProfile = tenantProfileDao.findById(tenantId, tenantProfileId.getId()); if (!tenantProfile.isDefault()) { tenantProfile.setDefault(true); diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java index 0f98fc945e..9eb78764b5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java @@ -167,7 +167,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A public ApiUsageState findApiUsageStateById(TenantId tenantId, ApiUsageStateId id) { log.trace("Executing findApiUsageStateById, tenantId [{}], apiUsageStateId [{}]", tenantId, id); validateId(tenantId, t -> INCORRECT_TENANT_ID + t); - validateId(id, usId -> "Incorrect apiUsageStateId " + usId); + validateId(id, u -> "Incorrect apiUsageStateId " + u); return apiUsageStateDao.findById(tenantId, id.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index b80c9be38f..4b804353ad 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -173,7 +173,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override public UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken) { log.trace("Executing findUserCredentialsByResetToken [{}]", resetToken); - validateString(resetToken, t -> "Incorrect activateToken " + t); + validateString(resetToken, t -> "Incorrect resetToken " + t); return userCredentialsDao.findByResetToken(tenantId, resetToken); } @@ -335,7 +335,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic log.trace("Executing findTenantAndCustomerUsers, tenantId [{}], customerIds [{}], pageLink [{}]", tenantId, customerIds, pageLink); validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); - customerIds.forEach(customerId -> {validateId(customerId, id -> "Incorrect customerId " + id);}); + customerIds.forEach(customerId -> validateId(customerId, id -> "Incorrect customerId " + id)); return userDao.findUsersByCustomerIds(tenantId.getId(), customerIds, pageLink); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java index 9bfb0b6c8f..f0a4938d64 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.service; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -27,20 +26,15 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThatThrownBy; - class ValidatorTest { final DeviceId goodDeviceId = new DeviceId(UUID.fromString("18594c15-9f05-4cda-b58e-70172467c3e5")); final UserId nullUserId = new UserId(null); - @BeforeEach - void setUp() { - } - @Test void validateEntityIdTest() { Validator.validateEntityId(TenantId.SYS_TENANT_ID, id -> "Incorrect entityId " + id); - Validator.validateEntityId((goodDeviceId), id -> "Incorrect entityId " + id); + Validator.validateEntityId(goodDeviceId, id -> "Incorrect entityId " + id); assertThatThrownBy(() -> Validator.validateEntityId(null, id -> "Incorrect entityId " + id)) .as("EntityId is null") @@ -51,7 +45,6 @@ class ValidatorTest { .as("EntityId with null UUID") .isInstanceOf(IncorrectParameterException.class) .hasMessageContaining("Incorrect entityId null"); - } @Test @@ -84,13 +77,12 @@ class ValidatorTest { .as("Id is null") .isInstanceOf(IncorrectParameterException.class) .hasMessageContaining("Incorrect Id null"); - } @Test void validateUUIDBasedIdTest() { Validator.validateId(TenantId.SYS_TENANT_ID, id -> "Incorrect Id " + id); - Validator.validateId((goodDeviceId), id -> "Incorrect Id " + id); + Validator.validateId(goodDeviceId, id -> "Incorrect Id " + id); assertThatThrownBy(() -> Validator.validateId((UUIDBased) null, id -> "Incorrect Id " + id)) .as("Id is null") @@ -119,7 +111,7 @@ class ValidatorTest { .isInstanceOf(IncorrectParameterException.class) .hasMessageContaining("Incorrect Ids []"); - ArrayList badList = new ArrayList<>(2); + List badList = new ArrayList<>(2); badList.add(goodDeviceId); badList.add(null); From 1e83e458f6cb6702129d3d7b4909e40961af0f81 Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Fri, 29 Mar 2024 17:47:00 +0100 Subject: [PATCH 095/107] Changes in validateIds method. Validate method for id,ids added. Minor changes in code formatting. Signed-off-by: Oleksandra Matviienko --- .../server/dao/service/Validator.java | 60 ++++++++++++------- .../server/dao/service/ValidatorTest.java | 2 +- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index 116d2beefa..adbd3fbe19 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -38,8 +38,8 @@ public class Validator { * This method validate EntityId entity id. If entity id is invalid than throw * IncorrectParameterException exception * - * @param entityId the entityId - * @param errorMessage the error message for exception + * @param entityId the entityId + * @param errorMessage the error message for exception */ public static void validateEntityId(EntityId entityId, String errorMessage) { if (entityId == null || entityId.getId() == null) { @@ -52,7 +52,7 @@ public class Validator { * IncorrectParameterException exception * * @param entityId the entityId - * @param errorMessageFunction the error message for exception that applies entityId + * @param errorMessageFunction the error message function for exception that applies entityId */ public static void validateEntityId(EntityId entityId, Function errorMessageFunction) { if (entityId == null || entityId.getId() == null) { @@ -64,8 +64,8 @@ public class Validator { * This method validate String string. If string is invalid than throw * IncorrectParameterException exception * - * @param val the val - * @param errorMessage the error message for exception + * @param val the val + * @param errorMessage the error message for exception */ public static void validateString(String val, String errorMessage) { if (val == null || val.isEmpty()) { @@ -77,8 +77,8 @@ public class Validator { * This method validate String string. If string is invalid than throw * IncorrectParameterException exception * - * @param val the value - * @param errorMessageFunction the error message function that applies value + * @param val the value + * @param errorMessageFunction the error message function that applies value */ public static void validateString(String val, Function errorMessageFunction) { if (val == null || val.isEmpty()) { @@ -90,8 +90,8 @@ public class Validator { * This method validate long value. If value isn't positive than throw * IncorrectParameterException exception * - * @param val the val - * @param errorMessage the error message for exception + * @param val the val + * @param errorMessage the error message for exception */ public static void validatePositiveNumber(long val, String errorMessage) { if (val <= 0) { @@ -103,8 +103,8 @@ public class Validator { * This method validate UUID id. If id is null than throw * IncorrectParameterException exception * - * @param id the id - * @param errorMessage the error message for exception + * @param id the id + * @param errorMessage the error message for exception */ public static void validateId(UUID id, String errorMessage) { if (id == null) { @@ -116,8 +116,8 @@ public class Validator { * This method validate UUID id. If id is null than throw * IncorrectParameterException exception * - * @param id the id - * @param errorMessageFunction the error message function for exception that applies id + * @param id the id + * @param errorMessageFunction the error message function for exception that applies id */ public static void validateId(UUID id, Function errorMessageFunction) { if (id == null) { @@ -129,8 +129,8 @@ public class Validator { * This method validate UUIDBased id. If id is null than throw * IncorrectParameterException exception * - * @param id the id - * @param errorMessage the error message for exception + * @param id the id + * @param errorMessage the error message for exception */ public static void validateId(UUIDBased id, String errorMessage) { if (id == null || id.getId() == null) { @@ -142,8 +142,8 @@ public class Validator { * This method validate UUIDBased id. If id is null than throw * IncorrectParameterException exception * - * @param id the id - * @param errorMessageFunction the error message for exception that applies id + * @param id the id + * @param errorMessageFunction the error message function for exception that applies id */ public static void validateId(UUIDBased id, Function errorMessageFunction) { if (id == null || id.getId() == null) { @@ -151,12 +151,26 @@ public class Validator { } } + /** + * This method validate UUIDBased id. If id is null than throw + * IncorrectParameterException exception + * + * @param id the id + * @param ids the list of ids + * @param errorMessageFunction the error message function for exception that applies ids + */ + static void validateId(UUIDBased id, List ids, Function, String> errorMessageFunction) { + if (id == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(ids)); + } + } + /** * This method validate list of UUIDBased ids. If at least one of the ids is null than throw * IncorrectParameterException exception * - * @param ids the list of ids - * @param errorMessage the error message for exception + * @param ids the list of ids + * @param errorMessage the error message for exception */ public static void validateIds(List ids, String errorMessage) { if (ids == null || ids.isEmpty()) { @@ -172,15 +186,15 @@ public class Validator { * This method validate list of UUIDBased ids. If at least one of the ids is null than throw * IncorrectParameterException exception * - * @param ids the list of ids - * @param errorMessageFunction the error message for exception that applies ids + * @param ids the list of ids + * @param errorMessageFunction the error message function for exception that applies ids */ public static void validateIds(List ids, Function, String> errorMessageFunction) { if (ids == null || ids.isEmpty()) { throw new IncorrectParameterException(errorMessageFunction.apply(ids)); } else { for (UUIDBased id : ids) { - validateId(id, errorMessageFunction.apply(ids)); + validateId(id, ids, errorMessageFunction); } } } @@ -189,7 +203,7 @@ public class Validator { * This method validate PageLink page link. If pageLink is invalid than throw * IncorrectParameterException exception * - * @param pageLink the page link + * @param pageLink the page link */ public static void validatePageLink(PageLink pageLink) { if (pageLink == null) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java index f0a4938d64..14833627ba 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -111,7 +111,7 @@ class ValidatorTest { .isInstanceOf(IncorrectParameterException.class) .hasMessageContaining("Incorrect Ids []"); - List badList = new ArrayList<>(2); + List badList = new ArrayList<>(2); badList.add(goodDeviceId); badList.add(null); From 10ab05963921e62b525ba4c58ad477604f780f6f Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 3 Apr 2024 15:40:50 +0300 Subject: [PATCH 096/107] Update ---bug-report.md --- .github/ISSUE_TEMPLATE/---bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md index 6d8889f98d..f5d093f424 100644 --- a/.github/ISSUE_TEMPLATE/---bug-report.md +++ b/.github/ISSUE_TEMPLATE/---bug-report.md @@ -3,7 +3,7 @@ name: "\U0001F41E Bug report" about: Create a report to help us improve title: "Your title here" labels: ['bug', 'unconfirmed'] -assignees: AndriichnekoDm +assignees: Ultrazombie --- From e1747b94b3a8d5d168fd35b3196d2dc351c1ab42 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 3 Apr 2024 15:41:06 +0300 Subject: [PATCH 097/107] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index c73f810cd8..1168adaf15 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,7 +3,7 @@ name: Feature request about: Suggest an idea for this project title: "Your title here" labels: ['feature'] -assignees: 'AndriichnekoDm' +assignees: 'Ultrazombie' --- From 79b5a07d5d59dc6bd8221724613ec469f1ce6c0e Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 3 Apr 2024 15:41:18 +0300 Subject: [PATCH 098/107] Update question.md --- .github/ISSUE_TEMPLATE/question.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 2f15690f68..dea6318ac5 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -3,7 +3,7 @@ name: Question about: Describe your questions in detail title: "Your title here" labels: ['question'] -assignees: 'AndriichnekoDm' +assignees: 'Ultrazombie' --- From f82d6b9db5ac13e5d571e914a9ed5fae4cb92697 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 3 Apr 2024 14:44:36 +0200 Subject: [PATCH 099/107] ValidatorTest fixed imports --- .../org/thingsboard/server/dao/service/ValidatorTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java index 14833627ba..4242cb69ae 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/ValidatorTest.java @@ -16,7 +16,10 @@ package org.thingsboard.server.dao.service; import org.junit.jupiter.api.Test; -import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.exception.IncorrectParameterException; import java.util.ArrayList; From 9cce187f56cd4afc17c495765e09072dfe49472a Mon Sep 17 00:00:00 2001 From: Andrii Landiak Date: Wed, 3 Apr 2024 16:01:25 +0300 Subject: [PATCH 100/107] Add V_3_7_0 to edge.proto --- common/edge-api/src/main/proto/edge.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 4ac1831601..58959e4d19 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -37,6 +37,7 @@ enum EdgeVersion { V_3_6_0 = 3; V_3_6_1 = 4; V_3_6_2 = 5; + V_3_7_0 = 6; } /** From 93adbd116ca2675a4ac51c7207ed47cbf8d96b86 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 3 Apr 2024 15:28:28 +0200 Subject: [PATCH 101/107] Validator: some old method usage replaced with a new ones with functions --- .../server/controller/BaseController.java | 2 +- .../server/dao/asset/BaseAssetService.java | 14 +++++++------- .../server/dao/attributes/AttributeUtils.java | 4 ++-- .../dao/attributes/BaseAttributesService.java | 8 ++++---- .../dao/entityview/EntityViewServiceImpl.java | 4 ++-- .../server/dao/oauth2/OAuth2ServiceImpl.java | 4 ++-- .../server/dao/rule/BaseRuleChainService.java | 2 +- .../dao/usagerecord/ApiUsageStateServiceImpl.java | 4 ++-- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 9c437821c6..990f8b3d4b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -541,7 +541,7 @@ public abstract class BaseController { if (entityId == null) { throw new ThingsboardException("Parameter entityId can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } - validateId(entityId.getId(), "Incorrect entityId " + entityId); + validateId(entityId.getId(), id -> "Incorrect entityId " + id); switch (entityId.getEntityType()) { case ALARM: checkAlarmId(new AlarmId(entityId.getId()), operation); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index e24ce86245..ce1e2ce53b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -239,7 +239,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -248,7 +248,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndType(tenantId.getId(), type, pageLink); } @@ -266,7 +266,7 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List assetIds) { log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateIds(assetIds, "Incorrect assetIds " + assetIds); + validateIds(assetIds, ids -> "Incorrect assetIds " + ids); return assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds)); } @@ -300,7 +300,7 @@ public class BaseAssetService extends AbstractCachedEntityService "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -310,7 +310,7 @@ public class BaseAssetService extends AbstractCachedEntityService "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); } @@ -330,7 +330,7 @@ public class BaseAssetService extends AbstractCachedEntityService "Incorrect assetIds " + ids); return assetDao.findAssetsByTenantIdAndCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(assetIds)); } @@ -430,7 +430,7 @@ public class BaseAssetService extends AbstractCachedEntityService "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java index 3fa6f7121d..7e705c2278 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java @@ -28,12 +28,12 @@ public class AttributeUtils { @Deprecated(since = "3.7.0") public static void validate(EntityId id, String scope) { - Validator.validateId(id.getId(), "Incorrect id " + id); + Validator.validateId(id.getId(), uuid -> "Incorrect id " + uuid); Validator.validateString(scope, sc -> "Incorrect scope " + sc); } public static void validate(EntityId id, AttributeScope scope) { - Validator.validateId(id.getId(), "Incorrect id " + id); + Validator.validateId(id.getId(), uuid -> "Incorrect id " + uuid); Validator.checkNotNull(scope, "Incorrect scope " + scope); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 6fb63613dc..5869ad9261 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -58,28 +58,28 @@ public class BaseAttributesService implements AttributesService { @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKey)); } @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, String attributeKey) { validate(entityId, scope); - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKey)); } @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, String scope, Collection attributeKeys) { validate(entityId, scope); - attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k)); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, AttributeScope.valueOf(scope), attributeKeys)); } @Override public ListenableFuture> find(TenantId tenantId, EntityId entityId, AttributeScope scope, Collection attributeKeys) { validate(entityId, scope); - attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, k -> "Incorrect attribute key " + k)); return Futures.immediateFuture(attributesDao.find(tenantId, entityId, scope, attributeKeys)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 65a3f47acb..9d3c33dcc2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -299,7 +299,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService> findEntityViewsByTenantIdAndEntityIdAsync(TenantId tenantId, EntityId entityId) { log.trace("Executing findEntityViewsByTenantIdAndEntityIdAsync, tenantId [{}], entityId [{}]", tenantId, entityId); validateId(tenantId, id -> INCORRECT_TENANT_ID + id); - validateId(entityId.getId(), "Incorrect entityId" + entityId); + validateId(entityId.getId(), id -> "Incorrect entityId" + id); return service.submit(() -> cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId), () -> entityViewDao.findEntityViewsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()), @@ -310,7 +310,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService findEntityViewsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId) { log.trace("Executing findEntityViewsByTenantIdAndEntityId, tenantId [{}], entityId [{}]", tenantId, entityId); validateId(tenantId, id -> INCORRECT_TENANT_ID + id); - validateId(entityId.getId(), "Incorrect entityId" + entityId); + validateId(entityId.getId(), id -> "Incorrect entityId" + id); return cache.getAndPutInTransaction(EntityViewCacheKey.byEntityId(tenantId, entityId), () -> entityViewDao.findEntityViewsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()), diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 7e94355039..6535b54a2c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -142,14 +142,14 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @Override public OAuth2Registration findRegistration(UUID id) { log.trace("Executing findRegistration [{}]", id); - validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); return oauth2RegistrationDao.findById(null, id); } @Override public String findAppSecret(UUID id, String pkgName) { log.trace("Executing findAppSecret [{}][{}]", id, pkgName); - validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); validateString(pkgName, "Incorrect package name"); return oauth2RegistrationDao.findAppSecret(id, pkgName); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 2857436fc8..3a90b5b95a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -755,7 +755,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public List findAllRuleNodesByIds(List ruleNodeIds) { log.trace("Executing findAllRuleNodesByIds, ruleNodeIds {}", ruleNodeIds); - validateIds(ruleNodeIds, "Incorrect ruleNodeIds " + ruleNodeIds); + validateIds(ruleNodeIds, ids -> "Incorrect ruleNodeIds " + ids); assert ruleNodeIds.size() <= 1024; return ruleNodeDao.findAllRuleNodeByIds(ruleNodeIds); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java index 9eb78764b5..52195cf8db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java @@ -79,7 +79,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public void deleteApiUsageStateByEntityId(EntityId entityId) { log.trace("Executing deleteApiUsageStateByEntityId [{}]", entityId); - validateId(entityId.getId(), "Invalid entity id"); + validateId(entityId.getId(), id -> "Invalid entity id " + id); apiUsageStateDao.deleteApiUsageStateByEntityId(entityId); } @@ -159,7 +159,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A @Override public ApiUsageState findApiUsageStateByEntityId(EntityId entityId) { - validateId(entityId.getId(), "Invalid entity id"); + validateId(entityId.getId(), id -> "Invalid entity id " + id); return apiUsageStateDao.findApiUsageStateByEntityId(entityId); } From 6ca90a80b2e9dd5747f949631c5520eb5cd5821c Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 3 Apr 2024 15:30:36 +0200 Subject: [PATCH 102/107] Deprecated old methods in Validator in favour the fastest ones --- .../java/org/thingsboard/server/dao/service/Validator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index adbd3fbe19..dab28e298b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -41,6 +41,7 @@ public class Validator { * @param entityId the entityId * @param errorMessage the error message for exception */ + @Deprecated public static void validateEntityId(EntityId entityId, String errorMessage) { if (entityId == null || entityId.getId() == null) { throw new IncorrectParameterException(errorMessage); @@ -106,6 +107,7 @@ public class Validator { * @param id the id * @param errorMessage the error message for exception */ + @Deprecated public static void validateId(UUID id, String errorMessage) { if (id == null) { throw new IncorrectParameterException(errorMessage); @@ -132,6 +134,7 @@ public class Validator { * @param id the id * @param errorMessage the error message for exception */ + @Deprecated public static void validateId(UUIDBased id, String errorMessage) { if (id == null || id.getId() == null) { throw new IncorrectParameterException(errorMessage); @@ -172,6 +175,7 @@ public class Validator { * @param ids the list of ids * @param errorMessage the error message for exception */ + @Deprecated public static void validateIds(List ids, String errorMessage) { if (ids == null || ids.isEmpty()) { throw new IncorrectParameterException(errorMessage); From e00b14bf846353d9e57fd68b9cfa4fa4d349e1f7 Mon Sep 17 00:00:00 2001 From: Oleksandra Matviienko Date: Wed, 3 Apr 2024 21:11:05 +0200 Subject: [PATCH 103/107] validateId refactoring Signed-off-by: Oleksandra Matviienko --- .../server/controller/BaseController.java | 6 +- .../dao/alarm/BaseAlarmCommentService.java | 4 +- .../server/dao/alarm/BaseAlarmService.java | 14 ++-- .../server/dao/asset/BaseAssetService.java | 66 +++++++++---------- .../server/dao/entity/BaseEntityService.java | 8 +-- .../server/dao/ota/BaseOtaPackageService.java | 14 ++-- .../dao/queue/BaseQueueStatsService.java | 8 +-- .../dao/relation/BaseRelationService.java | 2 +- .../server/dao/resource/BaseImageService.java | 2 +- .../dao/resource/BaseResourceService.java | 20 +++--- .../server/dao/rpc/BaseRpcService.java | 18 ++--- .../server/dao/rule/BaseRuleChainService.java | 10 +-- 12 files changed, 86 insertions(+), 86 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 990f8b3d4b..0942329f40 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -506,7 +506,7 @@ public abstract class BaseController { TenantProfile checkTenantProfileId(TenantProfileId tenantProfileId, Operation operation) throws ThingsboardException { try { - validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId); + validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id); TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(getTenantId(), tenantProfileId); checkNotNull(tenantProfile, "Tenant profile with id [" + tenantProfileId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, operation); @@ -668,7 +668,7 @@ public abstract class BaseController { AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId, AlarmId alarmId) throws ThingsboardException { try { - validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); + validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id); AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get(); checkNotNull(alarmComment, "Alarm comment with id [" + alarmCommentId + "] is not found"); if (!alarmId.equals(alarmComment.getAlarmId())) { @@ -736,7 +736,7 @@ public abstract class BaseController { } protected RuleNode checkRuleNode(RuleNodeId ruleNodeId, Operation operation) throws ThingsboardException { - validateId(ruleNodeId, "Incorrect ruleNodeId " + ruleNodeId); + validateId(ruleNodeId, id -> "Incorrect ruleNodeId " + id); RuleNode ruleNode = ruleChainService.findRuleNodeById(getTenantId(), ruleNodeId); checkNotNull(ruleNode, "Rule node with id [" + ruleNodeId + "] is not found"); checkRuleChain(ruleNode.getRuleChainId(), operation); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java index ac6f4955d8..2c111151b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java @@ -82,14 +82,14 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al @Override public ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, AlarmCommentId alarmCommentId) { log.trace("Executing findAlarmCommentByIdAsync by alarmCommentId [{}]", alarmCommentId); - validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); + validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id); return alarmCommentDao.findAlarmCommentByIdAsync(tenantId, alarmCommentId.getId()); } @Override public AlarmComment findAlarmCommentById(TenantId tenantId, AlarmCommentId alarmCommentId) { log.trace("Executing findAlarmCommentByIdAsync by alarmCommentId [{}]", alarmCommentId); - validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); + validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id); return alarmCommentDao.findById(tenantId, alarmCommentId.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 72e1b9c7bf..ddb6b21cc6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -169,7 +169,7 @@ public class BaseAlarmService extends AbstractCachedEntityService findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds) { - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateEntityDataPageLink(query.getPageLink()); return alarmDao.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds); } @@ -261,21 +261,21 @@ public class BaseAlarmService extends AbstractCachedEntityService "Incorrect alarmId " + id); return alarmDao.findAlarmById(tenantId, alarmId.getId()); } @Override public ListenableFuture findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmByIdAsync [{}]", alarmId); - validateId(alarmId, "Incorrect alarmId " + alarmId); + validateId(alarmId, id -> "Incorrect alarmId " + id); return alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()); } @Override public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); - validateId(alarmId, "Incorrect alarmId " + alarmId); + validateId(alarmId, id -> "Incorrect alarmId " + id); return alarmDao.findAlarmInfoById(tenantId, alarmId.getId()); } @@ -302,7 +302,7 @@ public class BaseAlarmService extends AbstractCachedEntityService findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) { log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId); - validateId(userId, "Incorrect userId " + userId); + validateId(userId, id -> "Incorrect userId " + id); return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink); } @@ -336,14 +336,14 @@ public class BaseAlarmService extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); return alarmDao.countAlarmsByQuery(tenantId, customerId, query); } @Override public PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); if (DEFAULT_ALARM_TYPES_PAGE_LINK.equals(pageLink)) { return cache.getAndPutInTransaction(tenantId, () -> alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink), false); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index ce1e2ce53b..f7342e7634 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -102,28 +102,28 @@ public class BaseAssetService extends AbstractCachedEntityService INCORRECT_ASSET_ID + id); return assetDao.findAssetInfoById(tenantId, assetId.getId()); } @Override public Asset findAssetById(TenantId tenantId, AssetId assetId) { log.trace("Executing findAssetById [{}]", assetId); - validateId(assetId, INCORRECT_ASSET_ID + assetId); + validateId(assetId, id -> INCORRECT_ASSET_ID + id); return assetDao.findById(tenantId, assetId.getId()); } @Override public ListenableFuture findAssetByIdAsync(TenantId tenantId, AssetId assetId) { log.trace("Executing findAssetById [{}]", assetId); - validateId(assetId, INCORRECT_ASSET_ID + assetId); + validateId(assetId, id -> INCORRECT_ASSET_ID + id); return assetDao.findByIdAsync(tenantId, assetId.getId()); } @Override public Asset findAssetByTenantIdAndName(TenantId tenantId, String name) { log.trace("Executing findAssetByTenantIdAndName [{}][{}]", tenantId, name); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return cache.getAndPutInTransaction(new AssetCacheKey(tenantId, name), () -> assetDao.findAssetsByTenantIdAndName(tenantId.getId(), name) .orElse(null), true); @@ -198,7 +198,7 @@ public class BaseAssetService extends AbstractCachedEntityService INCORRECT_ASSET_ID + id); if (entityViewService.existsByTenantIdAndEntityId(tenantId, assetId)) { throw new DataValidationException("Can't delete asset that has entity views!"); } @@ -222,7 +222,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return assetDao.findAssetsByTenantId(tenantId.getId(), pageLink); } @@ -230,7 +230,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantId(tenantId.getId(), pageLink); } @@ -238,7 +238,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink); @@ -247,7 +247,7 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndType(tenantId.getId(), type, pageLink); @@ -256,8 +256,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndAssetProfileId(TenantId tenantId, AssetProfileId assetProfileId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndAssetProfileId, tenantId [{}], assetProfileId [{}], pageLink [{}]", tenantId, assetProfileId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndAssetProfileId(tenantId.getId(), assetProfileId.getId(), pageLink); } @@ -265,7 +265,7 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List assetIds) { log.trace("Executing findAssetsByTenantIdAndIdsAsync, tenantId [{}], assetIds [{}]", tenantId, assetIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateIds(assetIds, ids -> "Incorrect assetIds " + ids); return assetDao.findAssetsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(assetIds)); } @@ -273,15 +273,15 @@ public class BaseAssetService extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantAssetsRemover.removeEntities(tenantId, tenantId); } @Override public PageData findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -289,8 +289,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); } @@ -298,8 +298,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); @@ -308,8 +308,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); @@ -318,9 +318,9 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(TenantId tenantId, CustomerId customerId, AssetProfileId assetProfileId, PageLink pageLink) { log.trace("Executing findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId, tenantId [{}], customerId [{}], assetProfileId [{}], pageLink [{}]", tenantId, customerId, assetProfileId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); - validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); + validateId(assetProfileId, id -> INCORRECT_ASSET_PROFILE_ID + id); validatePageLink(pageLink); return assetDao.findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(tenantId.getId(), customerId.getId(), assetProfileId.getId(), pageLink); } @@ -328,8 +328,8 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List assetIds) { log.trace("Executing findAssetsByTenantIdAndCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], assetIds [{}]", tenantId, customerId, assetIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateIds(assetIds, ids -> "Incorrect assetIds " + ids); return assetDao.findAssetsByTenantIdAndCustomerIdAndIdsAsync(tenantId.getId(), customerId.getId(), toUUIDs(assetIds)); } @@ -337,8 +337,8 @@ public class BaseAssetService extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); customerAssetsUnasigner.removeEntities(tenantId, customerId); } @@ -370,7 +370,7 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetTypesByTenantId(TenantId tenantId) { log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return assetDao.findTenantAssetTypesAsync(tenantId.getId()); } @@ -419,8 +419,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -428,8 +428,8 @@ public class BaseAssetService extends AbstractCachedEntityService findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}] pageLink [{}]", tenantId, edgeId, type, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(edgeId, INCORRECT_EDGE_ID + edgeId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(edgeId, id -> INCORRECT_EDGE_ID + id); validateString(type, t -> "Incorrect type " + t); validatePageLink(pageLink); return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index b91e2ed96d..8fb51d09ef 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -67,8 +67,8 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Override public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { log.trace("Executing countEntitiesByQuery, tenantId [{}], customerId [{}], query [{}]", tenantId, customerId, query); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateEntityCountQuery(query); return this.entityQueryDao.countEntitiesByQuery(tenantId, customerId, query); } @@ -76,8 +76,8 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe @Override public PageData findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { log.trace("Executing findEntityDataByQuery, tenantId [{}], customerId [{}], query [{}]", tenantId, customerId, query); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(customerId, id -> INCORRECT_CUSTOMER_ID + id); validateEntityDataQuery(query); return this.entityQueryDao.findEntityDataByQuery(tenantId, customerId, query); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index 53a585c358..b2fa1a3788 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -159,14 +159,14 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_OTA_PACKAGE_ID + id); return otaPackageDao.findById(tenantId, otaPackageId.getId()); } @Override public OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId) { log.trace("Executing findOtaPackageInfoById [{}]", otaPackageId); - validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + validateId(otaPackageId, id -> INCORRECT_OTA_PACKAGE_ID + id); return cache.getAndPutInTransaction(new OtaPackageCacheKey(otaPackageId), () -> otaPackageInfoDao.findById(tenantId, otaPackageId.getId()), true); } @@ -174,14 +174,14 @@ public class BaseOtaPackageService extends AbstractCachedEntityService findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId) { log.trace("Executing findOtaPackageInfoByIdAsync [{}]", otaPackageId); - validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + validateId(otaPackageId, id -> INCORRECT_OTA_PACKAGE_ID + id); return otaPackageInfoDao.findByIdAsync(tenantId, otaPackageId.getId()); } @Override public PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findTenantOtaPackagesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return otaPackageInfoDao.findOtaPackageInfoByTenantId(tenantId, pageLink); } @@ -189,7 +189,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink) { log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, pageLink); } @@ -197,7 +197,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_OTA_PACKAGE_ID + id); try { otaPackageDao.removeById(tenantId, otaPackageId.getId()); publishEvictEvent(new OtaPackageCacheEvictEvent(otaPackageId)); @@ -226,7 +226,7 @@ public class BaseOtaPackageService extends AbstractCachedEntityService INCORRECT_TENANT_ID + id); tenantOtaPackageRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java index ff2cabd2c4..2274a782bb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/queue/BaseQueueStatsService.java @@ -53,28 +53,28 @@ public class BaseQueueStatsService extends AbstractEntityService implements Queu @Override public QueueStats findQueueStatsById(TenantId tenantId, QueueStatsId queueStatsId) { log.trace("Executing findQueueStatsById [{}]", queueStatsId); - validateId(queueStatsId, "Incorrect queueStatsId " + queueStatsId); + validateId(queueStatsId, id -> "Incorrect queueStatsId " + id); return queueStatsDao.findById(tenantId, queueStatsId.getId()); } @Override public QueueStats findByTenantIdAndNameAndServiceId(TenantId tenantId, String queueName, String serviceId) { log.trace("Executing findByTenantIdAndNameAndServiceId, tenantId: [{}], queueName: [{}], serviceId: [{}]", tenantId, queueName, serviceId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return queueStatsDao.findByTenantIdQueueNameAndServiceId(tenantId, queueName, serviceId); } @Override public List findByTenantId(TenantId tenantId) { log.trace("Executing findByTenantId, tenantId: [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return queueStatsDao.findByTenantId(tenantId); } @Override public void deleteByTenantId(TenantId tenantId) { log.trace("Executing deleteByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); queueStatsDao.deleteByTenantId(tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java index 89cfff08d1..ec6af730ea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java @@ -513,7 +513,7 @@ public class BaseRelationService implements RelationService { @Override public List findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit) { log.trace("Executing findRuleNodeToRuleChainRelations, tenantId [{}], ruleChainType {} and limit {}", tenantId, ruleChainType, limit); - validateId(tenantId, "Invalid tenant id: " + tenantId); + validateId(tenantId, id -> "Invalid tenant id: " + id); return relationDao.findRuleNodeToRuleChainRelations(ruleChainType, limit); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index ef05229124..953037a864 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -256,7 +256,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic var tenantId = imageInfo.getTenantId(); var imageId = imageInfo.getId(); log.trace("Executing deleteImage [{}] [{}]", tenantId, imageId); - Validator.validateId(imageId, INCORRECT_RESOURCE_ID + imageId); + Validator.validateId(imageId, id -> INCORRECT_RESOURCE_ID + id); TbImageDeleteResult.TbImageDeleteResultBuilder result = TbImageDeleteResult.builder(); boolean success = true; if (!force) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index a5a692e3d4..37b030996f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -116,14 +116,14 @@ public class BaseResourceService extends AbstractCachedEntityService INCORRECT_RESOURCE_ID + id); return resourceDao.findById(tenantId, resourceId.getId()); } @Override public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) { log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); - Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + Validator.validateId(resourceId, id -> INCORRECT_RESOURCE_ID + id); return cache.getAndPutInTransaction(new ResourceInfoCacheKey(tenantId, resourceId), () -> resourceInfoDao.findById(tenantId, resourceId.getId()), true); @@ -138,7 +138,7 @@ public class BaseResourceService extends AbstractCachedEntityService findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId) { log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); - Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + Validator.validateId(resourceId, id -> INCORRECT_RESOURCE_ID + id); return resourceInfoDao.findByIdAsync(tenantId, resourceId.getId()); } @@ -150,7 +150,7 @@ public class BaseResourceService extends AbstractCachedEntityService INCORRECT_RESOURCE_ID + id); if (!force) { resourceValidator.validateDelete(tenantId, resourceId); } @@ -163,7 +163,7 @@ public class BaseResourceService extends AbstractCachedEntityService findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { TenantId tenantId = filter.getTenantId(); log.trace("Executing findAllTenantResourcesByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceInfoDao.findAllTenantResourcesByTenantId(filter, pageLink); } @@ -171,35 +171,35 @@ public class BaseResourceService extends AbstractCachedEntityService findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { TenantId tenantId = filter.getTenantId(); log.trace("Executing findTenantResourcesByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceInfoDao.findTenantResourcesByTenantId(filter, pageLink); } @Override public List findTenantResourcesByResourceTypeAndObjectIds(TenantId tenantId, ResourceType resourceType, String[] objectIds) { log.trace("Executing findTenantResourcesByResourceTypeAndObjectIds [{}][{}][{}]", tenantId, resourceType, objectIds); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceDao.findResourcesByTenantIdAndResourceType(tenantId, resourceType, objectIds, null); } @Override public PageData findAllTenantResources(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAllTenantResources [{}][{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceDao.findAllByTenantId(tenantId, pageLink); } @Override public PageData findTenantResourcesByResourceTypeAndPageLink(TenantId tenantId, ResourceType resourceType, PageLink pageLink) { log.trace("Executing findTenantResourcesByResourceTypeAndPageLink [{}][{}][{}]", tenantId, resourceType, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return resourceDao.findResourcesByTenantIdAndResourceType(tenantId, resourceType, pageLink); } @Override public void deleteResourcesByTenantId(TenantId tenantId) { log.trace("Executing deleteResourcesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantResourcesRemover.removeEntities(tenantId, tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java index 54d7a2170c..2b314e82df 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java @@ -54,38 +54,38 @@ public class BaseRpcService implements RpcService { @Override public void deleteRpc(TenantId tenantId, RpcId rpcId) { log.trace("Executing deleteRpc, tenantId [{}], rpcId [{}]", tenantId, rpcId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(rpcId, INCORRECT_RPC_ID + rpcId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(rpcId, id -> INCORRECT_RPC_ID + id); rpcDao.removeById(tenantId, rpcId.getId()); } @Override public void deleteAllRpcByTenantId(TenantId tenantId) { log.trace("Executing deleteAllRpcByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); tenantRpcRemover.removeEntities(tenantId, tenantId); } @Override public Rpc findById(TenantId tenantId, RpcId rpcId) { log.trace("Executing findById, tenantId [{}], rpcId [{}]", tenantId, rpcId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(rpcId, INCORRECT_RPC_ID + rpcId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(rpcId, id -> INCORRECT_RPC_ID + id); return rpcDao.findById(tenantId, rpcId.getId()); } @Override public ListenableFuture findRpcByIdAsync(TenantId tenantId, RpcId rpcId) { log.trace("Executing findRpcByIdAsync, tenantId [{}], rpcId: [{}]", tenantId, rpcId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(rpcId, INCORRECT_RPC_ID + rpcId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateId(rpcId, id -> INCORRECT_RPC_ID + id); return rpcDao.findByIdAsync(tenantId, rpcId.getId()); } @Override public PageData findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) { log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], rpcStatus [{}], pageLink [{}]", tenantId, deviceId, rpcStatus, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return rpcDao.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink); } @@ -93,7 +93,7 @@ public class BaseRpcService implements RpcService { @Override public PageData findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink) { log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], pageLink [{}]", tenantId, deviceId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validatePageLink(pageLink); return rpcDao.findAllByDeviceId(tenantId, deviceId, pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 3a90b5b95a..793adf7ac1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -645,8 +645,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public PageData findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { log.trace("Executing findRuleChainsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink); - Validator.validateId(tenantId, "Incorrect tenantId " + tenantId); - Validator.validateId(edgeId, "Incorrect edgeId " + edgeId); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(edgeId, id -> "Incorrect edgeId " + id); Validator.validatePageLink(pageLink); return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink); } @@ -705,14 +705,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public PageData findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAutoAssignToEdgeRuleChainsByTenantId, tenantId [{}], pageLink {}", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); return ruleChainDao.findAutoAssignToEdgeRuleChainsByTenantId(tenantId.getId(), pageLink); } @Override public List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) { log.trace("Executing findRuleNodes, tenantId [{}], type {}, search {}", tenantId, type, search); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateString(type, "Incorrect type of the rule node"); validateString(search, "Incorrect search text"); return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, search); @@ -721,7 +721,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC @Override public List findRuleNodesByTenantIdAndType(TenantId tenantId, String type) { log.trace("Executing findRuleNodes, tenantId [{}], type {}", tenantId, type); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); validateString(type, "Incorrect type of the rule node"); return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, ""); } From 866792fd86556f3b90bc08e7fdd5167533add841 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 3 Apr 2024 23:03:50 +0200 Subject: [PATCH 104/107] postgresql.driver.version 42.7.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c4b851bfbc..95931e8c18 100755 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 1.18.2 1.69 2.0.1 - 42.5.0 + 42.7.3 org/thingsboard/server/gen/**/*, org/thingsboard/server/extensions/core/plugin/telemetry/gen/**/* From 1a2dd016080b7dae2df6e11bc8ee6e0df6a341e3 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 3 Apr 2024 23:28:14 +0200 Subject: [PATCH 105/107] org.apache.zookeeper:zookeeper 3.9.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95931e8c18..7502f71f6f 100755 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 2.3.30 1.6.2 5.5.0 - 3.8.1 + 3.9.2 3.21.9 1.42.1 1.1.5 From 0dc1882685359fa961e96de5ae0ac8747d193b16 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 3 Apr 2024 23:28:57 +0200 Subject: [PATCH 106/107] "@google-cloud/pubsub": "^4.3.3" --> protobufjs "^7.2.4" --- msa/js-executor/package.json | 2 +- msa/js-executor/yarn.lock | 391 ++++++++++++++++++++++------------- 2 files changed, 250 insertions(+), 143 deletions(-) diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index c7edb2bbd7..01da4a356c 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -15,7 +15,7 @@ "dependencies": { "@aws-sdk/client-sqs": "^3.121.0", "@azure/service-bus": "^7.5.1", - "@google-cloud/pubsub": "^3.0.1", + "@google-cloud/pubsub": "^4.3.3", "amqplib": "^0.10.0", "config": "^3.3.7", "express": "^4.18.1", diff --git a/msa/js-executor/yarn.lock b/msa/js-executor/yarn.lock index eb6443e53d..42a32a93c4 100644 --- a/msa/js-executor/yarn.lock +++ b/msa/js-executor/yarn.lock @@ -797,68 +797,68 @@ enabled "2.0.x" kuler "^2.0.0" -"@google-cloud/paginator@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-4.0.0.tgz#9c3e01544717aecb9a922b4269ff298f30a0f1bb" - integrity sha512-wNmCZl+2G2DmgT/VlF+AROf80SoaC/CwS8trwmjNaq26VRNK8yPbU5F/Vy+R9oDAGKWQU2k8+Op5H4kFJVXFaQ== +"@google-cloud/paginator@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-5.0.0.tgz#b8cc62f151685095d11467402cbf417c41bf14e6" + integrity sha512-87aeg6QQcEPxGCOthnpUjvw4xAZ57G7pL8FS0C4e/81fr3FjkpUpibf1s2v5XGyGhUVGF4Jfg7yEcxqn2iUw1w== dependencies: arrify "^2.0.0" extend "^3.0.2" -"@google-cloud/precise-date@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-2.0.3.tgz#14f6f28ce35dabf3882e7aeab1c9d51bd473faed" - integrity sha512-+SDJ3ZvGkF7hzo6BGa8ZqeK3F6Z4+S+KviC9oOK+XCs3tfMyJCh/4j93XIWINgMMDIh9BgEvlw4306VxlXIlYA== +"@google-cloud/precise-date@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-4.0.0.tgz#e179893a3ad628b17a6fabdfcc9d468753aac11a" + integrity sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA== -"@google-cloud/projectify@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" - integrity sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ== +"@google-cloud/projectify@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-4.0.0.tgz#d600e0433daf51b88c1fa95ac7f02e38e80a07be" + integrity sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA== -"@google-cloud/promisify@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.2.tgz#81d654b4cb227c65c7ad2f9a7715262febd409ed" - integrity sha512-EvuabjzzZ9E2+OaYf+7P9OAiiwbTxKYL0oGLnREQd+Su2NTQBpomkdlkBowFvyWsaV0d1sSGxrKpSNcrhPqbxg== +"@google-cloud/promisify@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-4.0.0.tgz#a906e533ebdd0f754dca2509933334ce58b8c8b1" + integrity sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g== -"@google-cloud/pubsub@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-3.0.1.tgz#3a6bb9649a1b4309d82d8670a9c01375e622692f" - integrity sha512-dznNbRd/Y8J0C0xvdvCPi3B1msK/dj/Nya+NQZ2doUOLT6eoa261tBwk9umOQs5L5GKcdlqQKbBjrNjDYVbzQA== - dependencies: - "@google-cloud/paginator" "^4.0.0" - "@google-cloud/precise-date" "^2.0.0" - "@google-cloud/projectify" "^2.0.0" - "@google-cloud/promisify" "^2.0.0" - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/semantic-conventions" "^1.0.0" +"@google-cloud/pubsub@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-4.3.3.tgz#3d3f947ae8fca1694388592f22f77fdc3008ee8d" + integrity sha512-vJKh9L4dHf1XGSDKS1SB0IpqP/sUajQh4/QwhYasuq/NjzfHSxqSt+CuhrFGb5/gioTWE4gce0sn7h1SW7qESg== + dependencies: + "@google-cloud/paginator" "^5.0.0" + "@google-cloud/precise-date" "^4.0.0" + "@google-cloud/projectify" "^4.0.0" + "@google-cloud/promisify" "^4.0.0" + "@opentelemetry/api" "^1.6.0" + "@opentelemetry/semantic-conventions" "~1.21.0" "@types/duplexify" "^3.6.0" "@types/long" "^4.0.0" arrify "^2.0.0" extend "^3.0.2" - google-auth-library "^8.0.2" - google-gax "^3.0.1" + google-auth-library "^9.3.0" + google-gax "^4.3.1" + heap-js "^2.2.0" is-stream-ended "^0.1.4" lodash.snakecase "^4.1.1" p-defer "^3.0.0" -"@grpc/grpc-js@~1.6.0": - version "1.6.7" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.6.7.tgz#4c4fa998ff719fe859ac19fe977fdef097bb99aa" - integrity sha512-eBM03pu9hd3VqDQG+kHahiG1x80RGkkqqRb1Pchcwqej/KkAH95gAvKs6laqaHCycYaPK+TKuNQnOz9UXYA8qw== +"@grpc/grpc-js@~1.10.0": + version "1.10.6" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.10.6.tgz#1e3eb1af911dc888fbef7452f56a7573b8284d54" + integrity sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA== dependencies: - "@grpc/proto-loader" "^0.6.4" - "@types/node" ">=12.12.47" + "@grpc/proto-loader" "^0.7.10" + "@js-sdsl/ordered-map" "^4.4.2" -"@grpc/proto-loader@^0.6.12", "@grpc/proto-loader@^0.6.4": - version "0.6.13" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.13.tgz#008f989b72a40c60c96cd4088522f09b05ac66bc" - integrity sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g== +"@grpc/proto-loader@^0.7.0", "@grpc/proto-loader@^0.7.10": + version "0.7.12" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.12.tgz#787b58e3e3771df30b1567c057b6ab89e3a42911" + integrity sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q== dependencies: - "@types/long" "^4.0.1" lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^6.11.3" - yargs "^16.2.0" + long "^5.0.0" + protobufjs "^7.2.4" + yargs "^17.7.2" "@jridgewell/resolve-uri@^3.0.3": version "3.0.8" @@ -878,6 +878,11 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@js-sdsl/ordered-map@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" + integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -899,15 +904,20 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.0.1": +"@opentelemetry/api@^1.0.1": version "1.1.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.1.0.tgz#563539048255bbe1a5f4f586a4a10a1bb737f44a" integrity sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ== -"@opentelemetry/semantic-conventions@^1.0.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz#ba07b864a3c955f061aa30ea3ef7f4ae4449794a" - integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== +"@opentelemetry/api@^1.6.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec" + integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w== + +"@opentelemetry/semantic-conventions@~1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.21.0.tgz#83f7479c524ab523ac2df702ade30b9724476c72" + integrity sha512-lkC8kZYntxVKr7b8xmjCVUgE0a8xgDakPyDo9uSWavXPyYqLgYYGdEd2j8NxihRyb6UwpX3G/hFUF4/9q2V+/g== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -974,6 +984,11 @@ dependencies: defer-to-connect "^1.0.1" +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -1015,6 +1030,11 @@ "@types/connect" "*" "@types/node" "*" +"@types/caseless@*": + version "0.12.5" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.5.tgz#db9468cb1b1b5a925b8f34822f1669df0c5472f5" + integrity sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg== + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -1088,7 +1108,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.1.tgz#fdf6f6c6c73d3d8eee9c98a9a0485bc524b048d7" integrity sha512-HnYlg/BRF8uC1FyKRFZwRaCPTPYKa+6I8QiUZFLredaGOou481cgFS4wKRFyKvQtX8xudqkSdBczJHIYSQYKrQ== -"@types/node@>=12.12.47", "@types/node@>=13.7.0": +"@types/node@>=13.7.0": version "17.0.42" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.42.tgz#d7e8f22700efc94d125103075c074396b5f41f9b" integrity sha512-Q5BPGyGKcvQgAMbsr7qEGN/kIPN6zZecYYABeTDBizOsau+2NMdSVTar9UQw21A2+JyA2KRNDYaYrPB0Rpk2oQ== @@ -1108,6 +1128,16 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/request@^2.48.8": + version "2.48.12" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.12.tgz#0f590f615a10f87da18e9790ac94c29ec4c5ef30" + integrity sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw== + dependencies: + "@types/caseless" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + form-data "^2.5.0" + "@types/serve-static@*": version "1.13.10" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" @@ -1116,6 +1146,11 @@ "@types/mime" "^1" "@types/node" "*" +"@types/tough-cookie@*": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" + integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA== + "@types/tunnel@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.3.tgz#f109e730b072b3136347561fc558c9358bb8c6e9" @@ -1167,6 +1202,13 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + amqplib@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.10.0.tgz#766d696f8ceae097ee9eb73e6796999e5d40a1db" @@ -1462,6 +1504,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -1522,7 +1573,7 @@ colorspace@1.1.x: color "3.0.x" text-hex "1.0.x" -combined-stream@^1.0.8: +combined-stream@^1.0.6, combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -1609,7 +1660,7 @@ debug@2.6.9, debug@~2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.1: +debug@4: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -1623,6 +1674,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" @@ -1921,11 +1979,6 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" - integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== - fast-xml-parser@3.19.0: version "3.19.0" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" @@ -1982,6 +2035,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + form-data@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" @@ -2081,34 +2143,23 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaxios@^4.0.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.3.3.tgz#d44bdefe52d34b6435cc41214fdb160b64abfc22" - integrity sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA== +gaxios@^6.0.0, gaxios@^6.1.1: + version "6.4.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.4.0.tgz#08a42cb44d5123a72efaaf9f786c266e7f18be70" + integrity sha512-apAloYrY4dlBGlhauDAYSZveafb5U6+L9titing1wox6BvWM0TSXBp603zTrLpyLMGkrcFgohnUN150dFN/zOA== dependencies: - abort-controller "^3.0.0" extend "^3.0.2" - https-proxy-agent "^5.0.0" + https-proxy-agent "^7.0.1" is-stream "^2.0.0" - node-fetch "^2.6.7" - -gaxios@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-5.0.0.tgz#df11e5d0a45831dd39eb5fbbba0d6a6b09815e70" - integrity sha512-VD/yc5ln6XU8Ch1hyYY6kRMBE0Yc2np3fPyeJeYHhrPs1i8rgnsApPMWyrugkl7LLoSqpOJVBWlQIa87OAvt8Q== - dependencies: - abort-controller "^3.0.0" - extend "^3.0.2" - https-proxy-agent "^5.0.0" - is-stream "^2.0.0" - node-fetch "^2.6.7" + node-fetch "^2.6.9" + uuid "^9.0.1" -gcp-metadata@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-5.0.0.tgz#a00f999f60a4461401e7c515f8a3267cfb401ee7" - integrity sha512-gfwuX3yA3nNsHSWUL4KG90UulNiq922Ukj3wLTrcnX33BB7PwB1o0ubR8KVvXu9nJH+P5w1j2SQSNNqto+H0DA== +gcp-metadata@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.0.tgz#9b0dd2b2445258e7597f2024332d20611cbd6b8c" + integrity sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg== dependencies: - gaxios "^5.0.0" + gaxios "^6.0.0" json-bigint "^1.0.0" get-caller-file@^2.0.5: @@ -2178,46 +2229,35 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -google-auth-library@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-8.0.2.tgz#5fa0f2d3795c3e4019d2bb315ade4454cc9c30b5" - integrity sha512-HoG+nWFAThLovKpvcbYzxgn+nBJPTfAwtq0GxPN821nOO+21+8oP7MoEHfd1sbDulUFFGfcjJr2CnJ4YssHcyg== +google-auth-library@^9.3.0: + version "9.7.0" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.7.0.tgz#dd99a08e2e3f70778de8be4ed8556460e237550a" + integrity sha512-I/AvzBiUXDzLOy4iIZ2W+Zq33W4lcukQv1nl7C8WUA6SQwyQwUwu3waNmWNAvzds//FG8SZ+DnKnW/2k6mQS8A== dependencies: - arrify "^2.0.0" base64-js "^1.3.0" ecdsa-sig-formatter "^1.0.11" - fast-text-encoding "^1.0.0" - gaxios "^5.0.0" - gcp-metadata "^5.0.0" - gtoken "^5.3.2" + gaxios "^6.1.1" + gcp-metadata "^6.1.0" + gtoken "^7.0.0" jws "^4.0.0" - lru-cache "^6.0.0" -google-gax@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-3.1.0.tgz#a6fea06bfd0157969ddc0a6d04c47980978ead02" - integrity sha512-OHKVHZtaYHwxrjiI4BH+IzRWH2Q75sZg+q0/RatxLicbeeCCIug99+NKQiE39B5rLnGENtcbUjmXiqJHbSrkSg== +google-gax@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-4.3.2.tgz#417cbee97f2e68d78f641af19c0f15234c0dbd9c" + integrity sha512-2mw7qgei2LPdtGrmd1zvxQviOcduTnsvAWYzCxhOWXK4IQKmQztHnDQwD0ApB690fBQJemFKSU7DnceAy3RLzw== dependencies: - "@grpc/grpc-js" "~1.6.0" - "@grpc/proto-loader" "^0.6.12" + "@grpc/grpc-js" "~1.10.0" + "@grpc/proto-loader" "^0.7.0" "@types/long" "^4.0.0" abort-controller "^3.0.0" duplexify "^4.0.0" - fast-text-encoding "^1.0.3" - google-auth-library "^8.0.2" - is-stream-ended "^0.1.4" + google-auth-library "^9.3.0" node-fetch "^2.6.1" object-hash "^3.0.0" - proto3-json-serializer "^1.0.0" - protobufjs "6.11.3" - retry-request "^5.0.0" - -google-p12-pem@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.1.4.tgz#123f7b40da204de4ed1fbf2fd5be12c047fc8b3b" - integrity sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg== - dependencies: - node-forge "^1.3.1" + proto3-json-serializer "^2.0.0" + protobufjs "7.2.6" + retry-request "^7.0.0" + uuid "^9.0.1" got@^9.6.0: version "9.6.0" @@ -2241,13 +2281,12 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -gtoken@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" - integrity sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ== +gtoken@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26" + integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== dependencies: - gaxios "^4.0.0" - google-p12-pem "^3.1.3" + gaxios "^6.0.0" jws "^4.0.0" has-bigints@^1.0.1, has-bigints@^1.0.2: @@ -2301,6 +2340,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +heap-js@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/heap-js/-/heap-js-2.5.0.tgz#487e268b1733b187ca04eccf52f8387be92b46cb" + integrity sha512-kUGoI3p7u6B41z/dp33G6OaL7J4DRqRYwVmeIlwLClx7yaaAy7hoDExnuejTKtuDwfcatGmddHDEOjf6EyIxtQ== + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -2317,6 +2361,15 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -2325,6 +2378,14 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +https-proxy-agent@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2728,6 +2789,11 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +long@^5.0.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" + integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== + long@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/long/-/long-5.2.0.tgz#2696dadf4b4da2ce3f6f6b89186085d94d52fd61" @@ -2868,16 +2934,16 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + ms@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - multistream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/multistream/-/multistream-4.1.0.tgz#7bf00dfd119556fbc153cff3de4c6d477909f5a8" @@ -2903,18 +2969,13 @@ node-abi@^2.21.0: dependencies: semver "^5.4.1" -node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7: +node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7, node-fetch@^2.6.9: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -node-forge@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - nodemon@^2.0.16: version "2.0.16" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.16.tgz#d71b31bfdb226c25de34afea53486c8ef225fdef" @@ -3164,17 +3225,17 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== -proto3-json-serializer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-1.0.1.tgz#5d2b0b3c85568edb62984c94ce2e726985de44be" - integrity sha512-jtnGKL8EE1mTOl2qgMZJQXbS22dlb2K07i4b5vp8yMGMJ6mFSgBg4Ossu/g9NCuamdYXHjp3XxyWw4tJ0G/uKw== +proto3-json-serializer@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/proto3-json-serializer/-/proto3-json-serializer-2.0.1.tgz#da0b510f6d6e584b1b5c271f045c26728abe71e0" + integrity sha512-8awBvjO+FwkMd6gNoGFZyqkHZXCFd54CIYTb6De7dPaufGJ2XNW+QUNqbMr8MaAocMdb+KpsD4rxEOaTBDCffA== dependencies: - protobufjs "^6.11.3" + protobufjs "^7.2.5" -protobufjs@6.11.3, protobufjs@^6.11.3: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== +protobufjs@7.2.6, protobufjs@^7.2.4, protobufjs@^7.2.5: + version "7.2.6" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" + integrity sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw== dependencies: "@protobufjs/aspromise" "^1.1.2" "@protobufjs/base64" "^1.1.2" @@ -3186,9 +3247,8 @@ protobufjs@6.11.3, protobufjs@^6.11.3: "@protobufjs/path" "^1.1.2" "@protobufjs/pool" "^1.1.0" "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" "@types/node" ">=13.7.0" - long "^4.0.0" + long "^5.0.0" proxy-addr@~2.0.7: version "2.0.7" @@ -3363,13 +3423,14 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -retry-request@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-5.0.0.tgz#886ff8ec0e77fffbe66a4d5e90fd8f6646b6eae4" - integrity sha512-vBZdBxUordje9253imlmGtppC5gdcwZmNz7JnU2ui+KKFPk25keR+0c020AVV20oesYxIFOI0Kh3HE88/59ieg== +retry-request@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-7.0.2.tgz#60bf48cfb424ec01b03fca6665dee91d06dd95f3" + integrity sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w== dependencies: - debug "^4.1.1" + "@types/request" "^2.48.8" extend "^3.0.2" + teeny-request "^9.0.0" reusify@^1.0.4: version "1.0.4" @@ -3547,6 +3608,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +stream-events@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== + dependencies: + stubs "^3.0.0" + stream-meter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d" @@ -3594,7 +3662,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.2: +string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3680,6 +3748,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +stubs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== + supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -3720,6 +3793,17 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +teeny-request@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-9.0.0.tgz#18140de2eb6595771b1b02203312dfad79a4716d" + integrity sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g== + dependencies: + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + node-fetch "^2.6.9" + stream-events "^1.0.5" + uuid "^9.0.0" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -3973,6 +4057,11 @@ uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0, uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -4138,6 +4227,11 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -4151,6 +4245,19 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From 140f8dc4891ba08ae069dc25de940ce63723c6ab Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Mon, 8 Apr 2024 16:21:35 +0300 Subject: [PATCH 107/107] attribute scope is prepared correctly for rule engine message metadata --- .../server/service/action/EntityActionService.java | 9 +++++---- .../sync/ie/importing/csv/AbstractBulkImportService.java | 4 ++-- .../server/dao/audit/AuditLogServiceImpl.java | 9 +++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index db051ca0da..447c993e55 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; @@ -127,20 +128,20 @@ public class EntityActionService { } else { entityNode = JacksonUtil.newObjectNode(); if (actionType == ActionType.ATTRIBUTES_UPDATED) { - String scope = extractParameter(String.class, 0, additionalInfo); + AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo); @SuppressWarnings("unchecked") List attributes = extractParameter(List.class, 1, additionalInfo); - metaData.putValue(DataConstants.SCOPE, scope); + metaData.putValue(DataConstants.SCOPE, scope.name()); if (attributes != null) { for (AttributeKvEntry attr : attributes) { JacksonUtil.addKvEntry(entityNode, attr); } } } else if (actionType == ActionType.ATTRIBUTES_DELETED) { - String scope = extractParameter(String.class, 0, additionalInfo); + AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo); @SuppressWarnings("unchecked") List keys = extractParameter(List.class, 1, additionalInfo); - metaData.putValue(DataConstants.SCOPE, scope); + metaData.putValue(DataConstants.SCOPE, scope.name()); ArrayNode attrsArrayNode = entityNode.putArray("attributes"); if (keys != null) { keys.forEach(attrsArrayNode::add); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index d4c80f730a..909300a4b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -236,14 +236,14 @@ public abstract class AbstractBulkImportService attributes = extractParameter(List.class, 1, additionalInfo); - actionData.put("scope", scope); + actionData.put("scope", scope.name()); ObjectNode attrsNode = JacksonUtil.newObjectNode(); if (attributes != null) { for (AttributeKvEntry attr : attributes) { @@ -215,8 +216,8 @@ public class AuditLogServiceImpl implements AuditLogService { case ATTRIBUTES_DELETED: case ATTRIBUTES_READ: actionData.put("entityId", entityId.toString()); - scope = extractParameter(String.class, 0, additionalInfo); - actionData.put("scope", scope); + scope = extractParameter(AttributeScope.class, 0, additionalInfo); + actionData.put("scope", scope.name()); @SuppressWarnings("unchecked") List keys = extractParameter(List.class, 1, additionalInfo); ArrayNode attrsArrayNode = actionData.putArray("attributes");