From 2d22e8e8a8f3c5f0f1a89fe0ab6984d638939075 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 26 Jun 2023 23:39:31 +0200 Subject: [PATCH 1/4] implemented alarm types api --- .../server/controller/AlarmController.java | 12 +++++++ .../DefaultAlarmSubscriptionService.java | 7 ++++ .../controller/AlarmControllerTest.java | 28 ++++++++++++++++ .../server/dao/alarm/AlarmService.java | 4 +++ .../org/thingsboard/server/dao/DaoUtil.java | 14 ++++++++ .../server/dao/alarm/AlarmDao.java | 3 ++ .../server/dao/alarm/BaseAlarmService.java | 15 +++++++++ .../server/dao/sql/alarm/AlarmRepository.java | 3 ++ .../server/dao/sql/alarm/JpaAlarmDao.java | 8 +++++ .../server/dao/sql/asset/JpaAssetDao.java | 16 ++------- .../server/dao/sql/device/JpaDeviceDao.java | 17 ++-------- .../server/dao/sql/edge/JpaEdgeDao.java | 17 ++-------- .../dao/sql/entityview/JpaEntityViewDao.java | 17 ++-------- .../thingsboard/rest/client/RestClient.java | 9 +++++ .../engine/api/RuleEngineAlarmService.java | 4 +++ ui-ngx/src/app/core/http/alarm.service.ts | 5 +++ .../alarm/alarm-filter-config.component.html | 17 ++-------- .../alarm/alarm-filter-config.component.ts | 33 ++----------------- .../entity/entity-subtype-list.component.ts | 15 +++++++++ .../assets/locale/locale.constant-en_US.json | 6 +++- 20 files changed, 148 insertions(+), 102 deletions(-) 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 44263d19bd..9287ed515e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.google.common.util.concurrent.ListenableFuture; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; @@ -29,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -498,4 +500,14 @@ public class AlarmController extends BaseController { alarmStatus, assigneeId); } + @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.", produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) + @ResponseBody + public List getAlarmTypes() throws ThingsboardException, ExecutionException, InterruptedException { + ListenableFuture> alarmTypes = alarmService.findAlarmTypesByTenantId(getTenantId()); + return checkNotNull(alarmTypes.get()); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 1603d33bfd..dd52403bcd 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -25,6 +25,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ApiUsageRecordKey; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmComment; @@ -58,6 +59,7 @@ import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.Collection; +import java.util.List; /** * Created by ashvayka on 27.03.18. @@ -235,6 +237,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService }); } + @Override + public ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId) { + return alarmService.findAlarmTypesByTenantId(tenantId); + } + private void onAlarmUpdated(AlarmApiCallResult result) { wsCallBackExecutor.submit(() -> { AlarmInfo alarm = result.getAlarm(); diff --git a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java index 05030f0091..7e2e3d4d15 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -31,6 +31,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -42,8 +43,12 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -691,4 +696,27 @@ public class AlarmControllerTest extends AbstractControllerTest { doPost("/api/alarm", alarm).andExpect(status().isForbidden()); } + @Test + public void testGetAlarmTypes() throws Exception { + loginTenantAdmin(); + + Mockito.reset(tbClusterService, auditLogService); + + List types = new ArrayList<>(); + + for (int i = 1; i < 13; i++) { + types.add(createAlarm(TEST_ALARM_TYPE + i).getType()); + } + + List foundTypes = doGetTyped("/api/alarm/types", new TypeReference>() {}) + .stream() + .map(EntitySubtype::getType) + .collect(Collectors.toList()); + + Collections.sort(types); + Collections.sort(foundTypes); + + Assert.assertEquals(types, foundTypes); + } + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index fbf8892bd5..89feae53b8 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.Collection; +import java.util.List; public interface AlarmService extends EntityDaoService { @@ -121,4 +123,6 @@ public interface AlarmService extends EntityDaoService { void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); + + ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId); } 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 63b13bca66..4b407cb669 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,6 +18,9 @@ package org.thingsboard.server.dao; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -142,4 +145,15 @@ public abstract class DaoUtil { return null; } } + + public static List convertTenantEntityTypesToDto(UUID tenantId, EntityType entityType, List types) { + List list = Collections.emptyList(); + if (types != null && !types.isEmpty()) { + list = new ArrayList<>(); + for (String type : types) { + list.add(new EntitySubtype(TenantId.fromUUID(tenantId), entityType, type)); + } + } + return list; + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index 745861c6c8..df680bada0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -97,4 +98,6 @@ public interface AlarmDao extends Dao { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); + ListenableFuture> findTenantAlarmTypesAsync(UUID tenantId); + } 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 96342aca2f..f40be0378d 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 @@ -20,11 +20,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; @@ -64,6 +66,7 @@ import org.thingsboard.server.dao.tenant.TenantService; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -376,6 +379,18 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ return alarmDao.countAlarmsByQuery(tenantId, customerId, query); } + @Override + public ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId) { + log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + ListenableFuture> tenantAssetTypes = alarmDao.findTenantAlarmTypesAsync(tenantId.getId()); + return Futures.transform(tenantAssetTypes, + assetTypes -> { + assetTypes.sort(Comparator.comparing(EntitySubtype::getType)); + return assetTypes; + }, MoreExecutors.directExecutor()); + } + private Alarm merge(Alarm existing, Alarm alarm) { if (alarm.getStartTs() > existing.getEndTs()) { existing.setEndTs(alarm.getStartTs()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 92537dc850..81d9068f11 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -345,4 +345,7 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true) String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime); + + @Query("SELECT DISTINCT a.type FROM AlarmEntity a WHERE a.tenantId = :tenantId") + List findTenantAlarmTypes(@Param("tenantId") UUID tenantId); } 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 8dfdaf8a3e..d721996e0b 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 @@ -24,6 +24,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.alarm.Alarm; @@ -67,6 +68,8 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; + /** * Created by Valerii Sosliuk on 5/19/2017. */ @@ -366,6 +369,11 @@ public class JpaAlarmDao extends JpaAbstractDao implements A return alarmQueryRepository.countAlarmsByQuery(tenantId, customerId, query); } + @Override + public ListenableFuture> findTenantAlarmTypesAsync(UUID tenantId) { + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId))); + } + private static String getPropagationTypes(AlarmPropagationInfo ap) { String propagateRelationTypes; if (!CollectionUtils.isEmpty(ap.getPropagateRelationTypes())) { 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 ec5a8318f8..4c83815d19 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 @@ -37,14 +37,13 @@ import org.thingsboard.server.dao.model.sql.AssetInfoEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; /** @@ -194,7 +193,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A @Override public ListenableFuture> findTenantAssetTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantAssetTypesToDto(tenantId, assetRepository.findTenantAssetTypes(tenantId))); + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.ASSET, assetRepository.findTenantAssetTypes(tenantId))); } @Override @@ -212,17 +211,6 @@ public class JpaAssetDao extends JpaAbstractDao implements A DaoUtil.toPageable(pageLink))); } - private List convertTenantAssetTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.ASSET, type)); - } - } - return list; - } - @Override public PageData findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index d1ea342bd2..9b63e945ea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -43,13 +43,13 @@ import org.thingsboard.server.dao.model.sql.DeviceEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; + /** * Created by Valerii Sosliuk on 5/6/2017. */ @@ -217,7 +217,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement @Override public ListenableFuture> findTenantDeviceTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId))); + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.DEVICE, deviceRepository.findTenantDeviceTypes(tenantId))); } @Override @@ -240,17 +240,6 @@ public class JpaDeviceDao extends JpaAbstractDao implement return deviceRepository.countByTenantId(tenantId.getId()); } - private List convertTenantDeviceTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.DEVICE, type)); - } - } - return list; - } - @Override public PageData findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index c40ca2ca6d..29a9719b7d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -34,13 +34,13 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; + @Component @Slf4j @SqlDao @@ -144,7 +144,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge @Override public ListenableFuture> findTenantEdgeTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantEdgeTypesToDto(tenantId, edgeRepository.findTenantEdgeTypes(tenantId))); + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.EDGE, edgeRepository.findTenantEdgeTypes(tenantId))); } @Override @@ -184,17 +184,6 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge DaoUtil.toPageable(pageLink))); } - private List convertTenantEdgeTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.EDGE, type)); - } - } - return list; - } - @Override public EntityType getEntityType() { return EntityType.EDGE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index f3f82d34da..e3e9edf80d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -35,13 +35,13 @@ import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; + /** * Created by Victor Basanets on 8/31/2017. */ @@ -167,18 +167,7 @@ public class JpaEntityViewDao extends JpaAbstractDao> findTenantEntityViewTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantEntityViewTypesToDto(tenantId, entityViewRepository.findTenantEntityViewTypes(tenantId))); - } - - private List convertTenantEntityViewTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.ENTITY_VIEW, type)); - } - } - return list; + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.ENTITY_VIEW, entityViewRepository.findTenantEntityViewTypes(tenantId))); } @Override diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 51eb446d70..635d5357b2 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -499,6 +499,15 @@ public class RestClient implements Closeable { return restTemplate.postForEntity(baseURL + "/api/alarm", alarm, Alarm.class).getBody(); } + public List getAlarmTypes() { + return restTemplate.exchange( + baseURL + "/api/alarm/types", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + public AlarmComment saveAlarmComment(AlarmId alarmId, AlarmComment alarmComment) { return restTemplate.postForEntity(baseURL + "/api/alarm/{alarmId}/comment", alarmComment, AlarmComment.class, alarmId.getId()).getBody(); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index 05e9bf164f..ce069a92bb 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -18,6 +18,7 @@ package org.thingsboard.rule.engine.api; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.alarm.AlarmOperationResult; import java.util.Collection; +import java.util.List; /** * Created by ashvayka on 02.04.18. @@ -110,4 +112,6 @@ public interface RuleEngineAlarmService { AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus, String assigneeId); PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); + + ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId); } diff --git a/ui-ngx/src/app/core/http/alarm.service.ts b/ui-ngx/src/app/core/http/alarm.service.ts index e03e9ab783..f81a7b17e6 100644 --- a/ui-ngx/src/app/core/http/alarm.service.ts +++ b/ui-ngx/src/app/core/http/alarm.service.ts @@ -29,6 +29,7 @@ import { AlarmStatus } from '@shared/models/alarm.models'; import { UtilsService } from '@core/services/utils.service'; +import { EntitySubtype } from "@shared/models/entity-type.models"; @Injectable({ providedIn: 'root' @@ -108,4 +109,8 @@ export class AlarmService { defaultHttpOptionsFromConfig(config)); } + public getAlarmTypes(config?: RequestConfig): Observable> { + return this.http.get>('/api/alarm/types', defaultHttpOptionsFromConfig(config)); + } + } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html index a2a0c158a5..21c2688da6 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.html @@ -72,21 +72,8 @@
- - alarm.alarm-type-list - - - {{type}} - cancel - - - - + +
diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts index b46fbf506c..9594042e03 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-filter-config.component.ts @@ -42,6 +42,7 @@ import { import { MatChipInputEvent } from '@angular/material/chips'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { TranslateService } from '@ngx-translate/core'; +import { EntityType } from "@shared/models/entity-type.models"; export const ALARM_FILTER_CONFIG_DATA = new InjectionToken('AlarmFilterConfigData'); @@ -107,6 +108,8 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal panelResult: AlarmFilterConfig = null; + entityType = EntityType; + private alarmFilterConfig: AlarmFilterConfig; private propagateChange = (_: any) => {}; @@ -240,36 +243,6 @@ export class AlarmFilterConfigComponent implements OnInit, OnDestroy, ControlVal return this.alarmFilterConfigForm.get('typeList').value; } - public removeAlarmType(type: string): void { - const types: string[] = this.alarmFilterConfigForm.get('typeList').value; - const index = types.indexOf(type); - if (index >= 0) { - types.splice(index, 1); - this.alarmFilterConfigForm.get('typeList').setValue(types); - this.alarmFilterConfigForm.get('typeList').markAsDirty(); - } - } - - public addAlarmType(event: MatChipInputEvent): void { - const input = event.chipInput.inputElement; - const value = event.value; - - let types: string[] = this.alarmFilterConfigForm.get('typeList').value; - - if ((value || '').trim()) { - if (!types) { - types = []; - } - types.push(value.trim()); - this.alarmFilterConfigForm.get('typeList').setValue(types); - this.alarmFilterConfigForm.get('typeList').markAsDirty(); - } - - if (input) { - input.value = ''; - } - } - private updateAlarmConfigForm(alarmFilterConfig?: AlarmFilterConfig) { this.alarmFilterConfigForm.patchValue({ statusList: alarmFilterConfig?.statusList, diff --git a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts index 1442b44cfd..bc416010c9 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-subtype-list.component.ts @@ -31,6 +31,7 @@ import { EdgeService } from '@core/http/edge.service'; import { EntityViewService } from '@core/http/entity-view.service'; import { BroadcastService } from '@core/services/broadcast.service'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { AlarmService } from "@core/http/alarm.service"; @Component({ selector: 'tb-entity-subtype-list', @@ -102,6 +103,7 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, private deviceService: DeviceService, private edgeService: EdgeService, private entityViewService: EntityViewService, + private alarmService: AlarmService, private fb: UntypedFormBuilder) { this.entitySubtypeListFormGroup = this.fb.group({ entitySubtypeList: [this.entitySubtypeList, this.required ? [Validators.required] : []], @@ -164,6 +166,16 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, this.entitySubtypes = null; }); break; + case EntityType.ALARM: + this.placeholder = this.required ? this.translate.instant('alarm.enter-alarm-type') + : this.translate.instant('alarm.any-type'); + this.secondaryPlaceholder = '+' + this.translate.instant('alarm.alarm-type'); + this.noSubtypesMathingText = 'alarm.no-alarm-types-matching'; + this.subtypeListEmptyText = 'alarm.alarm-type-list-empty'; + this.broadcastSubscription = this.broadcast.on('alarmSaved', () => { + this.entitySubtypes = null; + }); + break; } this.filteredEntitySubtypeList = this.entitySubtypeListFormGroup.get('entitySubtype').valueChanges @@ -283,6 +295,9 @@ export class EntitySubTypeListComponent implements ControlValueAccessor, OnInit, case EntityType.ENTITY_VIEW: subTypesObservable = this.entityViewService.getEntityViewTypes({ignoreLoading: true}); break; + case EntityType.ALARM: + subTypesObservable = this.alarmService.getAlarmTypes({ignoreLoading: true}); + break; } if (subTypesObservable) { this.entitySubtypes = subTypesObservable.pipe( 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 341408a615..35a472f853 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -535,7 +535,11 @@ "search-propagated-alarms": "Search propagated alarms", "comments": "Alarm comments", "show-more": "Show more", - "additional-info": "Additional info" + "additional-info": "Additional info", + "alarm-type": "Alarm type", + "enter-alarm-type": "Enter alarm type", + "no-alarm-types-matching": "No alarm types matching '{{entitySubtype}}' were found.", + "alarm-type-list-empty": "No alarm types selected." }, "alarm-activity": { "add": "Add a comment...", From df2ef64e67b080a4bab29cff2c1596638247105d Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 4 Jul 2023 16:40:42 +0200 Subject: [PATCH 2/4] added alarm types cache --- .../server/controller/AlarmController.java | 4 +- .../DefaultAlarmSubscriptionService.java | 6 +-- .../src/main/resources/thingsboard.yml | 3 ++ .../server/dao/alarm/AlarmService.java | 2 +- .../server/common/data/CacheConstants.java | 1 + .../server/dao/alarm/AlarmDao.java | 2 +- .../dao/alarm/AlarmTypesCacheEvictEvent.java | 26 +++++++++++++ .../dao/alarm/AlarmTypesCaffeineCache.java | 36 ++++++++++++++++++ .../dao/alarm/AlarmTypesRedisCache.java | 38 +++++++++++++++++++ .../server/dao/alarm/BaseAlarmService.java | 35 ++++++++++++----- .../server/dao/sql/alarm/AlarmRepository.java | 2 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 4 +- .../resources/application-test.properties | 3 ++ .../engine/api/RuleEngineAlarmService.java | 3 +- 14 files changed, 143 insertions(+), 22 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java 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 9287ed515e..74b0075a17 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.controller; -import com.google.common.util.concurrent.ListenableFuture; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; @@ -506,8 +505,7 @@ public class AlarmController extends BaseController { @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) @ResponseBody public List getAlarmTypes() throws ThingsboardException, ExecutionException, InterruptedException { - ListenableFuture> alarmTypes = alarmService.findAlarmTypesByTenantId(getTenantId()); - return checkNotNull(alarmTypes.get()); + return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index dd52403bcd..394e0598ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -45,15 +45,15 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; @@ -238,7 +238,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId) { + public List findAlarmTypesByTenantId(TenantId tenantId) { return alarmService.findAlarmTypesByTenantId(tenantId); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 81654e3033..057013d7f2 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -497,6 +497,9 @@ cache: entityCount: timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_COUNT_TTL:1440}" maxSize: "${CACHE_SPECS_ENTITY_COUNT_MAX_SIZE:100000}" + alarmTypes: + timeToLiveInMinutes: "${CACHE_SPECS_ALARM_TYPES_TTL:60}" + maxSize: "${CACHE_SPECS_ALARM_TYPES_MAX_SIZE:10000}" # deliberately placed outside 'specs' group above notificationRules: diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index 89feae53b8..de0e451bcb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -124,5 +124,5 @@ public interface AlarmService extends EntityDaoService { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId); + List findAlarmTypesByTenantId(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index ff0032f435..dc59cef3e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -44,4 +44,5 @@ public class CacheConstants { public static final String USER_SETTINGS_CACHE = "userSettings"; public static final String DASHBOARD_TITLES_CACHE = "dashboardTitles"; public static final String ENTITY_COUNT_CACHE = "entityCount"; + public static final String ALARM_TYPES_CACHE = "alarmTypes"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index df680bada0..6c6d8cb27a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -98,6 +98,6 @@ public interface AlarmDao extends Dao { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - ListenableFuture> findTenantAlarmTypesAsync(UUID tenantId); + List findTenantAlarmTypesAsync(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java new file mode 100644 index 0000000000..985d0c8ad1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@RequiredArgsConstructor +class AlarmTypesCacheEvictEvent { + private final TenantId tenantId; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java new file mode 100644 index 0000000000..89b0d12b18 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.ArrayList; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("AlarmTypesCache") +public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache> { + + public AlarmTypesCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.ALARM_TYPES_CACHE); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java new file mode 100644 index 0000000000..4b5b0b3d9e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CacheSpecsMap; +import org.thingsboard.server.cache.RedisTbTransactionalCache; +import org.thingsboard.server.cache.TBRedisCacheConfiguration; +import org.thingsboard.server.cache.TbFSTRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.ArrayList; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("AlarmTypesCache") +public class AlarmTypesRedisCache extends RedisTbTransactionalCache> { + + public AlarmTypesRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { + super(CacheConstants.ALARM_TYPES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); + } +} 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 f40be0378d..eef476ae72 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 @@ -20,12 +20,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; @@ -56,7 +58,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; -import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; @@ -81,7 +83,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("AlarmDaoService") @Slf4j @RequiredArgsConstructor -public class BaseAlarmService extends AbstractEntityService implements AlarmService { +public class BaseAlarmService extends AbstractCachedEntityService, AlarmTypesCacheEvictEvent> implements AlarmService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; @@ -90,6 +92,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private final EntityService entityService; private final DataValidator alarmDataValidator; + @Autowired + protected TbTransactionalCache> alarmTypesCache; + + @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) + @Override + public void handleEvictEvent(AlarmTypesCacheEvictEvent event) { + TenantId tenantId = event.getTenantId(); + cache.evict(tenantId); + alarmTypesCache.evict(tenantId); + } + @Override public AlarmApiCallResult updateAlarm(AlarmUpdateRequest request) { validateAlarmRequest(request); @@ -115,6 +128,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ if (!result.isSuccessful() && !alarmCreationEnabled) { throw new ApiUsageLimitsExceededException("Alarms creation is disabled"); } + publishEvictEvent(new AlarmTypesCacheEvictEvent(request.getTenantId())); return withPropagated(result); } @@ -191,6 +205,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } else { deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); + publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).build(); } } @@ -206,12 +221,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); + publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); return result; } private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { log.debug("New Alarm : {}", alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); + publishEvictEvent(new AlarmTypesCacheEvictEvent(alarm.getTenantId())); List propagatedEntitiesList = createEntityAlarmRecords(saved); return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); } @@ -380,15 +397,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId) { + public List findAlarmTypesByTenantId(TenantId tenantId) { log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - ListenableFuture> tenantAssetTypes = alarmDao.findTenantAlarmTypesAsync(tenantId.getId()); - return Futures.transform(tenantAssetTypes, - assetTypes -> { - assetTypes.sort(Comparator.comparing(EntitySubtype::getType)); - return assetTypes; - }, MoreExecutors.directExecutor()); + return cache.getAndPutInTransaction(tenantId, () -> + alarmDao.findTenantAlarmTypesAsync(tenantId.getId()).stream() + .sorted(Comparator.comparing(EntitySubtype::getType)) + .collect(Collectors.toCollection(ArrayList::new)), false); } private Alarm merge(Alarm existing, Alarm alarm) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 81d9068f11..473814a0f5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -346,6 +346,6 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true) String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime); - @Query("SELECT DISTINCT a.type FROM AlarmEntity a WHERE a.tenantId = :tenantId") + @Query(value = "SELECT DISTINCT a.type FROM alarm a WHERE a.tenant_id = :tenantId LIMIT 256", nativeQuery = true) List findTenantAlarmTypes(@Param("tenantId") UUID tenantId); } 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 d721996e0b..ec5734df84 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 @@ -370,8 +370,8 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public ListenableFuture> findTenantAlarmTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId))); + public List findTenantAlarmTypesAsync(UUID tenantId) { + return convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId)); } private static String getPropagationTypes(AlarmPropagationInfo ap) { diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index d89211cb2f..e599cddb37 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -74,6 +74,9 @@ cache.specs.dashboardTitles.maxSize=10000 cache.specs.entityCount.timeToLiveInMinutes=1440 cache.specs.entityCount.maxSize=10000 +cache.specs.alarmTypes.timeToLiveInMinutes=60 +cache.specs.alarmTypes.maxSize=10000 + redis.connection.host=localhost redis.connection.port=6379 redis.connection.db=0 diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index ce069a92bb..5aa76726a7 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -56,6 +56,7 @@ public interface RuleEngineAlarmService { * Only one active alarm may exist for the pair {originatorId, alarmType} */ AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request); + /** * Designed to update existing alarm. Accepts only part of the alarm fields. */ @@ -113,5 +114,5 @@ public interface RuleEngineAlarmService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - ListenableFuture> findAlarmTypesByTenantId(TenantId tenantId); + List findAlarmTypesByTenantId(TenantId tenantId); } From ccdb0212a8bbe55e94a4ca1361728cee4fabf65b Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 1 Aug 2023 16:17:51 +0200 Subject: [PATCH 3/4] minor refactoring --- .../main/java/org/thingsboard/server/dao/alarm/AlarmDao.java | 2 +- .../java/org/thingsboard/server/dao/alarm/BaseAlarmService.java | 2 +- .../java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index 6c6d8cb27a..da31be082e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -98,6 +98,6 @@ public interface AlarmDao extends Dao { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - List findTenantAlarmTypesAsync(UUID tenantId); + List findTenantAlarmTypes(UUID tenantId); } 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 eef476ae72..0e8d18e042 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 @@ -401,7 +401,7 @@ public class BaseAlarmService extends AbstractCachedEntityService - alarmDao.findTenantAlarmTypesAsync(tenantId.getId()).stream() + alarmDao.findTenantAlarmTypes(tenantId.getId()).stream() .sorted(Comparator.comparing(EntitySubtype::getType)) .collect(Collectors.toCollection(ArrayList::new)), false); } 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 ec5734df84..42e50e878b 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 @@ -370,7 +370,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public List findTenantAlarmTypesAsync(UUID tenantId) { + public List findTenantAlarmTypes(UUID tenantId) { return convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId)); } From 1c17b0051351107d6d71c23559655dbf6dec6478 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 15 Aug 2023 16:00:14 +0200 Subject: [PATCH 4/4] added alarm_types table --- .../main/data/upgrade/3.5.1/schema_update.sql | 7 ++ .../server/controller/AlarmController.java | 13 ++- .../DefaultAlarmSubscriptionService.java | 6 +- .../service/ttl/AlarmsCleanUpService.java | 9 ++- .../controller/AlarmControllerTest.java | 81 ++++++++++++++++++- .../service/ttl/AlarmsCleanUpServiceTest.java | 4 +- .../server/dao/alarm/AlarmService.java | 9 ++- .../server/common/data/page/PageData.java | 3 +- .../server/dao/alarm/AlarmDao.java | 3 +- .../dao/alarm/AlarmTypesCaffeineCache.java | 5 +- .../dao/alarm/AlarmTypesRedisCache.java | 5 +- .../server/dao/alarm/BaseAlarmService.java | 43 ++++++---- .../server/dao/sql/alarm/AlarmRepository.java | 12 ++- .../server/dao/sql/alarm/JpaAlarmDao.java | 17 +++- .../main/resources/sql/schema-entities.sql | 6 ++ .../sql/schema-views-and-functions.sql | 1 + .../engine/api/RuleEngineAlarmService.java | 4 +- 17 files changed, 184 insertions(+), 44 deletions(-) diff --git a/application/src/main/data/upgrade/3.5.1/schema_update.sql b/application/src/main/data/upgrade/3.5.1/schema_update.sql index 1655ecb978..e0f0bccbb4 100644 --- a/application/src/main/data/upgrade/3.5.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.5.1/schema_update.sql @@ -122,3 +122,10 @@ ALTER TABLE resource UPDATE resource SET etag = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null; +CREATE TABLE IF NOT EXISTS alarm_types ( + tenant_id uuid NOT NULL, + type varchar(255) NOT NULL, + CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type) + ); + +INSERT INTO alarm_types (tenant_id, type) SELECT DISTINCT tenant_id, type FROM alarm ON CONFLICT (tenant_id, type) DO NOTHING; 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 74b0075a17..4e6e1d5824 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; @@ -504,8 +505,16 @@ public class AlarmController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) @ResponseBody - public List getAlarmTypes() throws ThingsboardException, ExecutionException, InterruptedException { - return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId())); + public PageData getAlarmTypes(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException, ExecutionException, InterruptedException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, "type", sortOrder); + return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId(), pageLink)); } } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index 394e0598ae..005ebd268a 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; @@ -59,7 +60,6 @@ import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import java.util.Collection; -import java.util.List; /** * Created by ashvayka on 27.03.18. @@ -238,8 +238,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public List findAlarmTypesByTenantId(TenantId tenantId) { - return alarmService.findAlarmTypesByTenantId(tenantId); + public PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) { + return alarmService.findAlarmTypesByTenantId(tenantId, pageLink); } private void onAlarmUpdated(AlarmApiCallResult result) { diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java index 5154829ea6..2f84767eff 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java @@ -39,7 +39,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.action.EntityActionService; import java.util.Date; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; @TbCoreComponent @@ -86,20 +88,25 @@ public class AlarmsCleanUpService { PageLink removalBatchRequest = new PageLink(removalBatchSize, 0); long totalRemoved = 0; + Set typesToRemove = new HashSet<>(); while (true) { PageData toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest); for (AlarmId alarmId : toRemove.getData()) { relationService.deleteEntityRelations(tenantId, alarmId); - Alarm alarm = alarmService.delAlarm(tenantId, alarmId).getAlarm(); + Alarm alarm = alarmService.delAlarm(tenantId, alarmId, false).getAlarm(); if (alarm != null) { entityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); totalRemoved++; + typesToRemove.add(alarm.getType()); } } if (!toRemove.hasNext()) { break; } } + + alarmService.delAlarmTypes(tenantId, typesToRemove); + if (totalRemoved > 0) { log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); } diff --git a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java index d4e5210ab2..8b6e6e508f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -47,7 +47,6 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -747,15 +746,15 @@ public class AlarmControllerTest extends AbstractControllerTest { public void testGetAlarmTypes() throws Exception { loginTenantAdmin(); - Mockito.reset(tbClusterService, auditLogService); - List types = new ArrayList<>(); for (int i = 1; i < 13; i++) { types.add(createAlarm(TEST_ALARM_TYPE + i).getType()); } - List foundTypes = doGetTyped("/api/alarm/types", new TypeReference>() {}) + List foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() .stream() .map(EntitySubtype::getType) .collect(Collectors.toList()); @@ -766,4 +765,78 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(types, foundTypes); } + @Test + public void testDeleteAlarmTypes() throws Exception { + loginTenantAdmin(); + + List alarms = new ArrayList<>(); + + for (int i = 1; i < 13; i++) { + alarms.add(createAlarm(TEST_ALARM_TYPE + i)); + } + + Device device = new Device(); + device.setName("Test device 2"); + device.setCustomerId(customerId); + customerDevice = doPost("/api/device", device, Device.class); + + for (int i = 1; i < 14; i++) { + alarms.add(createAlarm(TEST_ALARM_TYPE + i)); + } + + List expectedTypes = alarms.stream().map(AlarmInfo::getType).distinct().sorted().collect(Collectors.toList()); + + List foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(13, foundTypes.size()); + Assert.assertEquals(expectedTypes, foundTypes); + + for (int i = 0; i < 12; i++) { + doDelete("/api/alarm/" + alarms.get(i).getId()).andExpect(status().isOk()); + } + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(13, foundTypes.size()); + Assert.assertEquals(expectedTypes, foundTypes); + + doDelete("/api/alarm/" + alarms.get(12).getId()).andExpect(status().isOk()); + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(12, foundTypes.size()); + + for (int i = 13; i < alarms.size(); i++) { + doDelete("/api/alarm/" + alarms.get(i).getId()).andExpect(status().isOk()); + } + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertTrue(foundTypes.isEmpty()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java index 6cabff788e..f9735dc0a3 100644 --- a/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java @@ -104,10 +104,10 @@ public class AlarmsCleanUpServiceTest extends AbstractControllerTest { alarmsCleanUpService.cleanUp(); for (AlarmId outdatedAlarm : outdatedAlarms) { - verify(alarmService).delAlarm(eq(tenantId), eq(outdatedAlarm)); + verify(alarmService).delAlarm(eq(tenantId), eq(outdatedAlarm), eq(false)); } for (AlarmId freshAlarm : freshAlarms) { - verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm)); + verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm), eq(false)); } verify(cleanUpServiceLogger).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any()); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index de0e451bcb..c2c153267b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -34,13 +34,14 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.Collection; -import java.util.List; +import java.util.Set; public interface AlarmService extends EntityDaoService { @@ -76,6 +77,10 @@ public interface AlarmService extends EntityDaoService { AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId); + AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId, boolean deleteAlarmType); + + void delAlarmTypes(TenantId tenantId, Set types); + /* * Legacy API, before 3.5. */ @@ -124,5 +129,5 @@ public interface AlarmService extends EntityDaoService { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - List findAlarmTypesByTenantId(TenantId tenantId); + PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java index 2ae94a0064..6ccf49bd13 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java @@ -20,13 +20,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @ApiModel -public class PageData { +public class PageData implements Serializable { private final List data; private final int totalPages; diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index da31be082e..515866a810 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -98,6 +98,7 @@ public interface AlarmDao extends Dao { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); - List findTenantAlarmTypes(UUID tenantId); + PageData findTenantAlarmTypes(UUID tenantId, PageLink pageLink); + boolean removeAlarmTypes(UUID tenantId, Set types); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java index 89b0d12b18..4ad08338c4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java @@ -22,12 +22,11 @@ import org.thingsboard.server.cache.CaffeineTbTransactionalCache; import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.id.TenantId; - -import java.util.ArrayList; +import org.thingsboard.server.common.data.page.PageData; @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) @Service("AlarmTypesCache") -public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache> { +public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache> { public AlarmTypesCaffeineCache(CacheManager cacheManager) { super(cacheManager, CacheConstants.ALARM_TYPES_CACHE); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java index 4b5b0b3d9e..18eb81f690 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java @@ -25,12 +25,11 @@ import org.thingsboard.server.cache.TbFSTRedisSerializer; import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.id.TenantId; - -import java.util.ArrayList; +import org.thingsboard.server.common.data.page.PageData; @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") @Service("AlarmTypesCache") -public class AlarmTypesRedisCache extends RedisTbTransactionalCache> { +public class AlarmTypesRedisCache extends RedisTbTransactionalCache> { public AlarmTypesRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { super(CacheConstants.ALARM_TYPES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); 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 0e8d18e042..e6e006db79 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 @@ -22,12 +22,10 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.util.CollectionUtils; -import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; @@ -51,6 +49,8 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; @@ -68,7 +68,6 @@ import org.thingsboard.server.dao.tenant.TenantService; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -83,24 +82,22 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("AlarmDaoService") @Slf4j @RequiredArgsConstructor -public class BaseAlarmService extends AbstractCachedEntityService, AlarmTypesCacheEvictEvent> implements AlarmService { +public class BaseAlarmService extends AbstractCachedEntityService, AlarmTypesCacheEvictEvent> implements AlarmService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + private static final PageLink DEFAULT_ALARM_TYPES_PAGE_LINK = new PageLink(25, 0, null, new SortOrder("type")); + private final TenantService tenantService; private final AlarmDao alarmDao; private final EntityService entityService; private final DataValidator alarmDataValidator; - @Autowired - protected TbTransactionalCache> alarmTypesCache; - @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) @Override public void handleEvictEvent(AlarmTypesCacheEvictEvent event) { TenantId tenantId = event.getTenantId(); cache.evict(tenantId); - alarmTypesCache.evict(tenantId); } @Override @@ -198,6 +195,12 @@ public class BaseAlarmService extends AbstractCachedEntityService types) { + if (!types.isEmpty() && alarmDao.removeAlarmTypes(tenantId.getId(), types)) { + publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); + } + } + @Override @Transactional public AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId) { @@ -221,14 +233,12 @@ public class BaseAlarmService extends AbstractCachedEntityService(getPropagationEntityIds(alarm))); deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); - publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); return result; } private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { log.debug("New Alarm : {}", alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); - publishEvictEvent(new AlarmTypesCacheEvictEvent(alarm.getTenantId())); List propagatedEntitiesList = createEntityAlarmRecords(saved); return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); } @@ -397,13 +407,14 @@ public class BaseAlarmService extends AbstractCachedEntityService findAlarmTypesByTenantId(TenantId tenantId) { + public PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) { log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return cache.getAndPutInTransaction(tenantId, () -> - alarmDao.findTenantAlarmTypes(tenantId.getId()).stream() - .sorted(Comparator.comparing(EntitySubtype::getType)) - .collect(Collectors.toCollection(ArrayList::new)), false); + if (DEFAULT_ALARM_TYPES_PAGE_LINK.equals(pageLink)) { + return cache.getAndPutInTransaction(tenantId, () -> + alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink), false); + } + return alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink); } private Alarm merge(Alarm existing, Alarm alarm) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 473814a0f5..ab86b5afe3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -18,8 +18,10 @@ package org.thingsboard.server.dao.sql.alarm; 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.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.dao.model.sql.AlarmEntity; import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; @@ -346,6 +348,12 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true) String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime); - @Query(value = "SELECT DISTINCT a.type FROM alarm a WHERE a.tenant_id = :tenantId LIMIT 256", nativeQuery = true) - List findTenantAlarmTypes(@Param("tenantId") UUID tenantId); + @Query(value = "SELECT at.type FROM alarm_types AS at WHERE at.tenant_id = :tenantId AND LOWER(at.type) LIKE LOWER(CONCAT('%', :searchText, '%'))", nativeQuery = true) + Page findTenantAlarmTypes(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); + + @Transactional + @Modifying + @Query(value = "DELETE FROM alarm_types AS at WHERE NOT EXISTS (SELECT 1 FROM alarm AS a WHERE a.tenant_id = at.tenant_id AND a.type = at.type) AND at.tenant_id = :tenantId AND at.type IN (:types)", nativeQuery = true) + int deleteTypeIfNoOneAlarmExists(@Param("tenantId") UUID tenantId, @Param("types") Set types); + } 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 42e50e878b..15b576e391 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 @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; @@ -69,6 +70,7 @@ import java.util.Set; import java.util.UUID; import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; +import static org.thingsboard.server.dao.DaoUtil.toPageable; /** * Created by Valerii Sosliuk on 5/19/2017. @@ -370,8 +372,19 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public List findTenantAlarmTypes(UUID tenantId) { - return convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId)); + public PageData findTenantAlarmTypes(UUID tenantId, PageLink pageLink) { + Page page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink)); + if (page.isEmpty()) { + return new PageData<>(); + } + + List data = convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, page.getContent()); + return new PageData<>(data, page.getTotalPages(), page.getTotalElements(), page.hasNext()); + } + + @Override + public boolean removeAlarmTypes(UUID tenantId, Set types) { + return alarmRepository.deleteTypeIfNoOneAlarmExists(tenantId, types) > 0; } private static String getPropagationTypes(AlarmPropagationInfo ap) { diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index bfb2eed805..6c2def9d00 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -64,6 +64,12 @@ CREATE TABLE IF NOT EXISTS alarm ( cleared boolean ); +CREATE TABLE IF NOT EXISTS alarm_types ( + tenant_id uuid NOT NULL, + type varchar(255) NOT NULL, + CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type) +); + CREATE TABLE IF NOT EXISTS alarm_comment ( id uuid NOT NULL, created_time bigint NOT NULL, diff --git a/dao/src/main/resources/sql/schema-views-and-functions.sql b/dao/src/main/resources/sql/schema-views-and-functions.sql index 3cd20ac0c4..3aa797d7b6 100644 --- a/dao/src/main/resources/sql/schema-views-and-functions.sql +++ b/dao/src/main/resources/sql/schema-views-and-functions.sql @@ -114,6 +114,7 @@ BEGIN a_details, a_propagate, a_propagate_to_owner, a_propagate_to_tenant, a_propagation_types, false, 0, false, 0, NULL, 0); + INSERT INTO alarm_types (tenant_id, type) VALUES (t_id, a_type) ON CONFLICT (tenant_id, type) DO NOTHING; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; RETURN json_build_object('success', true, 'created', true, 'modified', true, 'alarm', row_to_json(result))::text; ELSE diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index 5aa76726a7..ebaff2bfaf 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -35,12 +35,12 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.alarm.AlarmOperationResult; import java.util.Collection; -import java.util.List; /** * Created by ashvayka on 02.04.18. @@ -114,5 +114,5 @@ public interface RuleEngineAlarmService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - List findAlarmTypesByTenantId(TenantId tenantId); + PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink); }