Merge pull request #9035 from imbeacon/customer-removing-alarm-unassign
Fix for alarm unassign on user or customer removed
This commit is contained in:
		
						commit
						db663cb5bc
					
				@ -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;
 | 
			
		||||
@ -29,20 +30,20 @@ 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;
 | 
			
		||||
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.TimePageLink;
 | 
			
		||||
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.service.entitiy.AbstractTbEntityService;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.concurrent.ExecutionException;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.concurrent.TimeoutException;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@ -104,19 +105,8 @@ 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.getTitle());
 | 
			
		||||
            addSystemAlarmComment(alarmInfo, user, "ACK", systemComment);
 | 
			
		||||
            notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
 | 
			
		||||
                    alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user);
 | 
			
		||||
        } else {
 | 
			
		||||
@ -138,19 +128,8 @@ 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.getTitle());
 | 
			
		||||
            addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment);
 | 
			
		||||
            notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
 | 
			
		||||
                    alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user);
 | 
			
		||||
        } else {
 | 
			
		||||
@ -168,21 +147,8 @@ 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.getTitle(), assignee.getTitle());
 | 
			
		||||
            addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId());
 | 
			
		||||
            notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
 | 
			
		||||
                    alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user);
 | 
			
		||||
        } else {
 | 
			
		||||
@ -199,19 +165,8 @@ 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.getTitle());
 | 
			
		||||
            addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment);
 | 
			
		||||
            notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
 | 
			
		||||
                    alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user);
 | 
			
		||||
        } else {
 | 
			
		||||
@ -221,37 +176,20 @@ 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<AlarmInfo> 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.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(),
 | 
			
		||||
                            alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, user);
 | 
			
		||||
                }
 | 
			
		||||
    public List<AlarmId> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) {
 | 
			
		||||
        List<AlarmId> totalAlarmIds = new ArrayList<>();
 | 
			
		||||
        PageLink pageLink = new PageLink(100, 0, null, new SortOrder("id", SortOrder.Direction.ASC));
 | 
			
		||||
        while (true) {
 | 
			
		||||
            PageData<AlarmId> pageData = alarmService.findAlarmIdsByAssigneeId(user.getTenantId(), user.getId(), pageLink);
 | 
			
		||||
            List<AlarmId> alarmIds = pageData.getData();
 | 
			
		||||
            if (alarmIds.isEmpty()) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
            processAlarmsUnassignment(tenantId, user, alarmIds, unassignTs);
 | 
			
		||||
            totalAlarmIds.addAll(alarmIds);
 | 
			
		||||
            pageLink = pageLink.nextPageLink();
 | 
			
		||||
        }
 | 
			
		||||
        return totalAlarmIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -265,4 +203,46 @@ 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<AlarmId> 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.getTitle());
 | 
			
		||||
                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(commentNode)
 | 
			
		||||
                .build();
 | 
			
		||||
        try {
 | 
			
		||||
            alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
 | 
			
		||||
        } catch (ThingsboardException e) {
 | 
			
		||||
            log.error("Failed to save alarm comment", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,12 @@ 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.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.UserId;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public interface TbAlarmService {
 | 
			
		||||
 | 
			
		||||
    Alarm save(Alarm entity, User user) throws ThingsboardException;
 | 
			
		||||
@ -38,7 +41,7 @@ public interface TbAlarmService {
 | 
			
		||||
 | 
			
		||||
    AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException;
 | 
			
		||||
 | 
			
		||||
    void unassignUserAlarms(TenantId tenantId, User user, long unassignTs);
 | 
			
		||||
    List<AlarmId> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs);
 | 
			
		||||
 | 
			
		||||
    Boolean delete(Alarm alarm, User user);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -64,7 +62,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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -77,17 +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 {
 | 
			
		||||
            tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis());
 | 
			
		||||
            userService.deleteUser(tenantId, userId);
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,104 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.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.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;
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
 | 
			
		||||
    final TbAlarmService alarmService;
 | 
			
		||||
 | 
			
		||||
    ListeningExecutorService executor;
 | 
			
		||||
 | 
			
		||||
    AtomicInteger queueSize = new AtomicInteger();
 | 
			
		||||
    AtomicInteger totalProcessedCounter = new AtomicInteger();
 | 
			
		||||
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void init() {
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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<List<AlarmId>> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) {
 | 
			
		||||
        log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", tenantId, user.getId().getId(), queueSize.get());
 | 
			
		||||
        queueSize.incrementAndGet();
 | 
			
		||||
        ListenableFuture<List<AlarmId>> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(tenantId, user, unassignTs));
 | 
			
		||||
        Futures.addCallback(future, new FutureCallback<>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onSuccess(List<AlarmId> 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -46,6 +47,8 @@ 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;
 | 
			
		||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 | 
			
		||||
@ -563,27 +566,28 @@ 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());
 | 
			
		||||
        AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
 | 
			
		||||
        doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk());
 | 
			
		||||
        Alarm 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);
 | 
			
		||||
 | 
			
		||||
        loginSysAdmin();
 | 
			
		||||
 | 
			
		||||
        doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk());
 | 
			
		||||
 | 
			
		||||
        loginDifferentTenant();
 | 
			
		||||
 | 
			
		||||
        foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
 | 
			
		||||
        foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull);
 | 
			
		||||
 | 
			
		||||
        Assert.assertNotNull(foundAlarm);
 | 
			
		||||
        Assert.assertNull(foundAlarm.getAssigneeId());
 | 
			
		||||
        Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
 | 
			
		||||
@ -609,15 +613,66 @@ 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/" + alarmId.getId(), AlarmInfo.class);
 | 
			
		||||
        Assert.assertNotNull(alarm);
 | 
			
		||||
        long beforeAssignmentTs = System.currentTimeMillis();
 | 
			
		||||
 | 
			
		||||
        alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
 | 
			
		||||
        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();
 | 
			
		||||
 | 
			
		||||
        doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk());
 | 
			
		||||
 | 
			
		||||
        foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull);
 | 
			
		||||
 | 
			
		||||
        Assert.assertNotNull(foundAlarm);
 | 
			
		||||
        Assert.assertNull(foundAlarm.getAssigneeId());
 | 
			
		||||
        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);
 | 
			
		||||
        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());
 | 
			
		||||
        AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
 | 
			
		||||
        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);
 | 
			
		||||
@ -626,9 +681,11 @@ public class AlarmControllerTest extends AbstractControllerTest {
 | 
			
		||||
 | 
			
		||||
        Mockito.reset(tbClusterService, auditLogService);
 | 
			
		||||
 | 
			
		||||
        doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk());
 | 
			
		||||
        doDelete("/api/customer/" + differentTenantCustomerId.getId()).andExpect(status().isOk());
 | 
			
		||||
 | 
			
		||||
        foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS)
 | 
			
		||||
                .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull);
 | 
			
		||||
 | 
			
		||||
        foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
 | 
			
		||||
        Assert.assertNotNull(foundAlarm);
 | 
			
		||||
        Assert.assertNull(foundAlarm.getAssigneeId());
 | 
			
		||||
        Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -74,7 +83,6 @@ public class DefaultTbAlarmServiceTest {
 | 
			
		||||
    protected TbClusterService tbClusterService;
 | 
			
		||||
    @MockBean
 | 
			
		||||
    private EntitiesVersionControlService vcService;
 | 
			
		||||
 | 
			
		||||
    @SpyBean
 | 
			
		||||
    DefaultTbAlarmService service;
 | 
			
		||||
 | 
			
		||||
@ -124,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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,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.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;
 | 
			
		||||
@ -118,6 +119,8 @@ public interface AlarmService extends EntityDaoService {
 | 
			
		||||
    PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
 | 
			
		||||
                                                        AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
 | 
			
		||||
 | 
			
		||||
    PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId);
 | 
			
		||||
 | 
			
		||||
    long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,29 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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 org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public interface HouseKeeperService {
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<List<AlarmId>> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -30,34 +30,34 @@ import org.thingsboard.server.dao.entity.EntityDaoService;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public interface UserService extends EntityDaoService {
 | 
			
		||||
	
 | 
			
		||||
	User findUserById(TenantId tenantId, UserId userId);
 | 
			
		||||
 | 
			
		||||
	ListenableFuture<User> findUserByIdAsync(TenantId tenantId, UserId userId);
 | 
			
		||||
    User findUserById(TenantId tenantId, UserId userId);
 | 
			
		||||
 | 
			
		||||
	User findUserByEmail(TenantId tenantId, String email);
 | 
			
		||||
    ListenableFuture<User> findUserByIdAsync(TenantId tenantId, UserId userId);
 | 
			
		||||
 | 
			
		||||
    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 findUserCredentialsByActivateToken(TenantId tenantId, String activateToken);
 | 
			
		||||
    UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId);
 | 
			
		||||
 | 
			
		||||
	UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken);
 | 
			
		||||
    UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken);
 | 
			
		||||
 | 
			
		||||
	UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials);
 | 
			
		||||
	
 | 
			
		||||
	UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password);
 | 
			
		||||
	
 | 
			
		||||
	UserCredentials requestPasswordReset(TenantId tenantId, String email);
 | 
			
		||||
    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<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<AlarmCommentId> 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. " +
 | 
			
		||||
 | 
			
		||||
@ -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 <I> List<I> fromUUIDs(List<UUID> uuids, Function<UUID, I> mapper) {
 | 
			
		||||
        return uuids.stream().map(mapper).collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <I> I toEntityId(UUID uuid, Function<UUID, I> creator) {
 | 
			
		||||
        if (uuid != null) {
 | 
			
		||||
            return creator.apply(uuid);
 | 
			
		||||
 | 
			
		||||
@ -77,6 +77,8 @@ public interface AlarmDao extends Dao<Alarm> {
 | 
			
		||||
 | 
			
		||||
    PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    void createEntityAlarmRecord(EntityAlarm entityAlarm);
 | 
			
		||||
 | 
			
		||||
    List<EntityAlarm> findEntityAlarmRecords(TenantId tenantId, AlarmId id);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -384,6 +385,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
 | 
			
		||||
        return Futures.immediateFuture(alarmDao.findCustomerAlarmsV2(tenantId, customerId, query));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) {
 | 
			
		||||
        log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId);
 | 
			
		||||
        validateId(userId, "Incorrect userId " + userId);
 | 
			
		||||
        return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
 | 
			
		||||
                                                  AlarmStatus alarmStatus, String assigneeId) {
 | 
			
		||||
@ -522,5 +530,4 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
 | 
			
		||||
            request.setEndTs(request.getStartTs());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,4 +28,7 @@ public class DeleteEntityEvent<T> {
 | 
			
		||||
    private final EntityId entityId;
 | 
			
		||||
    private final EdgeId edgeId;
 | 
			
		||||
    private final T entity;
 | 
			
		||||
 | 
			
		||||
    @Builder.Default
 | 
			
		||||
    private final long ts = System.currentTimeMillis();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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,6 +316,9 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
 | 
			
		||||
    @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.tenantId = :tenantId AND a.assigneeId = :assigneeId")
 | 
			
		||||
    Page<UUID> 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, " +
 | 
			
		||||
            ":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true)
 | 
			
		||||
 | 
			
		||||
@ -285,6 +285,12 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
 | 
			
		||||
                .mapData(AlarmId::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink) {
 | 
			
		||||
        return DaoUtil.pageToPageData(alarmRepository.findAlarmIdsByAssigneeId(tenantId.getId(), userId, DaoUtil.toPageable(pageLink)))
 | 
			
		||||
                .mapData(AlarmId::new);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void createEntityAlarmRecord(EntityAlarm entityAlarm) {
 | 
			
		||||
        log.debug("Saving entity {}", entityAlarm);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
@ -246,8 +247,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public void deleteUser(TenantId tenantId, UserId userId) {
 | 
			
		||||
        log.trace("Executing deleteUser [{}]", userId);
 | 
			
		||||
    public void deleteUser(TenantId tenantId, User user) {
 | 
			
		||||
        Objects.requireNonNull(user, "User is null");
 | 
			
		||||
        UserId userId = user.getId();
 | 
			
		||||
        log.trace("[{}] Executing deleteUser [{}]", tenantId, userId);
 | 
			
		||||
        validateId(userId, INCORRECT_USER_ID + userId);
 | 
			
		||||
        UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
 | 
			
		||||
        userCredentialsDao.removeById(tenantId, userCredentials.getUuidId());
 | 
			
		||||
@ -258,7 +261,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 +447,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 +460,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);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user