diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index c3c09d3d63..27af6f35da 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -448,7 +448,7 @@ public class UserController extends BaseController { @RequestMapping(value = "/users/assign/{alarmId}", params = {"pageSize", "page"}, method = RequestMethod.GET) @ResponseBody public PageData getUsersForAssign( - @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) + @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION, required = true) @PathVariable("alarmId") String strAlarmId, @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @@ -469,7 +469,11 @@ public class UserController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); PageData pageData; if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { - pageData = userService.findUsersForAssignForTenant(tenantId, alarm.getCustomerId(), pageLink); + if (alarm.getCustomerId() == null) { + pageData = userService.findTenantAdmins(tenantId, pageLink); + } else { + pageData = userService.findTenantAndCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); + } } else { pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index b93e74c959..9bd6f56d17 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -798,6 +798,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { } + public class EntityIdComparator implements Comparator { + + @Override + public int compare(D o1, D o2) { + return o1.getId().compareTo(o2.getId()); + } + + } + protected static ResultMatcher statusReason(Matcher matcher) { return jsonPath("$.message", matcher); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java index 7149f52630..f81acf7f37 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java @@ -31,10 +31,13 @@ import org.springframework.http.HttpHeaders; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.UserEmailInfo; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -53,7 +56,6 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -66,6 +68,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { private IdComparator idComparator = new IdComparator<>(); private IdComparator userDataIdComparator = new IdComparator<>(); + private EntityIdComparator userIdComparator = new EntityIdComparator<>(); + private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer()); @Autowired @@ -647,6 +651,89 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testGetUsersForAssign() throws Exception { + loginTenantAdmin(); + + String email = "testEmail1"; + List expectedCustomerUserIds = new ArrayList<>(); + expectedCustomerUserIds.add(customerUserId); + for (int i = 0; i < 45; i++) { + User customerUser = createCustomerUser( customerId); + customerUser.setEmail(email + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org"); + User user = doPost("/api/user", customerUser, User.class); + expectedCustomerUserIds.add(user.getId()); + } + List expectedTenantUserIds = new ArrayList<>(List.copyOf(expectedCustomerUserIds)); + expectedTenantUserIds.add(tenantAdminUserId); + + Device device = new Device(); + device.setName("testDevice"); + Device savedDevice = doPost("/api/device", device, Device.class); + + Alarm alarm = createTestAlarm(savedDevice); + + List loadedTenantUserIds = new ArrayList<>(); + PageLink pageLink = new PageLink(33, 0); + PageData pageData; + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedTenantUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Assert.assertEquals(1, loadedTenantUserIds.size()); + Assert.assertEquals(tenantAdminUserId, loadedTenantUserIds.get(0)); + + doDelete("/api/alarm/" + alarm.getId().getId().toString()); + + savedDevice.setCustomerId(customerId); + savedDevice = doPost("/api/customer/" + customerId.getId() + + "/device/" + savedDevice.getId().getId(), Device.class); + + alarm = createTestAlarm(savedDevice); + + List loadedUserIds = new ArrayList<>(); + pageLink = new PageLink(16, 0); + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + expectedTenantUserIds.sort(userIdComparator); + loadedUserIds.sort(userIdComparator); + + Assert.assertEquals(expectedTenantUserIds, loadedUserIds); + + loginCustomerUser(); + + loadedUserIds = new ArrayList<>(); + pageLink = new PageLink(16, 0); + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + expectedCustomerUserIds.sort(userIdComparator); + loadedUserIds.sort(userIdComparator); + + Assert.assertEquals(expectedCustomerUserIds, loadedUserIds); + } + @Test public void testDeleteUserWithDeleteRelationsOk() throws Exception { loginSysAdmin(); @@ -984,4 +1071,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { return loadedCustomerUsers; } + private Alarm createTestAlarm(Device device) { + Alarm alarm = new Alarm(); + alarm.setOriginator(device.getId()); + alarm.setCustomerId(device.getCustomerId()); + alarm.setSeverity(AlarmSeverity.MAJOR); + alarm.setType("testAlarm"); + alarm.setStartTs(System.currentTimeMillis()); + return doPost("/api/alarm", alarm, Alarm.class); + } + } 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 ba5f1722d2..7b11f1c6ff 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 @@ -77,7 +77,7 @@ public interface UserService extends EntityDaoService { PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); - PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink); + PageData findTenantAndCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); void deleteCustomerUsers(TenantId tenantId, CustomerId customerId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index 8c9f41eef6..2bfab53b64 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -103,10 +103,10 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple } @Override - public PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, PageLink pageLink) { + public PageData findTenantAndCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink) { return DaoUtil.toPageData( userRepository - .findUsersByCustomerIdAndNullCustomerId( + .findTenantAndCustomerUsers( tenantId, customerId, Objects.toString(pageLink.getTextSearch(), ""), diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index bdb9e829fa..9cd7029146 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -45,13 +45,13 @@ public interface UserRepository extends JpaRepository { Pageable pageable); @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + - "AND u.customerId IN (:customerId, :nullCustomerId) " + + "AND (:customerId IS NULL OR u.customerId IN (:customerId, :nullCustomerId)) " + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") - Page findUsersByCustomerIdAndNullCustomerId(@Param("tenantId") UUID tenantId, - @Param("customerId") UUID customerId, - @Param("searchText") String searchText, - @Param("nullCustomerId") UUID nullCustomerId, - Pageable pageable); + Page findTenantAndCustomerUsers(@Param("tenantId") UUID tenantId, + @Param("customerId") UUID customerId, + @Param("searchText") String searchText, + @Param("nullCustomerId") UUID nullCustomerId, + Pageable pageable); @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index c20439b24d..2dee06db87 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -90,7 +90,7 @@ public interface UserDao extends Dao, TenantEntityDao { * @param pageLink the page link * @return the list of user entities */ - PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, PageLink pageLink); + PageData findTenantAndCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); PageData findAll(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 b273beb516..222298d124 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 @@ -294,12 +294,12 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } @Override - public PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink) { - log.trace("Executing findUsersForAssignForTenant, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); + public PageData findTenantAndCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink) { + log.trace("Executing findTenantAndCustomerUsers, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateId(customerId, "Incorrect customerId " + customerId); validatePageLink(pageLink); - return userDao.findUsersForAssignForTenant(tenantId.getId(), customerId.getId(), pageLink); + validateId(customerId, "Incorrect customerId " + customerId); + return userDao.findTenantAndCustomerUsers(tenantId.getId(), customerId.getId(), pageLink); } @Override