diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java index 9d123319ef..b12a253b36 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -19,12 +19,13 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; @@ -54,6 +55,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RequiredArgsConstructor @RequestMapping("/api") public class AlarmCommentController extends BaseController { + public static final String ALARM_ID = "alarmId"; public static final String ALARM_COMMENT_ID = "commentId"; @@ -68,8 +70,7 @@ public class AlarmCommentController extends BaseController { "\n\n If comment type is not specified the default value 'OTHER' will be saved. If 'alarmId' or 'userId' specified in body it will be ignored." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/alarm/{alarmId}/comment") public AlarmComment saveAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); @@ -82,8 +83,7 @@ public class AlarmCommentController extends BaseController { @ApiOperation(value = "Delete Alarm comment (deleteAlarmComment)", notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/alarm/{alarmId}/comment/{commentId}") public void deleteAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @Parameter(description = ALARM_COMMENT_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); @@ -98,8 +98,7 @@ public class AlarmCommentController extends BaseController { notes = "Returns a page of alarm comments for specified alarm. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/{alarmId}/comment") public PageData getAlarmComments( @Parameter(description = ALARM_ID_PARAM_DESCRIPTION, required = true) @PathVariable(ALARM_ID) String strAlarmId, @@ -118,4 +117,5 @@ public class AlarmCommentController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder); return checkNotNull(alarmCommentService.findAlarmComments(alarm.getTenantId(), alarmId, pageLink)); } + } 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 dd8d883145..f5a04e1e48 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -21,12 +21,13 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; 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; @@ -58,7 +59,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; -import java.util.concurrent.ExecutionException; import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ALARM_INFO_DESCRIPTION; @@ -104,8 +104,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarm (getAlarmById)", notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/{alarmId}") public Alarm getAlarmById(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); @@ -117,8 +116,7 @@ public class AlarmController extends BaseController { notes = "Fetch the Alarm Info object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/info/{alarmId}") public AlarmInfo getAlarmInfoById(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); @@ -136,11 +134,9 @@ public class AlarmController extends BaseController { "If the user tries to create 'HighTemperature' alarm for the same device again, the previous alarm will be updated (the 'end_ts' will be set to current timestamp). " + "If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " + - TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - ) + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/alarm") public Alarm saveAlarm(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException { alarm.setTenantId(getTenantId()); checkNotNull(alarm.getOriginator()); @@ -155,8 +151,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Delete Alarm (deleteAlarm)", notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/alarm/{alarmId}") public boolean deleteAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); @@ -169,7 +164,7 @@ public class AlarmController extends BaseController { "Once acknowledged, the 'ack_ts' field will be set to current timestamp and special rule chain event 'ALARM_ACK' will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) + @PostMapping(value = "/alarm/{alarmId}/ack") @ResponseStatus(value = HttpStatus.OK) public AlarmInfo ackAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception { checkParameter(ALARM_ID, strAlarmId); @@ -184,7 +179,7 @@ public class AlarmController extends BaseController { "Once cleared, the 'clear_ts' field will be set to current timestamp and special rule chain event 'ALARM_CLEAR' will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST) + @PostMapping(value = "/alarm/{alarmId}/clear") @ResponseStatus(value = HttpStatus.OK) public AlarmInfo clearAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception { checkParameter(ALARM_ID, strAlarmId); @@ -200,7 +195,7 @@ public class AlarmController extends BaseController { "(or ALARM_REASSIGNED in case of assigning already assigned alarm) will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/assign/{assigneeId}", method = RequestMethod.POST) + @PostMapping(value = "/alarm/{alarmId}/assign/{assigneeId}") @ResponseStatus(value = HttpStatus.OK) public Alarm assignAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @@ -221,7 +216,7 @@ public class AlarmController extends BaseController { "Once unassigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_UNASSIGNED' will be generated. " + "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{alarmId}/assign", method = RequestMethod.DELETE) + @DeleteMapping(value = "/alarm/{alarmId}/assign") @ResponseStatus(value = HttpStatus.OK) public Alarm unassignAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId @@ -236,8 +231,7 @@ public class AlarmController extends BaseController { notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/{entityType}/{entityId}") public PageData getAlarms( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable(ENTITY_TYPE) String strEntityType, @@ -265,7 +259,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @Parameter(description = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); @@ -292,8 +286,7 @@ public class AlarmController extends BaseController { "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarms", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarms") public PageData getAllAlarms( @Parameter(description = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"})) @RequestParam(required = false) String searchStatus, @@ -317,7 +310,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long endTime, @Parameter(description = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION) @RequestParam(required = false) Boolean fetchOriginator - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus); AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); if (alarmSearchStatus != null && alarmStatus != null) { @@ -341,8 +334,7 @@ public class AlarmController extends BaseController { notes = "Returns a page of alarms for the selected entity. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/alarm/{entityType}/{entityId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/v2/alarm/{entityType}/{entityId}") public PageData getAlarmsV2( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable(ENTITY_TYPE) String strEntityType, @@ -370,7 +362,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long startTime, @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION) @RequestParam(required = false) Long endTime - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); @@ -407,8 +399,7 @@ public class AlarmController extends BaseController { "If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/v2/alarms", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/v2/alarms") public PageData getAllAlarmsV2( @Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, array = @ArraySchema(schema = @Schema(type = "string", allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))) @RequestParam(required = false) String[] statusList, @@ -432,7 +423,7 @@ public class AlarmController extends BaseController { @RequestParam(required = false) Long startTime, @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION) @RequestParam(required = false) Long endTime - ) throws ThingsboardException, ExecutionException, InterruptedException { + ) throws ThingsboardException { List alarmStatusList = new ArrayList<>(); if (statusList != null) { for (String strStatus : statusList) { @@ -465,11 +456,9 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)", notes = "Search the alarms by originator ('entityType' and entityId') and optional 'status' or 'searchStatus' filters and returns the highest AlarmSeverity(CRITICAL, MAJOR, MINOR, WARNING or INDETERMINATE). " + - "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH - ) + "Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}") public AlarmSeverity getHighestAlarmSeverity( @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable(ENTITY_TYPE) String strEntityType, @@ -499,8 +488,7 @@ public class AlarmController extends BaseController { @ApiOperation(value = "Get Alarm Types (getAlarmTypes)", notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/alarm/types") public PageData getAlarmTypes(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true) @@ -508,7 +496,7 @@ public class AlarmController extends BaseController { @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION) @RequestParam(required = false) String textSearch, @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"})) - @RequestParam(required = false) String sortOrder) throws ThingsboardException, ExecutionException, InterruptedException { + @RequestParam(required = false) String sortOrder) throws ThingsboardException { 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/entitiy/alarm/DefaultTbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java index de4d0ac9cb..73f6e0a861 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java @@ -32,7 +32,7 @@ import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @Service @AllArgsConstructor -public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService{ +public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService { @Autowired private AlarmCommentService alarmCommentService; @@ -68,4 +68,5 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem throw new ThingsboardException("System comment could not be deleted", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } + } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java index 55107a8326..1c7d312e8b 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsDeletionTaskProcessor.java @@ -43,33 +43,30 @@ public class AlarmsDeletionTaskProcessor extends HousekeeperTaskProcessor> alarms = alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, lastCreatedTime, lastId, 128); - if (alarms.isEmpty()) { - break; - } - - housekeeperClient.submitTask(new AlarmsDeletionHousekeeperTask(tenantId, entityId, alarms.stream().map(TbPair::getFirst).toList())); - - TbPair last = alarms.get(alarms.size() - 1); - lastId = new AlarmId(last.getFirst()); - lastCreatedTime = last.getSecond(); - log.debug("[{}][{}][{}] Submitted task for deleting {} alarms", tenantId, entityType, entityId, alarms.size()); + if (task.getAlarms() == null) { + AlarmId lastId = null; + long lastCreatedTime = 0; + while (true) { + List> alarms = alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, lastCreatedTime, lastId, 128); + if (alarms.isEmpty()) { + break; } - } else { - for (UUID alarmId : task.getAlarms()) { - alarmService.delAlarm(tenantId, new AlarmId(alarmId)); - } - log.debug("[{}][{}][{}] Deleted {} alarms", tenantId, entityType, entityId, task.getAlarms().size()); + + housekeeperClient.submitTask(new AlarmsDeletionHousekeeperTask(tenantId, entityId, alarms.stream().map(TbPair::getFirst).toList())); + + TbPair last = alarms.get(alarms.size() - 1); + lastId = new AlarmId(last.getFirst()); + lastCreatedTime = last.getSecond(); + log.debug("[{}][{}][{}] Submitted task for deleting {} alarms", tenantId, entityType, entityId, alarms.size()); } + int count = alarmService.deleteEntityAlarmRecords(tenantId, entityId); + log.debug("[{}][{}][{}] Deleted {} entity alarms", tenantId, entityType, entityId, count); + } else { + for (UUID alarmId : task.getAlarms()) { + alarmService.delAlarm(tenantId, new AlarmId(alarmId)); + } + log.debug("[{}][{}][{}] Deleted {} alarms", tenantId, entityType, entityId, task.getAlarms().size()); } - - int count = alarmService.deleteEntityAlarmRecords(tenantId, entityId); - log.debug("[{}][{}][{}] Deleted {} entity alarms", tenantId, entityType, entityId, count); } @Override diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java index d701ce6113..a276a9c5ff 100644 --- a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -31,6 +31,8 @@ import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.AttributeScope; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.StringUtils; @@ -77,6 +79,8 @@ import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.entity.EntityServiceRegistry; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.relation.RelationService; @@ -145,6 +149,10 @@ public class HousekeeperServiceTest extends AbstractControllerTest { private ApiUsageStateDao apiUsageStateDao; @Autowired private EntityServiceRegistry entityServiceRegistry; + @Autowired + private CustomerService customerService; + @Autowired + private DashboardService dashboardService; @SpyBean private TsHistoryDeletionTaskProcessor tsHistoryDeletionTaskProcessor; @@ -238,11 +246,67 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { verifyNoAlarms(device.getId()); }); } + @Test + public void whenAssetIsDeleted_thenDeleteAllAlarms() throws Exception { + Asset asset = createAsset(); + for (int i = 1; i <= 1000; i++) { + createAlarm(asset.getId()); + } + + doDelete("/api/asset/" + asset.getId()).andExpect(status().isOk()); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(asset.getId()); + }); + } + + @Test + public void whenDashboardIsDeleted_thenDeleteAllAlarms() throws Exception { + Dashboard dashboard = createDashboard(); + for (int i = 1; i <= 1000; i++) { + createAlarm(dashboard.getId()); + } + + doDelete("/api/dashboard/" + dashboard.getId()).andExpect(status().isOk()); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(dashboard.getId()); + }); + } + + @Test + public void whenCustomerIsDeleted_thenDeleteAllAlarms() throws Exception { + Customer customer = createCustomer(); + for (int i = 1; i <= 1000; i++) { + createAlarm(customer.getId()); + } + + doDelete("/api/customer/" + customer.getId()).andExpect(status().isOk()); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(customer.getId()); + }); + } + + @Test + public void whenUserIsDeleted_thenDeleteAllAlarms() throws Exception { + UserId userId = customerUserId; + for (int i = 1; i <= 1000; i++) { + createAlarm(userId); + } + + doDelete("/api/user/" + userId).andExpect(status().isOk()); + + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { + verifyNoAlarms(userId); + }); + } + @Test public void whenTenantIsDeleted_thenDeleteAllEntitiesAndCleanUpRelatedData() throws Exception { loginDifferentTenant(); @@ -335,7 +399,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); int attempts = 2; - await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { for (int i = 0; i <= attempts; i++) { int attempt = i; verify(housekeeperReprocessingService).submitForReprocessing(argThat(getTaskMatcher(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, @@ -345,7 +409,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { assertThat(getTimeseriesHistory(device.getId())).isNotEmpty(); doCallRealMethod().when(tsHistoryDeletionTaskProcessor).process(any()); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(getTimeseriesHistory(device.getId())).isEmpty(); }); } @@ -379,7 +443,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); int attempts = 2; - await().atMost(30, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> { for (int i = 0; i <= attempts; i++) { int attempt = i; verify(housekeeperReprocessingService).submitForReprocessing(argThat(getTaskMatcher(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, @@ -393,7 +457,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doCallRealMethod().when(tsHistoryDeletionTaskProcessor).process(any()); someExecutor.shutdown(); - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { assertThat(getTimeseriesHistory(device.getId())).isEmpty(); }); } @@ -409,7 +473,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { doDelete("/api/device/" + device.getId()).andExpect(status().isOk()); int maxAttempts = 5; - await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> { for (int i = 1; i <= maxAttempts; i++) { verifyTaskProcessing(device.getId(), HousekeeperTaskType.DELETE_TS_HISTORY, i); } @@ -479,7 +543,6 @@ public class HousekeeperServiceTest extends AbstractControllerTest { eventService.saveAsync(event); await().atMost(10, TimeUnit.SECONDS) .until(() -> !getEvents(entityId).isEmpty()); - } private void createRelation(DeviceId to, AssetId from) { @@ -502,14 +565,14 @@ public class HousekeeperServiceTest extends AbstractControllerTest { assertThat(alarmService.findAlarmIdsByOriginatorId(tenantId, deviceId, 0, null, 10)).isNotEmpty(); } - private void createAlarm(DeviceId deviceId) { - Alarm alarm = doPost("/api/alarm", Alarm.builder() + private void createAlarm(EntityId entityId) { + doPost("/api/alarm", Alarm.builder() .tenantId(tenantId) - .originator(deviceId) + .originator(entityId) .severity(AlarmSeverity.CRITICAL) - .type("test alarm for " + deviceId + " " + RandomStringUtils.randomAlphabetic(10)) + .type("test alarm for " + entityId + " " + RandomStringUtils.randomAlphabetic(10)) .build(), Alarm.class); - assertThat(alarmService.findAlarmIdsByOriginatorId(tenantId, deviceId, 0, null, 10)).isNotEmpty(); + assertThat(alarmService.findAlarmIdsByOriginatorId(tenantId, entityId, 0, null, 10)).isNotEmpty(); } private TsKvEntry getLatestTelemetry(EntityId entityId) throws Exception { @@ -534,6 +597,20 @@ public class HousekeeperServiceTest extends AbstractControllerTest { return doPost("/api/asset", asset, Asset.class); } + private Customer createCustomer() { + Customer customer = new Customer(); + customer.setTenantId(tenantId); + customer.setTitle(StringUtils.randomAlphabetic(10)); + return customerService.saveCustomer(customer); + } + + private Dashboard createDashboard() { + Dashboard dashboard = new Dashboard(); + dashboard.setTenantId(tenantId); + dashboard.setTitle(StringUtils.randomAlphabetic(10)); + return dashboardService.saveDashboard(dashboard); + } + private RuleChainMetaData createRuleChain() { RuleChain ruleChain = new RuleChain(); ruleChain.setTenantId(tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index a48a865fe4..519b30a356 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -37,13 +37,11 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.io.Serial; import java.util.List; import java.util.Optional; import java.util.UUID; -/** - * Created by ashvayka on 11.05.17. - */ @Schema @Data @EqualsAndHashCode(callSuper = true) @@ -52,6 +50,9 @@ import java.util.UUID; @JsonIgnoreProperties(ignoreUnknown = true) public class Alarm extends BaseData implements HasName, HasTenantId, HasCustomerId { + @Serial + private static final long serialVersionUID = -1935800187424953611L; + @Schema(description = "JSON object with Tenant Id", accessMode = Schema.AccessMode.READ_ONLY) private TenantId tenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 972887e702..0654ba5a9d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -30,11 +30,17 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.io.Serial; + @Schema @Data @Builder @AllArgsConstructor public class AlarmComment extends BaseData implements HasName { + + @Serial + private static final long serialVersionUID = -5454905526404017592L; + @Schema(description = "JSON object with Alarm id.", accessMode = Schema.AccessMode.READ_ONLY) private AlarmId alarmId; @Schema(description = "JSON object with User id.", accessMode = Schema.AccessMode.READ_ONLY) @@ -85,4 +91,5 @@ public class AlarmComment extends BaseData implements HasName { this.comment = alarmComment.getComment(); this.userId = alarmComment.getUserId(); } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java index 83df975ad4..b2d01c8f52 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java @@ -22,9 +22,6 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.TimePageLink; -/** - * Created by ashvayka on 11.05.17. - */ @Data @Builder @AllArgsConstructor diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java index 18e989c1bd..1eaa2ea393 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSeverity.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.common.data.alarm; -/** - * Created by ashvayka on 11.05.17. - */ public enum AlarmSeverity { CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java index 9a69ae8e23..c97b0d24f2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.common.data.alarm; -/** - * Created by ashvayka on 11.05.17. - */ public enum AlarmStatus { ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK; 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 15d8789ecd..5c3e4f3c79 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 @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.Function; import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; @@ -68,7 +67,6 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; -import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; import java.util.ArrayList; @@ -98,7 +96,6 @@ public class BaseAlarmService extends AbstractCachedEntityService alarmDataValidator; @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) @Override @@ -444,12 +441,6 @@ public class BaseAlarmService extends AbstractCachedEntityService T getAndUpdate(TenantId tenantId, AlarmId alarmId, Function function) { - validateId(alarmId, "Alarm id should be specified!"); - Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId()); - return function.apply(entity); - } - @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { return Optional.ofNullable(findAlarmById(tenantId, new AlarmId(entityId.getId()))); 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 b9c1abe416..376f610d66 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 @@ -32,9 +32,6 @@ import java.util.List; import java.util.Set; import java.util.UUID; -/** - * Created by Valerii Sosliuk on 5/21/2017. - */ public interface AlarmRepository extends JpaRepository { @Query("SELECT a FROM AlarmEntity a WHERE a.originatorId = :originatorId AND a.type = :alarmType ORDER BY a.startTs DESC") 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 508ebd214e..edcbffcfbc 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 @@ -42,9 +42,6 @@ import org.thingsboard.server.common.data.query.AlarmDataQuery; import java.util.Collection; -/** - * Created by ashvayka on 02.04.18. - */ public interface RuleEngineAlarmService { /*