From 25778ce451f704c7f79ac235fc4c407d0de0aa95 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Tue, 1 Aug 2023 12:11:42 +0300 Subject: [PATCH 01/23] Moved unassign to async processing, added to customer controller, to unassign customer users alarms on customer removing --- .../server/controller/CustomerController.java | 26 ++++++++ .../entitiy/alarm/DefaultTbAlarmService.java | 66 +++++++++++-------- .../service/entitiy/alarm/TbAlarmService.java | 3 +- .../entitiy/user/DefaultUserService.java | 5 +- .../controller/AlarmControllerTest.java | 52 +++++++++++++++ 5 files changed, 122 insertions(+), 30 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index dd904add2e..4e351625c0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -17,6 +17,8 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; @@ -32,16 +34,21 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import org.thingsboard.server.service.entitiy.customer.TbCustomerService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.ArrayList; +import java.util.List; + import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES; @@ -64,6 +71,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI public class CustomerController extends BaseController { private final TbCustomerService tbCustomerService; + private final TbAlarmService tbAlarmService; public static final String IS_PUBLIC = "isPublic"; public static final String CUSTOMER_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the customer is owned by the same tenant. " + @@ -149,6 +157,24 @@ public class CustomerController extends BaseController { checkParameter(CUSTOMER_ID, strCustomerId); CustomerId customerId = new CustomerId(toUUID(strCustomerId)); Customer customer = checkCustomerId(customerId, Operation.DELETE); + TenantId tenantId = getTenantId(); + PageLink pl = new PageLink(100); + boolean hasNext = true; + List> futures = new ArrayList<>(); + + while (hasNext) { + PageData customerUsers = userService.findCustomerUsers(tenantId, customerId, pl); + for (User user : customerUsers.getData()) { + ListenableFuture future = tbAlarmService.unassignUserAlarms(tenantId, user, System.currentTimeMillis()); + futures.add(future); + } + hasNext = customerUsers.hasNext(); + if (hasNext) { + pl = pl.nextPageLink(); + } + } + ListenableFuture> allFutures = Futures.allAsList(futures); + Futures.getChecked(allFutures, ThingsboardException.class); tbCustomerService.delete(customer, getCurrentUser()); } 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 07c66e359a..a31fbf6a6d 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 @@ -15,6 +15,9 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -37,13 +40,11 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; 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.TimePageLink; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; @Service @AllArgsConstructor @@ -216,35 +217,44 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public void unassignUserAlarms(TenantId tenantId, User user, long unassignTs) { + public ListenableFuture unassignUserAlarms(TenantId tenantId, User user, long unassignTs) { AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build(); - try { - List alarms = alarmService.findAlarmsV2(tenantId, alarmQuery).get(30, TimeUnit.SECONDS).getData(); - for (AlarmInfo alarm : alarms) { - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs)); - if (!result.isSuccessful()) { - continue; - } - if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned because user %s - was deleted", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_UNASSIGNED, user); + ListenableFuture> foundUserAlarmsFuture = alarmService.findAlarmsV2(tenantId, alarmQuery); + FutureCallback> callback = new FutureCallback<>() { + public void onSuccess(PageData alarmsData) { + for (AlarmInfo alarm : alarmsData.getData()) { + unassignUserAlarm(tenantId, user, unassignTs, alarm); } } - } catch (InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); + public void onFailure(Throwable t) { + log.error("Cannot get alarms for user {}", user.getId(), t); + } + }; + Futures.addCallback(foundUserAlarmsFuture, callback, dbExecutor); + return Futures.transform(foundUserAlarmsFuture, alarms -> null, dbExecutor); + } + + private void unassignUserAlarm(TenantId tenantId, User user, long unassignTs, AlarmInfo alarm) { + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs)); + if (!result.isSuccessful()) { + return; + } + if (result.isModified()) { + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned because user %s - was deleted", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) + .put("userId", user.getId().toString()) + .put("subtype", "ASSIGN")) + .build(); + try { + alarmCommentService.saveAlarmComment(alarm, alarmComment, user); + } catch (ThingsboardException e) { + log.error("Failed to save alarm comment", e); + } + notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_UNASSIGNED, user); } } 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 24af185539..5a3cf642c7 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -38,7 +39,7 @@ public interface TbAlarmService { AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; - void unassignUserAlarms(TenantId tenantId, User user, long unassignTs); + ListenableFuture unassignUserAlarms(TenantId tenantId, User user, long unassignTs); Boolean delete(Alarm alarm, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index d9f11dacb5..b87f1ebf8e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.service.entitiy.user; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -82,7 +84,8 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse UserId userId = tbUser.getId(); try { - tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis()); + ListenableFuture future = tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis()); + Futures.getChecked(future, ThingsboardException.class); userService.deleteUser(tenantId, userId); notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, userId, tbUser, user, ActionType.DELETED, true, null, customerId.toString()); 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 6ce6e22e9a..18f082e54a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -625,6 +625,58 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); } + @Test + public void testUnassignAlarmOnCustomerRemoving() throws Exception { + createDifferentTenantCustomer(); + loginDifferentTenant(); + + User user = new User(); + user.setAuthority(Authority.CUSTOMER_USER); + user.setTenantId(tenantId); + user.setCustomerId(differentTenantCustomerId); + user.setEmail("customerForAssign@thingsboard.org"); + User savedUser = createUser(user, "password"); + + Device device = createDevice("Different customer device", "default", "differentTenantTest"); + + Device assignedDevice = doPost("/api/customer/" + differentTenantCustomerId.getId() + + "/device/" + device.getId().getId(), Device.class); + Assert.assertEquals(differentTenantCustomerId, assignedDevice.getCustomerId()); + + Alarm alarm = Alarm.builder() + .type(TEST_ALARM_TYPE) + .tenantId(savedDifferentTenant.getId()) + .customerId(differentTenantCustomerId) + .originator(device.getId()) + .severity(AlarmSeverity.MAJOR) + .build(); + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + + alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(alarm); + + Mockito.reset(tbClusterService, auditLogService); + long beforeAssignmentTs = System.currentTimeMillis(); + + doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + + beforeAssignmentTs = System.currentTimeMillis(); + + Mockito.reset(tbClusterService, auditLogService); + + doDelete("/api/customer/" + differentTenantCustomerId.getId()).andExpect(status().isOk()); + + foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + } + @Test public void testFindAlarmsViaCustomerUser() throws Exception { loginCustomerUser(); From ed5b528964a14a0037781452136e2f25b9c255c6 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Thu, 3 Aug 2023 10:40:50 +0300 Subject: [PATCH 02/23] Updated unassign for alarms, on user removing to be transactional --- .../server/controller/CustomerController.java | 18 ------- .../entitiy/alarm/DefaultTbAlarmService.java | 47 ------------------- .../service/entitiy/alarm/TbAlarmService.java | 4 -- .../entitiy/user/DefaultUserService.java | 4 -- .../server/dao/alarm/AlarmService.java | 3 ++ .../server/dao/alarm/AlarmDao.java | 2 + .../server/dao/alarm/BaseAlarmService.java | 32 +++++++++++++ .../server/dao/sql/alarm/AlarmRepository.java | 3 ++ .../server/dao/sql/alarm/JpaAlarmDao.java | 6 +++ .../server/dao/user/UserServiceImpl.java | 1 + 10 files changed, 47 insertions(+), 73 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index 4e351625c0..b9719f9e63 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -157,24 +157,6 @@ public class CustomerController extends BaseController { checkParameter(CUSTOMER_ID, strCustomerId); CustomerId customerId = new CustomerId(toUUID(strCustomerId)); Customer customer = checkCustomerId(customerId, Operation.DELETE); - TenantId tenantId = getTenantId(); - PageLink pl = new PageLink(100); - boolean hasNext = true; - List> futures = new ArrayList<>(); - - while (hasNext) { - PageData customerUsers = userService.findCustomerUsers(tenantId, customerId, pl); - for (User user : customerUsers.getData()) { - ListenableFuture future = tbAlarmService.unassignUserAlarms(tenantId, user, System.currentTimeMillis()); - futures.add(future); - } - hasNext = customerUsers.hasNext(); - if (hasNext) { - pl = pl.nextPageLink(); - } - } - ListenableFuture> allFutures = Futures.allAsList(futures); - Futures.getChecked(allFutures, ThingsboardException.class); tbCustomerService.delete(customer, getCurrentUser()); } 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 a31fbf6a6d..22a542b893 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 @@ -15,9 +15,6 @@ */ package org.thingsboard.server.service.entitiy.alarm; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -32,7 +29,6 @@ import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; -import org.thingsboard.server.common.data.alarm.AlarmQueryV2; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -40,8 +36,6 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; 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.TimePageLink; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.List; @@ -216,47 +210,6 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb return alarmInfo; } - @Override - public ListenableFuture unassignUserAlarms(TenantId tenantId, User user, long unassignTs) { - AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build(); - ListenableFuture> foundUserAlarmsFuture = alarmService.findAlarmsV2(tenantId, alarmQuery); - FutureCallback> callback = new FutureCallback<>() { - public void onSuccess(PageData alarmsData) { - for (AlarmInfo alarm : alarmsData.getData()) { - unassignUserAlarm(tenantId, user, unassignTs, alarm); - } - } - - public void onFailure(Throwable t) { - log.error("Cannot get alarms for user {}", user.getId(), t); - } - }; - Futures.addCallback(foundUserAlarmsFuture, callback, dbExecutor); - return Futures.transform(foundUserAlarmsFuture, alarms -> null, dbExecutor); - } - - private void unassignUserAlarm(TenantId tenantId, User user, long unassignTs, AlarmInfo alarm) { - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs)); - if (!result.isSuccessful()) { - return; - } - if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned because user %s - was deleted", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_UNASSIGNED, user); - } - } @Override public Boolean delete(Alarm alarm, User user) { 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 5a3cf642c7..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 @@ -15,12 +15,10 @@ */ package org.thingsboard.server.service.entitiy.alarm; -import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; public interface TbAlarmService { @@ -39,7 +37,5 @@ public interface TbAlarmService { AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; - ListenableFuture unassignUserAlarms(TenantId tenantId, User user, long unassignTs); - Boolean delete(Alarm alarm, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index b87f1ebf8e..85eb3bd680 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.service.entitiy.user; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -84,8 +82,6 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse UserId userId = tbUser.getId(); try { - ListenableFuture future = tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis()); - Futures.getChecked(future, ThingsboardException.class); userService.deleteUser(tenantId, userId); notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, userId, tbUser, user, ActionType.DELETED, true, null, customerId.toString()); 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..149c224201 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.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -118,6 +119,8 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); + void unassignUserAlarms(TenantId tenantId, UserId userId, long unassignTs); + void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); 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..3cfafa4ebd 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 @@ -77,6 +77,8 @@ public interface AlarmDao extends Dao { PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); + List findAlarmByAssigneeId(UUID key); + void createEntityAlarmRecord(EntityAlarm entityAlarm); List findEntityAlarmRecords(TenantId tenantId, AlarmId id); 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..438334af24 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 @@ -25,9 +25,12 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmModificationRequest; @@ -84,6 +87,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private final TenantService tenantService; private final AlarmDao alarmDao; + private final AlarmCommentDao alarmCommentDao; private final EntityService entityService; private final DataValidator alarmDataValidator; @@ -308,6 +312,28 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ return withPropagated(alarmDao.unassignAlarm(tenantId, alarmId, unassignTime)); } + @Override + public void unassignUserAlarms(TenantId tenantId, UserId userId, long unassignTs) { + List alarms = findAlarmsByAssigneeId(userId); + for (Alarm alarm : alarms) { + AlarmApiCallResult result = unassignAlarm(alarm.getTenantId(), alarm.getId(), unassignTs); + if (!result.isSuccessful()) { + continue; + } + if (result.isModified()) { + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(JacksonUtil.newObjectNode() + .put("text", "Alarm was unassigned because assigned user was deleted!") + .put("userId", userId.toString()) + .put("subtype", "ASSIGN")) + .build(); + alarmCommentDao.save(tenantId, alarmComment); + } + } + } + @Override public Alarm findAlarmById(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmById [{}]", alarmId); @@ -488,4 +514,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } } + private List findAlarmsByAssigneeId(UserId userId) { + log.trace("Executing findAlarmsByAssigneeId [{}]", userId); + validateId(userId, "Incorrect alarmId " + userId); + return alarmDao.findAlarmByAssigneeId(userId.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 92537dc850..1479b396a7 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 @@ -315,6 +315,9 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId") AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId); + @Query("SELECT a FROM AlarmEntity a WHERE a.assigneeId = :assigneeId") + List findAlarmByAssigneeId(@Param("assigneeId") UUID assigneeId); + @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + ":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true) 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..da5f6bcaf3 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 @@ -285,6 +285,12 @@ public class JpaAlarmDao extends JpaAbstractDao implements A .mapData(AlarmId::new); } + @Override + public List findAlarmByAssigneeId(UUID key) { + List assignedAlarms = alarmRepository.findAlarmByAssigneeId(key); + return DaoUtil.convertDataList(assignedAlarms); + } + @Override public void createEntityAlarmRecord(EntityAlarm entityAlarm) { log.debug("Saving entity {}", entityAlarm); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 5dc32b8eed..22433bfce8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -230,6 +230,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic public void deleteUser(TenantId tenantId, UserId userId) { log.trace("Executing deleteUser [{}]", userId); validateId(userId, INCORRECT_USER_ID + userId); + alarmService.unassignUserAlarms(tenantId, userId, System.currentTimeMillis()); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); userCredentialsDao.removeById(tenantId, userCredentials.getUuidId()); userAuthSettingsDao.removeByUserId(userId); From fc7e1d88e6bd9c5b2f2f1fcfbdae79e49d3c99e0 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Thu, 3 Aug 2023 15:15:36 +0300 Subject: [PATCH 03/23] Optimized imports --- .../thingsboard/server/controller/CustomerController.java | 7 ------- .../org/thingsboard/server/dao/alarm/AlarmService.java | 1 - 2 files changed, 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index b9719f9e63..059f313936 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -17,8 +17,6 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; @@ -34,7 +32,6 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -46,9 +43,6 @@ import org.thingsboard.server.service.entitiy.customer.TbCustomerService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.ArrayList; -import java.util.List; - import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES; @@ -71,7 +65,6 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI public class CustomerController extends BaseController { private final TbCustomerService tbCustomerService; - private final TbAlarmService tbAlarmService; public static final String IS_PUBLIC = "isPublic"; public static final String CUSTOMER_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the customer is owned by the same tenant. " + 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 149c224201..93904582b2 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,7 +17,6 @@ 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.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; From 8ba6271ce0bbd49ffc61f92b73dcfa9624141e50 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 4 Aug 2023 13:38:35 +0300 Subject: [PATCH 04/23] Added ignore for exception, that can appear if sysadmin removed --- .../server/dao/alarm/BaseAlarmService.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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 438334af24..751a5f01f3 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 @@ -321,15 +321,17 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ continue; } if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode() - .put("text", "Alarm was unassigned because assigned user was deleted!") - .put("userId", userId.toString()) - .put("subtype", "ASSIGN")) - .build(); - alarmCommentDao.save(tenantId, alarmComment); + try { + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(JacksonUtil.newObjectNode() + .put("text", "Alarm was unassigned because assigned user was deleted!") + .put("userId", userId.toString()) + .put("subtype", "ASSIGN")) + .build(); + alarmCommentDao.save(tenantId, alarmComment); + } catch (Exception ignored) {} } } } From 97d8171f0760a53139d0568f4546cc11ce6d5d77 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 9 Aug 2023 14:49:53 +0300 Subject: [PATCH 05/23] Refactored to use events --- .../entitiy/alarm/DefaultTbAlarmService.java | 74 +++++++++++-------- .../server/dao/alarm/AlarmService.java | 3 +- .../server/dao/alarm/BaseAlarmService.java | 41 ++-------- .../server/dao/user/UserServiceImpl.java | 1 - 4 files changed, 53 insertions(+), 66 deletions(-) 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 9d6d36cfc9..c241fc3317 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 @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; @@ -33,9 +34,11 @@ import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.EdgeId; +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.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.List; @@ -48,6 +51,9 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Autowired protected TbAlarmCommentService alarmCommentService; + @Autowired + protected UserService userService; + @Override public Alarm save(Alarm alarm, User user) throws ThingsboardException { ActionType actionType = alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED; @@ -217,45 +223,53 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public void unassignUserAlarms(TenantId tenantId, User user, long unassignTs) { - AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build(); - try { - List alarms = alarmService.findAlarmsV2(tenantId, alarmQuery).get(30, TimeUnit.SECONDS).getData(); - for (AlarmInfo alarm : alarms) { - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs)); - if (!result.isSuccessful()) { - continue; - } - if (result.isModified()) { + public Boolean delete(Alarm alarm, User user) { + TenantId tenantId = alarm.getTenantId(); + notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(), + ActionType.DELETED, user); + return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); + } + + @TransactionalEventListener + public void unassignDeletedUserAlarms(UserId userId) { + List alarms = alarmService.findAlarmsByAssigneeId(userId); + for (Alarm alarm : alarms) { + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(alarm.getTenantId(), alarm.getId(), System.currentTimeMillis()); + if (!result.isSuccessful()) { + continue; + } + if (result.isModified()) { + try { AlarmComment alarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned because user %s - was deleted", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) + .comment(JacksonUtil.newObjectNode() + .put("text", String.format("Alarm was unassigned because user with id %s - was deleted", + userId.toString())) + .put("userId", userId.toString()) .put("subtype", "ASSIGN")) .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(), - alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); + alarmCommentService.saveAlarmComment(alarm, alarmComment, null); + } catch (ThingsboardException e) { + log.error("Failed to save alarm comment", e); } + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(), + alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, null); } - - } catch (InterruptedException | ExecutionException | TimeoutException e) { - throw new RuntimeException(e); } } - @Override - public Boolean delete(Alarm alarm, User user) { - TenantId tenantId = alarm.getTenantId(); - notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(), - ActionType.DELETED, user); - return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(DeleteEntityEvent event) { + try { + log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event); + EntityId entityId = event.getEntityId(); + if (EntityType.USER.equals(entityId.getEntityType())) { + unassignDeletedUserAlarms((UserId) entityId); + } + } catch (Exception e) { + log.error("[{}] failed to process DeleteEntityEvent: {}", event.getTenantId(), event); + } } private static long getOrDefault(long ts) { 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 93904582b2..212f544d29 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 @@ -39,6 +39,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 { @@ -118,7 +119,7 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - void unassignUserAlarms(TenantId tenantId, UserId userId, long unassignTs); + List findAlarmsByAssigneeId(UserId userId); void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); 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 edd7295613..b7bdaa3078 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 @@ -25,12 +25,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; -import org.thingsboard.server.common.data.alarm.AlarmComment; -import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmModificationRequest; @@ -347,30 +344,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ return result; } - @Override - public void unassignUserAlarms(TenantId tenantId, UserId userId, long unassignTs) { - List alarms = findAlarmsByAssigneeId(userId); - for (Alarm alarm : alarms) { - AlarmApiCallResult result = unassignAlarm(alarm.getTenantId(), alarm.getId(), unassignTs); - if (!result.isSuccessful()) { - continue; - } - if (result.isModified()) { - try { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode() - .put("text", "Alarm was unassigned because assigned user was deleted!") - .put("userId", userId.toString()) - .put("subtype", "ASSIGN")) - .build(); - alarmCommentDao.save(tenantId, alarmComment); - } catch (Exception ignored) {} - } - } - } - @Override public Alarm findAlarmById(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmById [{}]", alarmId); @@ -412,6 +385,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ return Futures.immediateFuture(alarmDao.findCustomerAlarmsV2(tenantId, customerId, query)); } + @Override + public List findAlarmsByAssigneeId(UserId userId) { + log.trace("Executing findAlarmsByAssigneeId [{}]", userId); + validateId(userId, "Incorrect alarmId " + userId); + return alarmDao.findAlarmByAssigneeId(userId.getId()); + } + @Override public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus, String assigneeId) { @@ -550,11 +530,4 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ request.setEndTs(request.getStartTs()); } } - - private List findAlarmsByAssigneeId(UserId userId) { - log.trace("Executing findAlarmsByAssigneeId [{}]", userId); - validateId(userId, "Incorrect alarmId " + userId); - return alarmDao.findAlarmByAssigneeId(userId.getId()); - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 5f1bc605bc..5afcc49def 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -249,7 +249,6 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic public void deleteUser(TenantId tenantId, UserId userId) { log.trace("Executing deleteUser [{}]", userId); validateId(userId, INCORRECT_USER_ID + userId); - alarmService.unassignUserAlarms(tenantId, userId, System.currentTimeMillis()); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); userCredentialsDao.removeById(tenantId, userCredentials.getUuidId()); userAuthSettingsDao.removeByUserId(userId); From 057729602ed5c1f9ee7e21b417219dcb9fdd0e47 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 9 Aug 2023 16:32:48 +0300 Subject: [PATCH 06/23] Imports --- .../org/thingsboard/server/controller/CustomerController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index 059f313936..dd904add2e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -38,7 +38,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import org.thingsboard.server.service.entitiy.customer.TbCustomerService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; From 93ceb4befdb580fb2c5c2b43589d577f387e24f6 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Thu, 10 Aug 2023 07:56:09 +0300 Subject: [PATCH 07/23] Refactoring --- .../server/service/entitiy/alarm/DefaultTbAlarmService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 c241fc3317..9e4ad00ad3 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 @@ -230,8 +230,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); } - @TransactionalEventListener - public void unassignDeletedUserAlarms(UserId userId) { + private void unassignDeletedUserAlarms(UserId userId) { List alarms = alarmService.findAlarmsByAssigneeId(userId); for (Alarm alarm : alarms) { AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(alarm.getTenantId(), alarm.getId(), System.currentTimeMillis()); From bd3c249fb259f3cedf0f0579c0f804637853c21d Mon Sep 17 00:00:00 2001 From: imbeacon Date: Thu, 10 Aug 2023 08:25:04 +0300 Subject: [PATCH 08/23] Removed uneccessary import service --- .../server/service/entitiy/alarm/DefaultTbAlarmService.java | 4 ---- 1 file changed, 4 deletions(-) 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 9e4ad00ad3..9cd7b2efac 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 @@ -38,7 +38,6 @@ 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.dao.eventsourcing.DeleteEntityEvent; -import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.List; @@ -51,9 +50,6 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Autowired protected TbAlarmCommentService alarmCommentService; - @Autowired - protected UserService userService; - @Override public Alarm save(Alarm alarm, User user) throws ThingsboardException { ActionType actionType = alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED; From c9a7ca62bcd1cba27f4ac8fc92a4c889f8a37141 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Thu, 10 Aug 2023 08:33:30 +0300 Subject: [PATCH 09/23] Removed uneccessary import --- .../java/org/thingsboard/server/dao/alarm/BaseAlarmService.java | 1 - 1 file changed, 1 deletion(-) 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 b7bdaa3078..106e37f278 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 @@ -88,7 +88,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private final TenantService tenantService; private final AlarmDao alarmDao; - private final AlarmCommentDao alarmCommentDao; private final EntityService entityService; private final DataValidator alarmDataValidator; From 328d53dffc47150ccf745b88765625c59e2adc8d Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 11 Aug 2023 07:52:03 +0300 Subject: [PATCH 10/23] Changed alarms unassign method to returen ids instead of Alarm objects and changed to use entity from deleted entity event --- .../entitiy/alarm/DefaultTbAlarmService.java | 18 ++++++++++-------- .../server/dao/alarm/AlarmService.java | 2 +- .../thingsboard/server/dao/alarm/AlarmDao.java | 2 +- .../server/dao/alarm/BaseAlarmService.java | 6 +++--- .../server/dao/sql/alarm/AlarmRepository.java | 4 ++-- .../server/dao/sql/alarm/JpaAlarmDao.java | 7 ++++--- 6 files changed, 21 insertions(+), 18 deletions(-) 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 9cd7b2efac..f1f8ba038c 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 @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -226,22 +227,23 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); } - private void unassignDeletedUserAlarms(UserId userId) { - List alarms = alarmService.findAlarmsByAssigneeId(userId); - for (Alarm alarm : alarms) { - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(alarm.getTenantId(), alarm.getId(), System.currentTimeMillis()); + private void unassignDeletedUserAlarms(User user) { + List alarmIds = alarmService.findAlarmIdsByAssigneeId(user.getId()); + for (AlarmId alarmId : alarmIds) { + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, System.currentTimeMillis()); + Alarm alarm = result.getAlarm(); if (!result.isSuccessful()) { continue; } if (result.isModified()) { try { AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) + .alarmId(alarmId) .type(AlarmCommentType.SYSTEM) .comment(JacksonUtil.newObjectNode() .put("text", String.format("Alarm was unassigned because user with id %s - was deleted", - userId.toString())) - .put("userId", userId.toString()) + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) + .put("userId", user.getId().toString()) .put("subtype", "ASSIGN")) .build(); alarmCommentService.saveAlarmComment(alarm, alarmComment, null); @@ -260,7 +262,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event); EntityId entityId = event.getEntityId(); if (EntityType.USER.equals(entityId.getEntityType())) { - unassignDeletedUserAlarms((UserId) entityId); + unassignDeletedUserAlarms((User) event.getEntity()); } } catch (Exception e) { log.error("[{}] failed to process DeleteEntityEvent: {}", event.getTenantId(), event); 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 212f544d29..7bfdeaa523 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 @@ -119,7 +119,7 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - List findAlarmsByAssigneeId(UserId userId); + List findAlarmIdsByAssigneeId(UserId userId); void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); 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 3cfafa4ebd..2e8844701c 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 @@ -77,7 +77,7 @@ public interface AlarmDao extends Dao { PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); - List findAlarmByAssigneeId(UUID key); + List findAlarmIdsByAssigneeId(UUID key); void createEntityAlarmRecord(EntityAlarm entityAlarm); 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 106e37f278..f36b56ca7a 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 @@ -385,10 +385,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public List findAlarmsByAssigneeId(UserId userId) { - log.trace("Executing findAlarmsByAssigneeId [{}]", userId); + public List findAlarmIdsByAssigneeId(UserId userId) { + log.trace("Executing findAlarmIdsByAssigneeId [{}]", userId); validateId(userId, "Incorrect alarmId " + userId); - return alarmDao.findAlarmByAssigneeId(userId.getId()); + return alarmDao.findAlarmIdsByAssigneeId(userId.getId()); } @Override 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 1479b396a7..b35ca52e79 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 @@ -315,8 +315,8 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId") AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId); - @Query("SELECT a FROM AlarmEntity a WHERE a.assigneeId = :assigneeId") - List findAlarmByAssigneeId(@Param("assigneeId") UUID assigneeId); + @Query("SELECT a.id FROM AlarmEntity a WHERE a.assigneeId = :assigneeId") + List findAlarmIdsByAssigneeId(@Param("assigneeId") UUID assigneeId); @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + 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 da5f6bcaf3..045297f4ba 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 @@ -66,6 +66,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; /** * Created by Valerii Sosliuk on 5/19/2017. @@ -286,9 +287,9 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public List findAlarmByAssigneeId(UUID key) { - List assignedAlarms = alarmRepository.findAlarmByAssigneeId(key); - return DaoUtil.convertDataList(assignedAlarms); + public List findAlarmIdsByAssigneeId(UUID key) { + List assignedAlarmIds = alarmRepository.findAlarmIdsByAssigneeId(key); + return assignedAlarmIds.stream().map(AlarmId::new).collect(Collectors.toList()); } @Override From e7543d78d403dd53c71511cac5c0dcd7aa16fbeb Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 11 Aug 2023 07:56:08 +0300 Subject: [PATCH 11/23] Changed method for user removing, added user as an entity to deleted entity event --- .../service/entitiy/user/DefaultUserService.java | 4 ++-- .../org/thingsboard/server/dao/user/UserService.java | 10 +++++----- .../thingsboard/server/dao/user/UserServiceImpl.java | 10 ++++++---- .../server/dao/service/UserServiceTest.java | 12 ++++++------ 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index fde39200bd..cc026e08ad 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -64,7 +64,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse try { mailService.sendActivationEmail(activateUrl, email); } catch (ThingsboardException e) { - userService.deleteUser(tenantId, savedUser.getId()); + userService.deleteUser(tenantId, savedUser); throw e; } } @@ -82,7 +82,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse UserId userId = tbUser.getId(); try { - userService.deleteUser(tenantId, userId); + userService.deleteUser(tenantId, tbUser); notificationEntityService.logEntityAction(tenantId, userId, tbUser, customerId, actionType, user, customerId.toString()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index 47a7423191..3ef93dc2f7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -30,7 +30,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.List; public interface UserService extends EntityDaoService { - + User findUserById(TenantId tenantId, UserId userId); ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId); @@ -42,22 +42,22 @@ public interface UserService extends EntityDaoService { User saveUser(TenantId tenantId, User user); UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); - + UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken); UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken); UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); - + UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password); - + UserCredentials requestPasswordReset(TenantId tenantId, String email); UserCredentials requestExpiredPasswordReset(TenantId tenantId, UserCredentialsId userCredentialsId); UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials); - void deleteUser(TenantId tenantId, UserId userId); + void deleteUser(TenantId tenantId, User user); PageData findUsersByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 5afcc49def..295bdc4849 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -246,7 +246,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override @Transactional - public void deleteUser(TenantId tenantId, UserId userId) { + public void deleteUser(TenantId tenantId, User user) { + UserId userId = user.getId(); log.trace("Executing deleteUser [{}]", userId); validateId(userId, INCORRECT_USER_ID + userId); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); @@ -258,7 +259,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic countService.publishCountEntityEvictEvent(tenantId, EntityType.USER); eventPublisher.publishEvent(DeleteEntityEvent.builder() .tenantId(tenantId) - .entityId(userId).build()); + .entityId(userId) + .entity(user).build()); } @Override @@ -443,7 +445,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override protected void removeEntity(TenantId tenantId, User entity) { - deleteUser(tenantId, new UserId(entity.getUuidId())); + deleteUser(tenantId, entity); } }; @@ -456,7 +458,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override protected void removeEntity(TenantId tenantId, User entity) { - deleteUser(tenantId, new UserId(entity.getUuidId())); + deleteUser(tenantId, entity); } }; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java index c423147474..5a8286a449 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java @@ -135,7 +135,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals("Joe", savedUser.getFirstName()); Assert.assertEquals("Downs", savedUser.getLastName()); - userService.deleteUser(tenantId, savedUser.getId()); + userService.deleteUser(tenantId, savedUser); } @Test @@ -188,7 +188,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertNotNull(foundUser); UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId()); Assert.assertNotNull(userCredentials); - userService.deleteUser(tenantId, foundUser.getId()); + userService.deleteUser(tenantId, foundUser); userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId()); foundUser = userService.findUserById(tenantId, foundUser.getId()); Assert.assertNull(foundUser); @@ -301,7 +301,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2); for (User user : loadedTenantAdminsEmail1) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email1); @@ -310,7 +310,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(0, pageData.getData().size()); for (User user : loadedTenantAdminsEmail2) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email2); @@ -440,7 +440,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(customerUsersEmail2, loadedCustomerUsersEmail2); for (User user : loadedCustomerUsersEmail1) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email1); @@ -449,7 +449,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(0, pageData.getData().size()); for (User user : loadedCustomerUsersEmail2) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email2); From 19c5e5088403a39615dcdde61831e136bbb71163 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Tue, 15 Aug 2023 18:24:38 +0300 Subject: [PATCH 12/23] Updated due to comments --- .../entitiy/alarm/DefaultTbAlarmService.java | 35 +++++---- .../service/entitiy/alarm/TbAlarmService.java | 5 ++ .../InMemoryHouseKeeperServiceService.java | 73 +++++++++++++++++++ .../dao/housekeeper/HouseKeeperService.java | 27 +++++++ .../org/thingsboard/server/dao/DaoUtil.java | 5 ++ .../server/dao/sql/alarm/JpaAlarmDao.java | 2 +- 6 files changed, 130 insertions(+), 17 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java 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 f1f8ba038c..48e496c05b 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 @@ -39,6 +39,7 @@ 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.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.List; @@ -51,6 +52,9 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Autowired protected TbAlarmCommentService alarmCommentService; + @Autowired + private HouseKeeperService housekeeper; + @Override public Alarm save(Alarm alarm, User user) throws ThingsboardException { ActionType actionType = alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED; @@ -220,16 +224,10 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public Boolean delete(Alarm alarm, User user) { - TenantId tenantId = alarm.getTenantId(); - notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(), - ActionType.DELETED, user); - return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); - } - - private void unassignDeletedUserAlarms(User user) { + public List unassignDeletedUserAlarms(User user) { List alarmIds = alarmService.findAlarmIdsByAssigneeId(user.getId()); for (AlarmId alarmId : alarmIds) { + log.trace("[{}] Unassigning alarm {} userId {}", user.getTenantId().getId(), alarmId.getId(), user.getId().getId()); AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, System.currentTimeMillis()); Alarm alarm = result.getAlarm(); if (!result.isSuccessful()) { @@ -254,21 +252,26 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, null); } } + return alarmIds; } @TransactionalEventListener(fallbackExecution = true) public void handleEvent(DeleteEntityEvent event) { - try { - log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event); - EntityId entityId = event.getEntityId(); - if (EntityType.USER.equals(entityId.getEntityType())) { - unassignDeletedUserAlarms((User) event.getEntity()); - } - } catch (Exception e) { - log.error("[{}] failed to process DeleteEntityEvent: {}", event.getTenantId(), event); + log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event); + EntityId entityId = event.getEntityId(); + if (EntityType.USER.equals(entityId.getEntityType())) { + housekeeper.unassignDeletedUserAlarms((User) event.getEntity()); } } + @Override + public Boolean delete(Alarm alarm, User user) { + TenantId tenantId = alarm.getTenantId(); + notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(), + ActionType.DELETED, user); + return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); + } + private static long getOrDefault(long ts) { return ts > 0 ? ts : System.currentTimeMillis(); } 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 a2ae9c8cc7..c531c20c53 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 @@ -19,8 +19,11 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.UserId; +import java.util.List; + public interface TbAlarmService { Alarm save(Alarm entity, User user) throws ThingsboardException; @@ -37,5 +40,7 @@ public interface TbAlarmService { AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; + List unassignDeletedUserAlarms(User user); + Boolean delete(Alarm alarm, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java new file mode 100644 index 0000000000..2e62a68333 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java @@ -0,0 +1,73 @@ +/** + * 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.service.housekeeper; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.dao.housekeeper.HouseKeeperService; +import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +@Component +@RequiredArgsConstructor +@Slf4j +public class InMemoryHouseKeeperServiceService implements HouseKeeperService { + + @Lazy + final TbAlarmService alarmService; + + ListeningExecutorService executor; + + AtomicInteger queueSize = new AtomicInteger(); + + @PostConstruct + public void init() { + executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper"))); + } + + @PreDestroy + public void destroy() { + if (executor != null) { + executor.shutdown(); + } + } + + @Override + public ListenableFuture> unassignDeletedUserAlarms(User user) { + log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", user.getTenantId().getId(), user.getId().getId(), queueSize.get()); + queueSize.incrementAndGet(); + ListenableFuture> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(user)); + future.addListener(() -> { + queueSize.decrementAndGet(); + log.debug("[{}][{}] unassignDeletedUserAlarms finished, pending queue size: {} ", user.getTenantId().getId(), user.getId().getId(), queueSize.get()); + }, MoreExecutors.directExecutor()); + return future; + } + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java new file mode 100644 index 0000000000..3ffd309ca5 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java @@ -0,0 +1,27 @@ +/** + * 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.housekeeper; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.AlarmId; + +import java.util.List; + +public interface HouseKeeperService { + ListenableFuture> unassignDeletedUserAlarms(User user); + +} 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..9688f7b7e1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -33,6 +33,7 @@ import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; public abstract class DaoUtil { @@ -109,6 +110,10 @@ public abstract class DaoUtil { return ids; } + public static List fromUUIDs(List uuids, Function mapper) { + return uuids.stream().map(mapper).collect(Collectors.toList()); + } + public static I toEntityId(UUID uuid, Function creator) { if (uuid != null) { return creator.apply(uuid); 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 045297f4ba..ed25595442 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 @@ -289,7 +289,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Override public List findAlarmIdsByAssigneeId(UUID key) { List assignedAlarmIds = alarmRepository.findAlarmIdsByAssigneeId(key); - return assignedAlarmIds.stream().map(AlarmId::new).collect(Collectors.toList()); + return DaoUtil.fromUUIDs(assignedAlarmIds, AlarmId::new); } @Override From 4b8db9f37d182849de729cd5fb2c710ca9c6f181 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 16 Aug 2023 09:09:40 +0300 Subject: [PATCH 13/23] Refactored repository method for getting assigned alarms, added limitations, logs and adopted tests for event based model alarms unassigning on user removing --- .../entitiy/alarm/DefaultTbAlarmService.java | 64 +++++++++-------- .../service/entitiy/alarm/TbAlarmService.java | 3 +- .../InMemoryHouseKeeperServiceService.java | 34 +++++++-- .../controller/AlarmControllerTest.java | 71 ++++++++++++------- .../server/dao/alarm/AlarmService.java | 2 +- .../dao/housekeeper/HouseKeeperService.java | 4 +- .../server/dao/user/UserService.java | 20 +++--- .../server/dao/alarm/AlarmDao.java | 2 +- .../server/dao/alarm/BaseAlarmService.java | 6 +- .../server/dao/sql/alarm/AlarmRepository.java | 2 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 6 +- 11 files changed, 134 insertions(+), 80 deletions(-) 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 48e496c05b..65302222fc 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 @@ -42,6 +42,7 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import java.util.ArrayList; import java.util.List; @Service @@ -224,43 +225,50 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public List unassignDeletedUserAlarms(User user) { - List alarmIds = alarmService.findAlarmIdsByAssigneeId(user.getId()); - for (AlarmId alarmId : alarmIds) { - log.trace("[{}] Unassigning alarm {} userId {}", user.getTenantId().getId(), alarmId.getId(), user.getId().getId()); - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, System.currentTimeMillis()); - Alarm alarm = result.getAlarm(); - if (!result.isSuccessful()) { - continue; - } - if (result.isModified()) { - try { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarmId) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode() - .put("text", String.format("Alarm was unassigned because user with id %s - was deleted", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - alarmCommentService.saveAlarmComment(alarm, alarmComment, null); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); + public List unassignDeletedUserAlarms(TenantId tenantId, User user) { + List totalAlarmIds = new ArrayList<>(); + List alarmIds; + do { + alarmIds = alarmService.findAlarmIdsByAssigneeId(tenantId, user.getId(), 100); + for (AlarmId alarmId : alarmIds) { + log.trace("[{}] Unassigning alarm {} userId {}", tenantId, alarmId.getId(), user.getId().getId()); + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, System.currentTimeMillis()); + Alarm alarm = result.getAlarm(); + if (!result.isSuccessful()) { + continue; } - notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(), - alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, null); + if (result.isModified()) { + try { + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarmId) + .type(AlarmCommentType.SYSTEM) + .comment(JacksonUtil.newObjectNode() + .put("text", String.format("Alarm was unassigned because user with id %s - was deleted", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) + .put("userId", user.getId().toString()) + .put("subtype", "ASSIGN")) + .build(); + alarmCommentService.saveAlarmComment(alarm, alarmComment, null); + } catch (ThingsboardException e) { + log.error("Failed to save alarm comment", e); + } + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(), + alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, null); + } + + totalAlarmIds.addAll(alarmIds); } } - return alarmIds; + while (!alarmIds.isEmpty()); + return totalAlarmIds; } @TransactionalEventListener(fallbackExecution = true) public void handleEvent(DeleteEntityEvent event) { - log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event); + log.trace("[{}] DeleteEntityEvent handler: {}", event.getTenantId(), event); EntityId entityId = event.getEntityId(); if (EntityType.USER.equals(entityId.getEntityType())) { - housekeeper.unassignDeletedUserAlarms((User) event.getEntity()); + housekeeper.unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity()); } } 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 c531c20c53..c5eb6714ef 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 @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import java.util.List; @@ -40,7 +41,7 @@ public interface TbAlarmService { AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; - List unassignDeletedUserAlarms(User user); + List unassignDeletedUserAlarms(TenantId tenantId, User user); Boolean delete(Alarm alarm, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java index 2e62a68333..ad3bff921e 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java @@ -15,16 +15,20 @@ */ package org.thingsboard.server.service.housekeeper; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; @@ -45,27 +49,43 @@ public class InMemoryHouseKeeperServiceService implements HouseKeeperService { ListeningExecutorService executor; AtomicInteger queueSize = new AtomicInteger(); + AtomicInteger totalProcessedCounter = new AtomicInteger(); @PostConstruct public void init() { - executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper"))); + log.debug("Starting HouseKeeper service"); + executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper"))); } @PreDestroy public void destroy() { if (executor != null) { + log.debug("Stopping HouseKeeper service"); executor.shutdown(); } } @Override - public ListenableFuture> unassignDeletedUserAlarms(User user) { - log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", user.getTenantId().getId(), user.getId().getId(), queueSize.get()); + public ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user) { + log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", tenantId, user.getId().getId(), queueSize.get()); queueSize.incrementAndGet(); - ListenableFuture> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(user)); - future.addListener(() -> { - queueSize.decrementAndGet(); - log.debug("[{}][{}] unassignDeletedUserAlarms finished, pending queue size: {} ", user.getTenantId().getId(), user.getId().getId(), queueSize.get()); + ListenableFuture> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(tenantId, user)); + Futures.addCallback(future, new FutureCallback<>() { + @Override + public void onSuccess(List alarmIds) { + queueSize.decrementAndGet(); + totalProcessedCounter.incrementAndGet(); + log.debug("[{}][{}] unassignDeletedUserAlarms finished, pending queue size: {}, total processed count: {} ", + tenantId, user.getId().getId(), queueSize.get(), totalProcessedCounter.get()); + } + + @Override + public void onFailure(@NotNull Throwable throwable) { + queueSize.decrementAndGet(); + totalProcessedCounter.incrementAndGet(); + log.error("[{}][{}] unassignDeletedUserAlarms failed, pending queue size: {}, total processed count: {}", + tenantId, user.getId().getId(), queueSize.get(), totalProcessedCounter.get(), throwable); + } }, MoreExecutors.directExecutor()); return future; } 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 7cf8e05503..ea66bcfcc2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; +import org.testcontainers.shaded.org.awaitility.Awaitility; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; @@ -46,6 +47,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -568,10 +570,13 @@ public class AlarmControllerTest extends AbstractControllerTest { long beforeAssignmentTs = System.currentTimeMillis(); doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); - AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); - Assert.assertNotNull(foundAlarm); - Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); - Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + Alarm finalAlarm = alarm; + var alarmObj = new Object() { + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); + }; + Assert.assertNotNull(alarmObj.foundAlarm); + Assert.assertEquals(savedUser.getId(), alarmObj.foundAlarm.getAssigneeId()); + Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); beforeAssignmentTs = System.currentTimeMillis(); @@ -583,10 +588,14 @@ public class AlarmControllerTest extends AbstractControllerTest { loginDifferentTenant(); - foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); - Assert.assertNotNull(foundAlarm); - Assert.assertNull(foundAlarm.getAssigneeId()); - Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + alarmObj.foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); + return alarmObj.foundAlarm.getAssigneeId() == null; + }); + + Assert.assertNotNull(alarmObj.foundAlarm); + Assert.assertNull(alarmObj.foundAlarm.getAssigneeId()); + Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); } @Test @@ -617,10 +626,13 @@ public class AlarmControllerTest extends AbstractControllerTest { long beforeAssignmentTs = System.currentTimeMillis(); doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); - AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); - Assert.assertNotNull(foundAlarm); - Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); - Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + Alarm finalAlarm = alarm; + var alarmObj = new Object() { + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); + }; + Assert.assertNotNull(alarmObj.foundAlarm); + Assert.assertEquals(savedUser.getId(), alarmObj.foundAlarm.getAssigneeId()); + Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); beforeAssignmentTs = System.currentTimeMillis(); @@ -628,10 +640,14 @@ public class AlarmControllerTest extends AbstractControllerTest { doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); - foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); - Assert.assertNotNull(foundAlarm); - Assert.assertNull(foundAlarm.getAssigneeId()); - Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + alarmObj.foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); + return alarmObj.foundAlarm.getAssigneeId() == null; + }); + + Assert.assertNotNull(alarmObj.foundAlarm); + Assert.assertNull(alarmObj.foundAlarm.getAssigneeId()); + Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); } @Test @@ -669,10 +685,13 @@ public class AlarmControllerTest extends AbstractControllerTest { long beforeAssignmentTs = System.currentTimeMillis(); doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); - AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); - Assert.assertNotNull(foundAlarm); - Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); - Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + Alarm finalAlarm = alarm; + var alarmObj = new Object() { + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); + }; + Assert.assertNotNull(alarmObj.foundAlarm); + Assert.assertEquals(savedUser.getId(), alarmObj.foundAlarm.getAssigneeId()); + Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); beforeAssignmentTs = System.currentTimeMillis(); @@ -680,10 +699,14 @@ public class AlarmControllerTest extends AbstractControllerTest { doDelete("/api/customer/" + differentTenantCustomerId.getId()).andExpect(status().isOk()); - foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); - Assert.assertNotNull(foundAlarm); - Assert.assertNull(foundAlarm.getAssigneeId()); - Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { + alarmObj.foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); + return alarmObj.foundAlarm.getAssigneeId() == null; + }); + + Assert.assertNotNull(alarmObj.foundAlarm); + Assert.assertNull(alarmObj.foundAlarm.getAssigneeId()); + Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); } @Test 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 7bfdeaa523..95e46d66ff 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 @@ -119,7 +119,7 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - List findAlarmIdsByAssigneeId(UserId userId); + List findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, int limit); void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java index 3ffd309ca5..41f6136c90 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java @@ -18,10 +18,12 @@ package org.thingsboard.server.dao.housekeeper; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; import java.util.List; public interface HouseKeeperService { - ListenableFuture> unassignDeletedUserAlarms(User user); + + ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index 3ef93dc2f7..b874e038d2 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -31,27 +31,27 @@ import java.util.List; public interface UserService extends EntityDaoService { - User findUserById(TenantId tenantId, UserId userId); + User findUserById(TenantId tenantId, UserId userId); - ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId); + ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId); - User findUserByEmail(TenantId tenantId, String email); + User findUserByEmail(TenantId tenantId, String email); User findUserByTenantIdAndEmail(TenantId tenantId, String email); - User saveUser(TenantId tenantId, User user); + User saveUser(TenantId tenantId, User user); - UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); + UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); - UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken); + UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken); - UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken); + UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken); - UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); + UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); - UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password); + UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password); - UserCredentials requestPasswordReset(TenantId tenantId, String email); + UserCredentials requestPasswordReset(TenantId tenantId, String email); UserCredentials requestExpiredPasswordReset(TenantId tenantId, UserCredentialsId userCredentialsId); 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 2e8844701c..f05e7a1453 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 @@ -77,7 +77,7 @@ public interface AlarmDao extends Dao { PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); - List findAlarmIdsByAssigneeId(UUID key); + List findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, int limit); void createEntityAlarmRecord(EntityAlarm entityAlarm); 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 f36b56ca7a..542b11494a 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 @@ -385,10 +385,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public List findAlarmIdsByAssigneeId(UserId userId) { + public List findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, int limit) { log.trace("Executing findAlarmIdsByAssigneeId [{}]", userId); - validateId(userId, "Incorrect alarmId " + userId); - return alarmDao.findAlarmIdsByAssigneeId(userId.getId()); + validateId(userId, "Incorrect userId " + userId); + return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), limit); } @Override 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 b35ca52e79..1afc7a96be 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 @@ -316,7 +316,7 @@ public interface AlarmRepository extends JpaRepository { AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId); @Query("SELECT a.id FROM AlarmEntity a WHERE a.assigneeId = :assigneeId") - List findAlarmIdsByAssigneeId(@Param("assigneeId") UUID assigneeId); + List findAlarmIdsByAssigneeId(@Param("assigneeId") UUID assigneeId, Pageable pageable); @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + 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 ed25595442..0a7de4c5e8 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 @@ -66,7 +66,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; /** * Created by Valerii Sosliuk on 5/19/2017. @@ -287,8 +286,9 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public List findAlarmIdsByAssigneeId(UUID key) { - List assignedAlarmIds = alarmRepository.findAlarmIdsByAssigneeId(key); + public List findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, int limit) { + log.debug("[{}] findAlarmIdsByAssigneeId [{}] limit {}", tenantId, userId, limit); + List assignedAlarmIds = alarmRepository.findAlarmIdsByAssigneeId(userId, PageRequest.of(0, limit)); return DaoUtil.fromUUIDs(assignedAlarmIds, AlarmId::new); } From f722ad0757628db09db39d7d19c291a96bb0cd1b Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 16 Aug 2023 10:52:15 +0300 Subject: [PATCH 14/23] Updated test with HouseKeeper service --- .../service/entitiy/alarm/DefaultTbAlarmServiceTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java index 229ea5d9f9..19b8c0bc34 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; @@ -74,6 +75,8 @@ public class DefaultTbAlarmServiceTest { protected TbClusterService tbClusterService; @MockBean private EntitiesVersionControlService vcService; + @MockBean + protected HouseKeeperService houseKeeperService; @SpyBean DefaultTbAlarmService service; From 35b86999b1338a0795ad470d98ac463e76389639 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 16 Aug 2023 11:18:31 +0300 Subject: [PATCH 15/23] Added timestamp to entity deleted event, to have correct assign ts in alarms after unassignment --- .../server/service/entitiy/alarm/DefaultTbAlarmService.java | 6 +++--- .../server/service/entitiy/alarm/TbAlarmService.java | 2 +- .../housekeeper/InMemoryHouseKeeperServiceService.java | 4 ++-- .../server/dao/housekeeper/HouseKeeperService.java | 2 +- .../server/dao/eventsourcing/DeleteEntityEvent.java | 3 +++ 5 files changed, 10 insertions(+), 7 deletions(-) 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 65302222fc..e250cca7bc 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 @@ -225,14 +225,14 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } @Override - public List unassignDeletedUserAlarms(TenantId tenantId, User user) { + public List unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) { List totalAlarmIds = new ArrayList<>(); List alarmIds; do { alarmIds = alarmService.findAlarmIdsByAssigneeId(tenantId, user.getId(), 100); for (AlarmId alarmId : alarmIds) { log.trace("[{}] Unassigning alarm {} userId {}", tenantId, alarmId.getId(), user.getId().getId()); - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, System.currentTimeMillis()); + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, unassignTs); Alarm alarm = result.getAlarm(); if (!result.isSuccessful()) { continue; @@ -268,7 +268,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb log.trace("[{}] DeleteEntityEvent handler: {}", event.getTenantId(), event); EntityId entityId = event.getEntityId(); if (EntityType.USER.equals(entityId.getEntityType())) { - housekeeper.unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity()); + housekeeper.unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity(), event.getTs()); } } 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 c5eb6714ef..24af55f38b 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 @@ -41,7 +41,7 @@ public interface TbAlarmService { AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; - List unassignDeletedUserAlarms(TenantId tenantId, User user); + List unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs); Boolean delete(Alarm alarm, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java index ad3bff921e..1e4d23ac15 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java @@ -66,10 +66,10 @@ public class InMemoryHouseKeeperServiceService implements HouseKeeperService { } @Override - public ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user) { + public ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) { log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", tenantId, user.getId().getId(), queueSize.get()); queueSize.incrementAndGet(); - ListenableFuture> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(tenantId, user)); + ListenableFuture> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(tenantId, user, unassignTs)); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(List alarmIds) { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java index 41f6136c90..62f53e0f41 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java @@ -24,6 +24,6 @@ import java.util.List; public interface HouseKeeperService { - ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user); + ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java index 6277b7910b..7d0499e8aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java +++ b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java @@ -28,4 +28,7 @@ public class DeleteEntityEvent { private final EntityId entityId; private final EdgeId edgeId; private final T entity; + + @Builder.Default + private final long ts = System.currentTimeMillis(); } From 1ce4214b79c9e7dd59ecf6ad0692fb854cadef51 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Wed, 16 Aug 2023 14:21:22 +0300 Subject: [PATCH 16/23] Added test for DeleteEntityEvent --- .../eventsourcing/DeleteEntityEventTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java diff --git a/dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java b/dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java new file mode 100644 index 0000000000..39d228ceac --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java @@ -0,0 +1,45 @@ +/** + * 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.eventsourcing; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.byLessThan; + +class DeleteEntityEventTest { + + @Test + void testBuilderDefaultTs() { + assertThat(DeleteEntityEvent.builder().build().getTs()) + .isCloseTo(System.currentTimeMillis(), byLessThan(TimeUnit.MINUTES.toMillis(1))); + + assertThat(DeleteEntityEvent.builder().ts(Long.MIN_VALUE).build().getTs()) + .isEqualTo(Long.MIN_VALUE); + assertThat(DeleteEntityEvent.builder().ts(Long.MAX_VALUE).build().getTs()) + .isEqualTo(Long.MAX_VALUE); + assertThat(DeleteEntityEvent.builder().ts(-1L).build().getTs()) + .isEqualTo(-1L); + assertThat(DeleteEntityEvent.builder().ts(0L).build().getTs()) + .isEqualTo(0L); + + assertThat(DeleteEntityEvent.builder().ts(1692175215000L).build().getTs()) + .isEqualTo(1692175215000L); + } + +} \ No newline at end of file From c4d12c6ba9e0c3ef2822e27271a900c47f80c8f7 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 18 Aug 2023 12:03:27 +0300 Subject: [PATCH 17/23] Refactored DefaultTbAlarmService, added improvements for alarm unassignment on user removing, updated namings and tests. --- .../entitiy/alarm/DefaultTbAlarmService.java | 154 ++++++++---------- .../entitiy/user/DefaultUserService.java | 12 +- .../service/entitiy/user/TbUserService.java | 2 +- .../controller/AlarmControllerTest.java | 92 +++++------ .../server/dao/alarm/AlarmService.java | 4 +- .../server/dao/alarm/AlarmDao.java | 2 +- .../server/dao/alarm/BaseAlarmService.java | 7 +- .../server/dao/sql/alarm/AlarmRepository.java | 5 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 7 +- .../server/dao/user/UserServiceImpl.java | 4 +- 10 files changed, 128 insertions(+), 161 deletions(-) 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 e250cca7bc..5d69007884 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 @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +39,9 @@ import org.thingsboard.server.common.data.id.AlarmId; 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.page.SortOrder; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @@ -108,19 +112,9 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was acknowledged by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ACK")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } + String systemComment = String.format("Alarm was acknowledged by user %s", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + addSystemAlarmComment(alarmInfo, user, "ACK", systemComment); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user); } else { @@ -142,19 +136,9 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isCleared()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was cleared by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "CLEAR")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } + String systemComment = String.format("Alarm was cleared by user %s", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user); } else { @@ -172,21 +156,10 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { AlarmAssignee assignee = alarmInfo.getAssignee(); - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was assigned by user %s to user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName(), - (assignee.getFirstName() == null || assignee.getLastName() == null) ? assignee.getEmail() : assignee.getFirstName() + " " + assignee.getLastName())) - .put("userId", user.getId().toString()) - .put("assigneeId", assignee.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } + String systemComment = String.format("Alarm was assigned by user %s to user %s", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName(), + (assignee.getFirstName() == null || assignee.getLastName() == null) ? assignee.getEmail() : assignee.getFirstName() + " " + assignee.getLastName()); + addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId()); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user); } else { @@ -203,19 +176,9 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } + String systemComment = String.format("Alarm was unassigned by user %s", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); } else { @@ -227,39 +190,17 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Override public List unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) { List totalAlarmIds = new ArrayList<>(); - List alarmIds; - do { - alarmIds = alarmService.findAlarmIdsByAssigneeId(tenantId, user.getId(), 100); - for (AlarmId alarmId : alarmIds) { - log.trace("[{}] Unassigning alarm {} userId {}", tenantId, alarmId.getId(), user.getId().getId()); - AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, unassignTs); - Alarm alarm = result.getAlarm(); - if (!result.isSuccessful()) { - continue; - } - if (result.isModified()) { - try { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarmId) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode() - .put("text", String.format("Alarm was unassigned because user with id %s - was deleted", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - alarmCommentService.saveAlarmComment(alarm, alarmComment, null); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(), - alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, null); - } - - totalAlarmIds.addAll(alarmIds); + PageLink pageLink = new PageLink(100, 0, null, new SortOrder("id", SortOrder.Direction.ASC)); + while (true) { + PageData pageData = alarmService.findAlarmIdsByAssigneeId(user.getTenantId(), user.getId(), pageLink); + List alarmIds = pageData.getData(); + if (alarmIds.isEmpty()) { + break; } + processAlarmsUnassignment(tenantId, user, alarmIds, unassignTs); + totalAlarmIds.addAll(alarmIds); + pageLink = pageLink.nextPageLink(); } - while (!alarmIds.isEmpty()); return totalAlarmIds; } @@ -283,4 +224,47 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb private static long getOrDefault(long ts) { return ts > 0 ? ts : System.currentTimeMillis(); } + + private void processAlarmsUnassignment(TenantId tenantId, User user, List alarmIds, long unassignTs) { + for (AlarmId alarmId : alarmIds) { + log.trace("[{}] Unassigning alarm {} userId {}", tenantId, alarmId, user.getId()); + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, unassignTs); + if (!result.isSuccessful()) { + log.error("[{}] Cannot unassign alarm {} userId {}", tenantId, alarmId, user.getId()); + continue; + } + if (result.isModified()) { + String comment = String.format("Alarm was unassigned because user %s - was deleted", + (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + addSystemAlarmComment(result.getAlarm(), null, "ASSIGN", comment); + notificationEntityService.logEntityAction(result.getAlarm().getTenantId(), result.getAlarm().getOriginator(), result.getAlarm(), result.getAlarm().getCustomerId(), ActionType.ALARM_UNASSIGNED, null); + } + } + } + + private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText) { + addSystemAlarmComment(alarm, user, subType, commentText, null); + } + + private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText, UserId assigneeId) { + ObjectNode commentNode = JacksonUtil.newObjectNode(); + commentNode.put("text", commentText) + .put("subtype", subType); + if (user != null) { + commentNode.put("userId", user.getId().getId().toString()); + } + if (assigneeId != null) { + commentNode.put("assigneeId", assigneeId.getId().toString()); + } + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(JacksonUtil.newObjectNode()) + .build(); + try { + alarmCommentService.saveAlarmComment(alarm, alarmComment, user); + } catch (ThingsboardException e) { + log.error("Failed to save alarm comment", e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index cc026e08ad..2c2db7c6c7 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import org.thingsboard.server.service.security.system.SystemSecurityService; import javax.servlet.http.HttpServletRequest; @@ -44,7 +43,6 @@ import static org.thingsboard.server.controller.UserController.ACTIVATE_URL_PATT public class DefaultUserService extends AbstractTbEntityService implements TbUserService { private final UserService userService; - private final TbAlarmService tbAlarmService; private final MailService mailService; private final SystemSecurityService systemSecurityService; @@ -77,16 +75,16 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse } @Override - public void delete(TenantId tenantId, CustomerId customerId, User tbUser, User user) throws ThingsboardException { + public void delete(TenantId tenantId, CustomerId customerId, User user, User responsibleUser) throws ThingsboardException { ActionType actionType = ActionType.DELETED; - UserId userId = tbUser.getId(); + UserId userId = user.getId(); try { - userService.deleteUser(tenantId, tbUser); - notificationEntityService.logEntityAction(tenantId, userId, tbUser, customerId, actionType, user, customerId.toString()); + userService.deleteUser(tenantId, user); + notificationEntityService.logEntityAction(tenantId, userId, user, customerId, actionType, responsibleUser, customerId.toString()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), - actionType, user, e, userId.toString()); + actionType, responsibleUser, e, userId.toString()); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java index 3ef79371d4..0764425116 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java @@ -25,5 +25,5 @@ import javax.servlet.http.HttpServletRequest; public interface TbUserService { User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, User user) throws ThingsboardException; - void delete(TenantId tenantId, CustomerId customerId, User tbUser, User user) throws ThingsboardException; + void delete(TenantId tenantId, CustomerId customerId, User user, User responsibleUser) throws ThingsboardException; } 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 ea66bcfcc2..7ee9ffb458 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -47,6 +47,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.containsString; @@ -565,22 +566,18 @@ public class AlarmControllerTest extends AbstractControllerTest { alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(alarm); + AlarmId alarmId = alarm.getId(); - Mockito.reset(tbClusterService, auditLogService); long beforeAssignmentTs = System.currentTimeMillis(); - doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); - Alarm finalAlarm = alarm; - var alarmObj = new Object() { - AlarmInfo foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); - }; - Assert.assertNotNull(alarmObj.foundAlarm); - Assert.assertEquals(savedUser.getId(), alarmObj.foundAlarm.getAssigneeId()); - Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); + doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); - beforeAssignmentTs = System.currentTimeMillis(); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); - Mockito.reset(tbClusterService, auditLogService); + beforeAssignmentTs = System.currentTimeMillis(); loginSysAdmin(); @@ -588,14 +585,12 @@ public class AlarmControllerTest extends AbstractControllerTest { loginDifferentTenant(); - Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { - alarmObj.foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); - return alarmObj.foundAlarm.getAssigneeId() == null; - }); + foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull); - Assert.assertNotNull(alarmObj.foundAlarm); - Assert.assertNull(alarmObj.foundAlarm.getAssigneeId()); - Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); } @Test @@ -618,36 +613,27 @@ public class AlarmControllerTest extends AbstractControllerTest { .build(); alarm = doPost("/api/alarm", alarm, Alarm.class); Assert.assertNotNull(alarm); - - alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + AlarmId alarmId = alarm.getId(); + alarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); Assert.assertNotNull(alarm); - - Mockito.reset(tbClusterService, auditLogService); long beforeAssignmentTs = System.currentTimeMillis(); - doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); - Alarm finalAlarm = alarm; - var alarmObj = new Object() { - AlarmInfo foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); - }; - Assert.assertNotNull(alarmObj.foundAlarm); - Assert.assertEquals(savedUser.getId(), alarmObj.foundAlarm.getAssigneeId()); - Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); + doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); beforeAssignmentTs = System.currentTimeMillis(); - Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); - Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { - alarmObj.foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); - return alarmObj.foundAlarm.getAssigneeId() == null; - }); + foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull); - Assert.assertNotNull(alarmObj.foundAlarm); - Assert.assertNull(alarmObj.foundAlarm.getAssigneeId()); - Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); } @Test @@ -677,21 +663,19 @@ public class AlarmControllerTest extends AbstractControllerTest { .build(); alarm = doPost("/api/alarm", alarm, Alarm.class); Assert.assertNotNull(alarm); + AlarmId alarmId = alarm.getId(); - alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + alarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); Assert.assertNotNull(alarm); Mockito.reset(tbClusterService, auditLogService); long beforeAssignmentTs = System.currentTimeMillis(); - doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); - Alarm finalAlarm = alarm; - var alarmObj = new Object() { - AlarmInfo foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); - }; - Assert.assertNotNull(alarmObj.foundAlarm); - Assert.assertEquals(savedUser.getId(), alarmObj.foundAlarm.getAssigneeId()); - Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); + doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); beforeAssignmentTs = System.currentTimeMillis(); @@ -699,14 +683,12 @@ public class AlarmControllerTest extends AbstractControllerTest { doDelete("/api/customer/" + differentTenantCustomerId.getId()).andExpect(status().isOk()); - Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> { - alarmObj.foundAlarm = doGet("/api/alarm/info/" + finalAlarm.getId(), AlarmInfo.class); - return alarmObj.foundAlarm.getAssigneeId() == null; - }); + foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull); - Assert.assertNotNull(alarmObj.foundAlarm); - Assert.assertNull(alarmObj.foundAlarm.getAssigneeId()); - Assert.assertTrue(alarmObj.foundAlarm.getAssignTs() >= beforeAssignmentTs); + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); } @Test 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 95e46d66ff..558a65e8f4 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 @@ -33,13 +33,13 @@ 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; public interface AlarmService extends EntityDaoService { @@ -119,7 +119,7 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); - List findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, int limit); + PageData findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink); void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); 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 f05e7a1453..93ebc61581 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 @@ -77,7 +77,7 @@ public interface AlarmDao extends Dao { PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); - List findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, int limit); + PageData findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink); void createEntityAlarmRecord(EntityAlarm entityAlarm); 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 542b11494a..b26daba312 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 @@ -48,6 +48,7 @@ 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.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; @@ -385,10 +386,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public List findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, int limit) { - log.trace("Executing findAlarmIdsByAssigneeId [{}]", userId); + public PageData findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) { + log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId); validateId(userId, "Incorrect userId " + userId); - return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), limit); + return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink); } @Override 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 1afc7a96be..7db3ea24f8 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 @@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.sql.AlarmEntity; import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; @@ -315,8 +316,8 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId") AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId); - @Query("SELECT a.id FROM AlarmEntity a WHERE a.assigneeId = :assigneeId") - List findAlarmIdsByAssigneeId(@Param("assigneeId") UUID assigneeId, Pageable pageable); + @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.assigneeId = :assigneeId") + Page findAlarmIdsByAssigneeId(@Param("tenantId") UUID tenantId, @Param("assigneeId") UUID assigneeId, Pageable pageable); @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + 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 0a7de4c5e8..b6a1d0c92e 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 @@ -286,10 +286,9 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public List findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, int limit) { - log.debug("[{}] findAlarmIdsByAssigneeId [{}] limit {}", tenantId, userId, limit); - List assignedAlarmIds = alarmRepository.findAlarmIdsByAssigneeId(userId, PageRequest.of(0, limit)); - return DaoUtil.fromUUIDs(assignedAlarmIds, AlarmId::new); + public PageData findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink) { + return DaoUtil.pageToPageData(alarmRepository.findAlarmIdsByAssigneeId(tenantId.getId(), userId, DaoUtil.toPageable(pageLink))) + .mapData(AlarmId::new); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 295bdc4849..477610f204 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -55,6 +55,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import static org.thingsboard.server.common.data.StringUtils.generateSafeToken; @@ -247,8 +248,9 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override @Transactional public void deleteUser(TenantId tenantId, User user) { + Objects.requireNonNull(user, "User is null"); UserId userId = user.getId(); - log.trace("Executing deleteUser [{}]", userId); + log.trace("[{}] Executing deleteUser [{}]", tenantId, userId); validateId(userId, INCORRECT_USER_ID + userId); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); userCredentialsDao.removeById(tenantId, userCredentials.getUuidId()); From 4bfe4395f60ed221cbe1751094ec9628cf3a13b1 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 18 Aug 2023 12:47:21 +0300 Subject: [PATCH 18/23] Refactored user represantation for system comments in alarms --- .../entitiy/alarm/DefaultTbAlarmService.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) 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 5d69007884..d1e3c19e2e 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 @@ -112,8 +112,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - String systemComment = String.format("Alarm was acknowledged by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + String systemComment = String.format("Alarm was acknowledged by user %s", user.getTitle()); addSystemAlarmComment(alarmInfo, user, "ACK", systemComment); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user); @@ -136,8 +135,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isCleared()) { - String systemComment = String.format("Alarm was cleared by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + String systemComment = String.format("Alarm was cleared by user %s", user.getTitle()); addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user); @@ -156,8 +154,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { AlarmAssignee assignee = alarmInfo.getAssignee(); - String systemComment = String.format("Alarm was assigned by user %s to user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName(), + String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), (assignee.getFirstName() == null || assignee.getLastName() == null) ? assignee.getEmail() : assignee.getFirstName() + " " + assignee.getLastName()); addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId()); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, @@ -176,8 +173,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - String systemComment = String.format("Alarm was unassigned by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + String systemComment = String.format("Alarm was unassigned by user %s", user.getTitle()); addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); @@ -234,8 +230,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb continue; } if (result.isModified()) { - String comment = String.format("Alarm was unassigned because user %s - was deleted", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()); + String comment = String.format("Alarm was unassigned because user %s - was deleted", user.getTitle()); addSystemAlarmComment(result.getAlarm(), null, "ASSIGN", comment); notificationEntityService.logEntityAction(result.getAlarm().getTenantId(), result.getAlarm().getOriginator(), result.getAlarm(), result.getAlarm().getCustomerId(), ActionType.ALARM_UNASSIGNED, null); } From 64e60d43f01744255fbc27620c60f0e657695255 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 18 Aug 2023 14:06:08 +0300 Subject: [PATCH 19/23] Moved event listener to HouseKeeper service and added method to get representation from AlarmAssignee --- .../entitiy/alarm/DefaultTbAlarmService.java | 15 +------------ .../InMemoryHouseKeeperServiceService.java | 13 ++++++++++++ .../common/data/alarm/AlarmAssignee.java | 21 +++++++++++++++++++ 3 files changed, 35 insertions(+), 14 deletions(-) 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 d1e3c19e2e..0e02d1ccd3 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 @@ -20,7 +20,6 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; @@ -36,13 +35,11 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AlarmId; -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.page.SortOrder; -import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @@ -154,8 +151,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { AlarmAssignee assignee = alarmInfo.getAssignee(); - String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), - (assignee.getFirstName() == null || assignee.getLastName() == null) ? assignee.getEmail() : assignee.getFirstName() + " " + assignee.getLastName()); + String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), assignee.getTitle()); addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId()); notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user); @@ -200,15 +196,6 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb return totalAlarmIds; } - @TransactionalEventListener(fallbackExecution = true) - public void handleEvent(DeleteEntityEvent event) { - log.trace("[{}] DeleteEntityEvent handler: {}", event.getTenantId(), event); - EntityId entityId = event.getEntityId(); - if (EntityType.USER.equals(entityId.getEntityType())) { - housekeeper.unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity(), event.getTs()); - } - } - @Override public Boolean delete(Alarm alarm, User user) { TenantId tenantId = alarm.getTenantId(); diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java index 1e4d23ac15..b281452995 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java @@ -25,10 +25,14 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; @@ -65,6 +69,15 @@ public class InMemoryHouseKeeperServiceService implements HouseKeeperService { } } + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(DeleteEntityEvent event) { + log.trace("[{}] DeleteEntityEvent handler: {}", event.getTenantId(), event); + EntityId entityId = event.getEntityId(); + if (EntityType.USER.equals(entityId.getEntityType())) { + unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity(), event.getTs()); + } + } + @Override public ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) { log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", tenantId, user.getId().getId(), queueSize.get()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java index 20ed7b549c..43680242e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.alarm; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -22,6 +23,8 @@ import org.thingsboard.server.common.data.id.UserId; import java.io.Serializable; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + @Builder @AllArgsConstructor @Data @@ -34,4 +37,22 @@ public class AlarmAssignee implements Serializable { private final String lastName; private final String email; + @JsonIgnore + public String getTitle() { + String title = ""; + if (isNotEmpty(firstName)) { + title += firstName; + } + if (isNotEmpty(lastName)) { + if (!title.isEmpty()) { + title += " "; + } + title += lastName; + } + if (title.isEmpty()) { + title = email; + } + return title; + } + } From 853dfed14cb46e4b8d0b68f4475ccf2e9e985820 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 18 Aug 2023 14:12:19 +0300 Subject: [PATCH 20/23] Removed unnecessary imports --- .../server/service/entitiy/alarm/DefaultTbAlarmService.java | 4 ---- .../housekeeper/InMemoryHouseKeeperServiceService.java | 2 -- 2 files changed, 6 deletions(-) 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 0e02d1ccd3..91223ef69d 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 @@ -40,7 +40,6 @@ 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.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.ArrayList; @@ -54,9 +53,6 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb @Autowired protected TbAlarmCommentService alarmCommentService; - @Autowired - private HouseKeeperService housekeeper; - @Override public Alarm save(Alarm alarm, User user) throws ThingsboardException { ActionType actionType = alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED; diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java index b281452995..4e83aa0060 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java @@ -23,7 +23,6 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -47,7 +46,6 @@ import java.util.concurrent.atomic.AtomicInteger; @Slf4j public class InMemoryHouseKeeperServiceService implements HouseKeeperService { - @Lazy final TbAlarmService alarmService; ListeningExecutorService executor; From 5e9bfa32f5c4850f9c2c9707056696dcb43fe0d4 Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 18 Aug 2023 14:26:22 +0300 Subject: [PATCH 21/23] Updated import for Awaitility --- .../org/thingsboard/server/controller/AlarmControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 7ee9ffb458..778a97d690 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -29,7 +30,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; -import org.testcontainers.shaded.org.awaitility.Awaitility; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; From 38f255ea0d8f84770b35c99a5b1fe6200a2b34ad Mon Sep 17 00:00:00 2001 From: imbeacon Date: Fri, 18 Aug 2023 14:56:56 +0300 Subject: [PATCH 22/23] Removed not required service mock --- .../service/entitiy/alarm/DefaultTbAlarmServiceTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java index 19b8c0bc34..0318360a91 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java @@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.edge.EdgeService; -import org.thingsboard.server.dao.housekeeper.HouseKeeperService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; @@ -75,9 +74,6 @@ public class DefaultTbAlarmServiceTest { protected TbClusterService tbClusterService; @MockBean private EntitiesVersionControlService vcService; - @MockBean - protected HouseKeeperService houseKeeperService; - @SpyBean DefaultTbAlarmService service; From 08f26bad5b502d406ff5d343093c832183ba3dd6 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 5 Sep 2023 12:43:38 +0300 Subject: [PATCH 23/23] fixed addSystemAlarmComment method --- .../entitiy/alarm/DefaultTbAlarmService.java | 2 +- .../alarm/DefaultTbAlarmServiceTest.java | 66 +++++++++++++++++++ .../common/data/alarm/AlarmComment.java | 2 + 3 files changed, 69 insertions(+), 1 deletion(-) 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 91223ef69d..bd09b04a02 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 @@ -237,7 +237,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmComment alarmComment = AlarmComment.builder() .alarmId(alarm.getId()) .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode()) + .comment(commentNode) .build(); try { alarmCommentService.saveAlarmComment(alarm, alarmComment, user); diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java index 0318360a91..b3e8941364 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,14 +24,20 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AlarmId; +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.dao.alarm.AlarmService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.edge.EdgeService; @@ -39,6 +46,8 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; +import java.util.Collections; +import java.util.List; import java.util.UUID; import static org.mockito.ArgumentMatchers.any; @@ -123,4 +132,61 @@ public class DefaultTbAlarmServiceTest { verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.DELETED), any()); verify(alarmSubscriptionService, times(1)).deleteAlarm(any(), any()); } + + @Test + public void testUnassignAlarm() throws ThingsboardException { + AlarmInfo alarm = new AlarmInfo(); + alarm.setId(new AlarmId(UUID.randomUUID())); + when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong())) + .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(alarm).build()); + + User user = new User(); + user.setEmail("testEmail@gmail.com"); + user.setId(new UserId(UUID.randomUUID())); + service.unassign(new Alarm(), 0L, user); + + ObjectNode commentNode = JacksonUtil.newObjectNode(); + commentNode.put("subtype", "ASSIGN"); + commentNode.put("text", "Alarm was unassigned by user " + user.getTitle()); + commentNode.put("userId", user.getId().getId().toString()); + AlarmComment expectedAlarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(commentNode) + .build(); + + verify(alarmCommentService, times(1)) + .saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(user)); + } + + @Test + public void testUnassignDeletedUserAlarms() throws ThingsboardException { + AlarmInfo alarm = new AlarmInfo(); + alarm.setId(new AlarmId(UUID.randomUUID())); + + when(alarmService.findAlarmIdsByAssigneeId(any(), any(), any())) + .thenReturn(new PageData<>(List.of(alarm.getId()), 0, 1, false)) + .thenReturn(new PageData<>(Collections.EMPTY_LIST, 0, 0, false)); + when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong())) + .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(alarm).build()); + + User user = new User(); + user.setEmail("testEmail@gmail.com"); + user.setId(new UserId(UUID.randomUUID())); + service.unassignDeletedUserAlarms(new TenantId(UUID.randomUUID()), user, System.currentTimeMillis()); + + ObjectNode commentNode = JacksonUtil.newObjectNode(); + commentNode.put("subtype", "ASSIGN"); + commentNode.put("text", String.format("Alarm was unassigned because user %s - was deleted", user.getTitle())); + AlarmComment expectedAlarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(commentNode) + .build(); + + verify(alarmCommentService, times(1)) + .saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(null)); + } + + } 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 982e0f94d1..76ea06d27a 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 @@ -22,6 +22,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.AlarmCommentId; @@ -44,6 +45,7 @@ public class AlarmComment extends BaseData implements HasName { @ApiModelProperty(position = 6, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode") @NoXss @Length(fieldName = "comment", max = 10000) + @EqualsAndHashCode.Include private transient JsonNode comment; @ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " +