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); }