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 e57bb73262..577a25f65d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -42,8 +42,10 @@ import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.EntityType; 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.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.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -58,9 +60,9 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserSettings; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; +import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.user.TbUserService; -import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.service.query.EntityQueryService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -70,12 +72,12 @@ import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.system.SystemSecurityService; import javax.servlet.http.HttpServletRequest; - import java.util.Arrays; import java.util.List; import java.util.Map; import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD; +import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; 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.DEFAULT_DASHBOARD; @@ -440,6 +442,45 @@ public class UserController extends BaseController { } } + @ApiOperation(value = "Get usersForAssign (getUsersForAssign)", + notes = "Returns page of user data objects that can be assigned to provided alarmId. " + + "Search is been executed by email, firstName and lastName fields. " + + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/users/assign", params = {"alarmId", "pageSize", "page"}, method = RequestMethod.GET) + @ResponseBody + public PageData getAssignees( + @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) + @RequestParam String alarmId, + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = USER_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = USER_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + try { + checkParameter("alarmId", alarmId); + AlarmId alarmEntityId = new AlarmId(toUUID(alarmId)); + Alarm alarm = checkAlarmId(alarmEntityId, Operation.READ); + SecurityUser currentUser = getCurrentUser(); + TenantId tenantId = currentUser.getTenantId(); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData pageData; + if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { + pageData = userService.findUsersForAssignForTenant(tenantId, alarm.getCustomerId(), pageLink); + } else { + pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); + } + return pageData.mapData(user -> new UserEmailInfo(user.getId(), user.getEmail(), user.getFirstName(), user.getLastName())); + } catch (Exception e) { + throw handleException(e); + } + } + @ApiOperation(value = "Save user settings (saveUserSettings)", notes = "Save user settings represented in json format for authorized user. " ) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index bb40507772..fc4bf8c8d5 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 @@ -64,6 +64,8 @@ public interface UserService extends EntityDaoService { PageData findCustomerUsers(TenantId tenantId, CustomerId customerId, PageLink pageLink); + PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink); + void deleteCustomerUsers(TenantId tenantId, CustomerId customerId); void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled); 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 473c145b5d..b667e1227d 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 @@ -100,6 +100,19 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple } + @Override + public PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, PageLink pageLink) { + return DaoUtil.toPageData( + userRepository + .findUsersByCustomerIdAndNullCustomerId( + tenantId, + customerId, + Objects.toString(pageLink.getTextSearch(), ""), + NULL_UUID, + DaoUtil.toPageable(pageLink))); + + } + @Override public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); 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 8a7ee7d581..13b3214dd0 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 @@ -20,7 +20,6 @@ import org.springframework.data.domain.Pageable; 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.id.TenantId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.model.sql.UserEntity; @@ -44,6 +43,15 @@ public interface UserRepository extends JpaRepository { @Param("authority") Authority authority, Pageable pageable); + @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + + "AND 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); + @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + "AND LOWER(u.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") Page findByTenantId(@Param("tenantId") UUID tenantId, 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 c2551c84d3..3bf2138cf5 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 @@ -79,4 +79,14 @@ public interface UserDao extends Dao, TenantEntityDao { */ PageData findCustomerUsers(UUID tenantId, UUID customerId, PageLink pageLink); + /** + * Find users for alarm assignment by tenantId, customerId and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param pageLink the page link + * @return the list of user entities + */ + PageData findUsersForAssignForTenant(UUID tenantId, UUID customerId, 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 b99883c52f..08622bb776 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 @@ -265,6 +265,15 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic return userDao.findCustomerUsers(tenantId.getId(), customerId.getId(), pageLink); } + @Override + public PageData findUsersForAssignForTenant(TenantId tenantId, CustomerId customerId, PageLink pageLink) { + log.trace("Executing findUsersForAssignForTenant, 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); + } + @Override public void deleteCustomerUsers(TenantId tenantId, CustomerId customerId) { log.trace("Executing deleteCustomerUsers, customerId [{}]", customerId); diff --git a/ui-ngx/src/app/core/http/user.service.ts b/ui-ngx/src/app/core/http/user.service.ts index 9a6b70ce29..48ca76a4a0 100644 --- a/ui-ngx/src/app/core/http/user.service.ts +++ b/ui-ngx/src/app/core/http/user.service.ts @@ -51,6 +51,13 @@ export class UserService { defaultHttpOptionsFromConfig(config)); } + public getUsersForAssign(alarmId: string, pageLink: PageLink, + config?: RequestConfig): Observable> { + let url = `/api/users/assign${pageLink.toQuery()}`; + url += `&alarmId=` + alarmId; + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + } + public getUser(userId: string, config?: RequestConfig): Observable { return this.http.get(`/api/user/${userId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts index bd50beef31..121a09452c 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.ts @@ -44,6 +44,8 @@ import { AlarmService } from '@core/http/alarm.service'; import { OverlayRef } from '@angular/cdk/overlay'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { UtilsService } from '@core/services/utils.service'; +import {Authority} from "@shared/models/authority.enum"; +import {NULL_UUID} from "@shared/models/id/has-uuid"; export const ALARM_ASSIGNEE_PANEL_DATA = new InjectionToken('AlarmAssigneePanelData'); @@ -144,7 +146,7 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe property: 'email', direction: Direction.ASC }); - return this.userService.findUsersByQuery(pageLink, {ignoreLoading: true}) + return this.userService.getUsersForAssign(this.alarmId, pageLink, {ignoreLoading: true}) .pipe( catchError(() => of(emptyPageData())), map(pageData => {