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 196a660e00..78ee6620fe 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -119,7 +119,7 @@ public class AlarmController extends BaseController { return checkAlarmInfoId(alarmId, Operation.READ); } - @ApiOperation(value = "Create or update Alarm (saveAlarm)", + @ApiOperation(value = "Create or Update Alarm (saveAlarm)", notes = "Creates or Updates the Alarm. " + "When creating alarm, platform generates Alarm Id as " + UUID_WIKI_LINK + "The newly created Alarm id will be present in the response. Specify existing Alarm id to update the alarm. " + @@ -193,9 +193,9 @@ public class AlarmController extends BaseController { @RequestMapping(value = "/alarm/{alarmId}/assign/{assigneeId}", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public Alarm assignAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) - @PathVariable(ALARM_ID) String strAlarmId, - @ApiParam(value = ASSIGN_ID_PARAM_DESCRIPTION) - @PathVariable(ASSIGNEE_ID) String strAssigneeId + @PathVariable(ALARM_ID) String strAlarmId, + @ApiParam(value = ASSIGN_ID_PARAM_DESCRIPTION) + @PathVariable(ASSIGNEE_ID) String strAssigneeId ) throws Exception { checkParameter(ALARM_ID, strAlarmId); checkParameter(ASSIGNEE_ID, strAssigneeId); @@ -203,7 +203,7 @@ public class AlarmController extends BaseController { Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); UserId assigneeId = new UserId(UUID.fromString(strAssigneeId)); checkUserId(assigneeId, Operation.READ); - return tbAlarmService.assign(alarm, getCurrentUser(), assigneeId); + return tbAlarmService.assign(alarm, assigneeId, System.currentTimeMillis(), getCurrentUser()); } @ApiOperation(value = "Unassign Alarm (unassignAlarm)", @@ -214,12 +214,12 @@ public class AlarmController extends BaseController { @RequestMapping(value = "/alarm/{alarmId}/assign", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) public Alarm unassignAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) - @PathVariable(ALARM_ID) String strAlarmId + @PathVariable(ALARM_ID) String strAlarmId ) throws Exception { checkParameter(ALARM_ID, strAlarmId); AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); - return tbAlarmService.unassign(alarm, getCurrentUser()); + return tbAlarmService.unassign(alarm, System.currentTimeMillis(), getCurrentUser()); } @ApiOperation(value = "Get Alarms (getAlarms)", diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index 012a5b0b8f..652529caa1 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -53,11 +53,28 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } else { result = alarmSubscriptionService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm)); } + if (!result.isSuccessful()) { + throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); + } actionType = result.isCreated() ? ActionType.ADDED : ActionType.UPDATED; if (result.isModified()) { notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), actionType, user); } - return new Alarm(result.getAlarm()); + AlarmInfo resultAlarm = result.getAlarm(); + if (alarm.isAcknowledged() && !resultAlarm.isAcknowledged()) { + resultAlarm = ack(resultAlarm, alarm.getAckTs(), user); + } + if (alarm.isCleared() && !resultAlarm.isCleared()) { + resultAlarm = clear(resultAlarm, alarm.getClearTs(), user); + } + UserId newAssignee = alarm.getAssigneeId(); + UserId curAssignee = resultAlarm.getAssigneeId(); + if (newAssignee != null && !newAssignee.equals(curAssignee)) { + resultAlarm = assign(alarm, newAssignee, alarm.getAssignTs(), user); + } else if (newAssignee == null && curAssignee != null) { + resultAlarm = unassign(alarm, alarm.getAssignTs(), user); + } + return new Alarm(resultAlarm); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ALARM), alarm, actionType, user, e); throw e; @@ -66,7 +83,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Override public AlarmInfo ack(Alarm alarm, User user) throws ThingsboardException { - AlarmApiCallResult result = alarmSubscriptionService.acknowledgeAlarm(alarm.getTenantId(), alarm.getId(), System.currentTimeMillis()); + return ack(alarm, System.currentTimeMillis(), user); + } + + @Override + public AlarmInfo ack(Alarm alarm, long ackTs, User user) throws ThingsboardException { + AlarmApiCallResult result = alarmSubscriptionService.acknowledgeAlarm(alarm.getTenantId(), alarm.getId(), getOrDefault(ackTs)); if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } @@ -89,7 +111,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Override public AlarmInfo clear(Alarm alarm, User user) throws ThingsboardException { - AlarmApiCallResult result = alarmSubscriptionService.clearAlarm(alarm.getTenantId(), alarm.getId(), System.currentTimeMillis(), null); + return clear(alarm, System.currentTimeMillis(), user); + } + + @Override + public AlarmInfo clear(Alarm alarm, long clearTs, User user) throws ThingsboardException { + AlarmApiCallResult result = alarmSubscriptionService.clearAlarm(alarm.getTenantId(), alarm.getId(), getOrDefault(clearTs), null); if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } @@ -111,8 +138,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public AlarmInfo assign(Alarm alarm, User user, UserId assigneeId) throws ThingsboardException { - AlarmApiCallResult result = alarmSubscriptionService.assignAlarm(alarm.getTenantId(), alarm.getId(), assigneeId, System.currentTimeMillis()); + public AlarmInfo assign(Alarm alarm, UserId assigneeId, long assignTs, User user) throws ThingsboardException { + AlarmApiCallResult result = alarmSubscriptionService.assignAlarm(alarm.getTenantId(), alarm.getId(), assigneeId, getOrDefault(assignTs)); if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } @@ -138,8 +165,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public AlarmInfo unassign(Alarm alarm, User user) throws ThingsboardException { - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(alarm.getTenantId(), alarm.getId(), System.currentTimeMillis()); + public AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException { + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(alarm.getTenantId(), alarm.getId(), getOrDefault(unassignTs)); if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } @@ -169,4 +196,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb relatedEdgeIds, user, JacksonUtil.toString(alarm)); return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); } + + private static long getOrDefault(long ts) { + return ts > 0 ? ts : System.currentTimeMillis(); + } } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java index e4b40f1305..a2ae9c8cc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java @@ -27,11 +27,15 @@ public interface TbAlarmService { AlarmInfo ack(Alarm alarm, User user) throws ThingsboardException; + AlarmInfo ack(Alarm alarm, long ackTs, User user) throws ThingsboardException; + AlarmInfo clear(Alarm alarm, User user) throws ThingsboardException; - AlarmInfo assign(Alarm alarm, User user, UserId assigneeId) throws ThingsboardException; + AlarmInfo clear(Alarm alarm, long clearTs, User user) throws ThingsboardException; - AlarmInfo unassign(Alarm alarm, User user) throws ThingsboardException; + AlarmInfo assign(Alarm alarm, UserId assigneeId, long assignTs, User user) throws ThingsboardException; + + AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; Boolean delete(Alarm alarm, User user); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index b7234f1008..777b97d940 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -145,6 +145,54 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED); + + alarm = updatedAlarm; + alarm.setAcknowledged(true); + alarm.setAckTs(System.currentTimeMillis() - 1000); + updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(updatedAlarm); + Assert.assertTrue(updatedAlarm.isAcknowledged()); + Assert.assertEquals(alarm.getAckTs(), updatedAlarm.getAckTs()); + + foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); + testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ACK); + + alarm = updatedAlarm; + alarm.setCleared(true); + alarm.setClearTs(System.currentTimeMillis() - 1000); + updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(updatedAlarm); + Assert.assertTrue(updatedAlarm.isCleared()); + Assert.assertEquals(alarm.getClearTs(), updatedAlarm.getClearTs()); + + foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); + testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR); + + alarm = updatedAlarm; + alarm.setAssigneeId(tenantAdminUserId); + alarm.setAssignTs(System.currentTimeMillis() - 1000); + updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(updatedAlarm); + Assert.assertEquals(tenantAdminUserId, updatedAlarm.getAssigneeId()); + Assert.assertEquals(alarm.getAssignTs(), updatedAlarm.getAssignTs()); + + foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); + testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGN); + + alarm = updatedAlarm; + alarm.setAssigneeId(null); + alarm.setAssignTs(System.currentTimeMillis() - 1000); + updatedAlarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(updatedAlarm); + Assert.assertNull(updatedAlarm.getAssigneeId()); + Assert.assertEquals(alarm.getAssignTs(), updatedAlarm.getAssignTs()); + + foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); + testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_UNASSIGN); } @Test @@ -490,7 +538,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { var response = doGetTyped( "/api/alarm/" + EntityType.DEVICE + "/" + customerDevice.getUuidId() + "?page=0&pageSize=" + size, - new TypeReference>() {} + new TypeReference>() { + } ); var foundAlarmInfos = response.getData(); Assert.assertNotNull("Found pageData is null", foundAlarmInfos); @@ -556,7 +605,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { this.token = tokens.get("token").asText(); PageData pageData = doGetTyped( - "/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=1", new TypeReference>() {} + "/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=1", new TypeReference>() { + } ); Assert.assertNotNull("Found pageData is null", pageData);