Moved unassign to async processing, added to customer controller, to unassign customer users alarms on customer removing
This commit is contained in:
parent
581c02bf6b
commit
25778ce451
@ -17,6 +17,8 @@ package org.thingsboard.server.controller;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
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.ApiOperation;
|
||||||
import io.swagger.annotations.ApiParam;
|
import io.swagger.annotations.ApiParam;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -32,16 +34,21 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.common.data.Customer;
|
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.exception.ThingsboardException;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
import org.thingsboard.server.common.data.page.PageLink;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
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.entitiy.customer.TbCustomerService;
|
||||||
import org.thingsboard.server.service.security.permission.Operation;
|
import org.thingsboard.server.service.security.permission.Operation;
|
||||||
import org.thingsboard.server.service.security.permission.Resource;
|
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;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
|
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES;
|
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 {
|
public class CustomerController extends BaseController {
|
||||||
|
|
||||||
private final TbCustomerService tbCustomerService;
|
private final TbCustomerService tbCustomerService;
|
||||||
|
private final TbAlarmService tbAlarmService;
|
||||||
|
|
||||||
public static final String IS_PUBLIC = "isPublic";
|
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. " +
|
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);
|
checkParameter(CUSTOMER_ID, strCustomerId);
|
||||||
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
|
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
|
||||||
Customer customer = checkCustomerId(customerId, Operation.DELETE);
|
Customer customer = checkCustomerId(customerId, Operation.DELETE);
|
||||||
|
TenantId tenantId = getTenantId();
|
||||||
|
PageLink pl = new PageLink(100);
|
||||||
|
boolean hasNext = true;
|
||||||
|
List<ListenableFuture<Void>> futures = new ArrayList<>();
|
||||||
|
|
||||||
|
while (hasNext) {
|
||||||
|
PageData<User> customerUsers = userService.findCustomerUsers(tenantId, customerId, pl);
|
||||||
|
for (User user : customerUsers.getData()) {
|
||||||
|
ListenableFuture<Void> future = tbAlarmService.unassignUserAlarms(tenantId, user, System.currentTimeMillis());
|
||||||
|
futures.add(future);
|
||||||
|
}
|
||||||
|
hasNext = customerUsers.hasNext();
|
||||||
|
if (hasNext) {
|
||||||
|
pl = pl.nextPageLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListenableFuture<List<Void>> allFutures = Futures.allAsList(futures);
|
||||||
|
Futures.getChecked(allFutures, ThingsboardException.class);
|
||||||
tbCustomerService.delete(customer, getCurrentUser());
|
tbCustomerService.delete(customer, getCurrentUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.entitiy.alarm;
|
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.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
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.EdgeId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
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.common.data.page.TimePageLink;
|
||||||
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
|
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ -216,35 +217,44 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unassignUserAlarms(TenantId tenantId, User user, long unassignTs) {
|
public ListenableFuture<Void> unassignUserAlarms(TenantId tenantId, User user, long unassignTs) {
|
||||||
AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build();
|
AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build();
|
||||||
try {
|
ListenableFuture<PageData<AlarmInfo>> foundUserAlarmsFuture = alarmService.findAlarmsV2(tenantId, alarmQuery);
|
||||||
List<AlarmInfo> alarms = alarmService.findAlarmsV2(tenantId, alarmQuery).get(30, TimeUnit.SECONDS).getData();
|
FutureCallback<PageData<AlarmInfo>> callback = new FutureCallback<>() {
|
||||||
for (AlarmInfo alarm : alarms) {
|
public void onSuccess(PageData<AlarmInfo> alarmsData) {
|
||||||
AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs));
|
for (AlarmInfo alarm : alarmsData.getData()) {
|
||||||
if (!result.isSuccessful()) {
|
unassignUserAlarm(tenantId, user, unassignTs, alarm);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
public void onFailure(Throwable t) {
|
||||||
throw new RuntimeException(e);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.entitiy.alarm;
|
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.User;
|
||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
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;
|
AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException;
|
||||||
|
|
||||||
void unassignUserAlarms(TenantId tenantId, User user, long unassignTs);
|
ListenableFuture<Void> unassignUserAlarms(TenantId tenantId, User user, long unassignTs);
|
||||||
|
|
||||||
Boolean delete(Alarm alarm, User user);
|
Boolean delete(Alarm alarm, User user);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.service.entitiy.user;
|
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.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -82,7 +84,8 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse
|
|||||||
UserId userId = tbUser.getId();
|
UserId userId = tbUser.getId();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis());
|
ListenableFuture<Void> future = tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis());
|
||||||
|
Futures.getChecked(future, ThingsboardException.class);
|
||||||
userService.deleteUser(tenantId, userId);
|
userService.deleteUser(tenantId, userId);
|
||||||
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, userId, tbUser,
|
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, userId, tbUser,
|
||||||
user, ActionType.DELETED, true, null, customerId.toString());
|
user, ActionType.DELETED, true, null, customerId.toString());
|
||||||
|
|||||||
@ -625,6 +625,58 @@ public class AlarmControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
|
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
|
@Test
|
||||||
public void testFindAlarmsViaCustomerUser() throws Exception {
|
public void testFindAlarmsViaCustomerUser() throws Exception {
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user