Query Improvement to use entity field aliases
This commit is contained in:
		
						commit
						b707ea090b
					
				@ -246,6 +246,28 @@ public class UserController extends BaseController {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
 | 
			
		||||
    @RequestMapping(value = "/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
 | 
			
		||||
    @ResponseBody
 | 
			
		||||
    public PageData<User> getUsers(
 | 
			
		||||
            @RequestParam int pageSize,
 | 
			
		||||
            @RequestParam int page,
 | 
			
		||||
            @RequestParam(required = false) String textSearch,
 | 
			
		||||
            @RequestParam(required = false) String sortProperty,
 | 
			
		||||
            @RequestParam(required = false) String sortOrder) throws ThingsboardException {
 | 
			
		||||
        try {
 | 
			
		||||
            PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
 | 
			
		||||
            SecurityUser currentUser = getCurrentUser();
 | 
			
		||||
            if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) {
 | 
			
		||||
                return checkNotNull(userService.findUsersByTenantId(currentUser.getTenantId(), pageLink));
 | 
			
		||||
            } else {
 | 
			
		||||
                return checkNotNull(userService.findCustomerUsers(currentUser.getTenantId(), currentUser.getCustomerId(), pageLink));
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw handleException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PreAuthorize("hasAuthority('SYS_ADMIN')")
 | 
			
		||||
    @RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
 | 
			
		||||
    @ResponseBody
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Customer;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityView;
 | 
			
		||||
import org.thingsboard.server.common.data.Tenant;
 | 
			
		||||
import org.thingsboard.server.common.data.User;
 | 
			
		||||
import org.thingsboard.server.common.data.asset.Asset;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
import org.thingsboard.server.common.data.id.AssetId;
 | 
			
		||||
@ -40,6 +41,7 @@ import org.thingsboard.server.common.data.id.EntityViewId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.RuleChainId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.RuleNodeId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.UserId;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleChain;
 | 
			
		||||
import org.thingsboard.server.common.data.rule.RuleNode;
 | 
			
		||||
import org.thingsboard.server.controller.HttpValidationCallback;
 | 
			
		||||
@ -172,6 +174,9 @@ public class AccessValidator {
 | 
			
		||||
            case TENANT:
 | 
			
		||||
                validateTenant(currentUser, operation, entityId, callback);
 | 
			
		||||
                return;
 | 
			
		||||
            case USER:
 | 
			
		||||
                validateUser(currentUser, operation, entityId, callback);
 | 
			
		||||
                return;
 | 
			
		||||
            case ENTITY_VIEW:
 | 
			
		||||
                validateEntityView(currentUser, operation, entityId, callback);
 | 
			
		||||
                return;
 | 
			
		||||
@ -308,6 +313,22 @@ public class AccessValidator {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void validateUser(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
 | 
			
		||||
        ListenableFuture<User> userFuture = userService.findUserByIdAsync(currentUser.getTenantId(), new UserId(entityId.getId()));
 | 
			
		||||
        Futures.addCallback(userFuture, getCallback(callback, user -> {
 | 
			
		||||
            if (user == null) {
 | 
			
		||||
                return ValidationResult.entityNotFound("User with requested id wasn't found!");
 | 
			
		||||
            }
 | 
			
		||||
            try {
 | 
			
		||||
                accessControlService.checkPermission(currentUser, Resource.USER, operation, entityId, user);
 | 
			
		||||
            } catch (ThingsboardException e) {
 | 
			
		||||
                return ValidationResult.accessDenied(e.getMessage());
 | 
			
		||||
            }
 | 
			
		||||
            return ValidationResult.ok(user);
 | 
			
		||||
 | 
			
		||||
        }), executor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void validateEntityView(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
 | 
			
		||||
        if (currentUser.isSystemAdmin()) {
 | 
			
		||||
            callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
 | 
			
		||||
 | 
			
		||||
@ -52,8 +52,10 @@ public interface UserService {
 | 
			
		||||
    UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials);
 | 
			
		||||
 | 
			
		||||
	void deleteUser(TenantId tenantId, UserId userId);
 | 
			
		||||
	
 | 
			
		||||
	PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink);
 | 
			
		||||
	
 | 
			
		||||
	void deleteTenantAdmins(TenantId tenantId);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -254,7 +254,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
 | 
			
		||||
 | 
			
		||||
        String entityWhereClause = this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
 | 
			
		||||
        String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings);
 | 
			
		||||
        String whereClause = this.buildWhere(ctx, latestFiltersMapping);
 | 
			
		||||
        String whereClause = this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType);
 | 
			
		||||
        String textSearchQuery = this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch());
 | 
			
		||||
        String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
 | 
			
		||||
        String entityTypeStr;
 | 
			
		||||
@ -316,7 +316,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
 | 
			
		||||
                                    EntityType entityType) {
 | 
			
		||||
        String permissionQuery = this.buildPermissionQuery(ctx, entityFilter, tenantId, customerId, entityType);
 | 
			
		||||
        String entityFilterQuery = this.buildEntityFilterQuery(ctx, entityFilter);
 | 
			
		||||
        String entityFieldsQuery = EntityKeyMapping.buildQuery(ctx, entityFieldsFilters);
 | 
			
		||||
        String entityFieldsQuery = EntityKeyMapping.buildQuery(ctx, entityFieldsFilters, entityFilter.getType(), entityType);
 | 
			
		||||
        String result = permissionQuery;
 | 
			
		||||
        if (!entityFilterQuery.isEmpty()) {
 | 
			
		||||
            result += " and " + entityFilterQuery;
 | 
			
		||||
@ -478,8 +478,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
 | 
			
		||||
        return from;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String buildWhere(QueryContext ctx, List<EntityKeyMapping> latestFiltersMapping) {
 | 
			
		||||
        String latestFilters = EntityKeyMapping.buildQuery(ctx, latestFiltersMapping);
 | 
			
		||||
    private String buildWhere(QueryContext ctx, List<EntityKeyMapping> latestFiltersMapping, EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        String latestFilters = EntityKeyMapping.buildQuery(ctx, latestFiltersMapping, filterType, entityType);
 | 
			
		||||
        if (!StringUtils.isEmpty(latestFilters)) {
 | 
			
		||||
            return String.format("where %s", latestFilters);
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,7 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
@ -86,7 +87,7 @@ public class EntityKeyMapping {
 | 
			
		||||
        allowedEntityFieldMap.get(EntityType.TENANT).add(REGION);
 | 
			
		||||
        allowedEntityFieldMap.put(EntityType.CUSTOMER, new HashSet<>(contactBasedEntityFields));
 | 
			
		||||
 | 
			
		||||
        allowedEntityFieldMap.put(EntityType.USER, new HashSet<>(Arrays.asList(FIRST_NAME, LAST_NAME, EMAIL)));
 | 
			
		||||
        allowedEntityFieldMap.put(EntityType.USER, new HashSet<>(Arrays.asList(CREATED_TIME, FIRST_NAME, LAST_NAME, EMAIL)));
 | 
			
		||||
 | 
			
		||||
        allowedEntityFieldMap.put(EntityType.DASHBOARD, new HashSet<>(commonEntityFields));
 | 
			
		||||
        allowedEntityFieldMap.put(EntityType.RULE_CHAIN, new HashSet<>(commonEntityFields));
 | 
			
		||||
@ -160,27 +161,8 @@ public class EntityKeyMapping {
 | 
			
		||||
 | 
			
		||||
    public String toSelection(EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
 | 
			
		||||
            Set<String> existingEntityFields;
 | 
			
		||||
            String alias;
 | 
			
		||||
            if (filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
 | 
			
		||||
                existingEntityFields = relationQueryEntityFieldsSet;
 | 
			
		||||
                alias = entityKey.getKey();
 | 
			
		||||
            } else {
 | 
			
		||||
                existingEntityFields = allowedEntityFieldMap.get(entityType);
 | 
			
		||||
                if (existingEntityFields == null) {
 | 
			
		||||
                    existingEntityFields = commonEntityFieldsSet;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Map<String, String> entityAliases = aliases.get(entityType);
 | 
			
		||||
                if (entityAliases != null) {
 | 
			
		||||
                    alias = entityAliases.get(entityKey.getKey());
 | 
			
		||||
                } else {
 | 
			
		||||
                    alias = null;
 | 
			
		||||
                }
 | 
			
		||||
                if (alias == null) {
 | 
			
		||||
                    alias = entityKey.getKey();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Set<String> existingEntityFields = getExistingEntityFields(filterType, entityType);
 | 
			
		||||
            String alias = getEntityFieldAlias(filterType, entityType);
 | 
			
		||||
            if (existingEntityFields.contains(alias)) {
 | 
			
		||||
                String column = entityFieldColumnMap.get(alias);
 | 
			
		||||
                return String.format("e.%s as %s", column, getValueAlias());
 | 
			
		||||
@ -194,11 +176,48 @@ public class EntityKeyMapping {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Stream<String> toQueries(QueryContext ctx) {
 | 
			
		||||
    private String getEntityFieldAlias(EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        String alias;
 | 
			
		||||
        if (filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
 | 
			
		||||
            alias = entityKey.getKey();
 | 
			
		||||
        } else {
 | 
			
		||||
            alias = getAliasByEntityKeyAndType(entityKey.getKey(), entityType);
 | 
			
		||||
        }
 | 
			
		||||
        return alias;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Set<String> getExistingEntityFields(EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        Set<String> existingEntityFields;
 | 
			
		||||
        if (filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
 | 
			
		||||
            existingEntityFields = relationQueryEntityFieldsSet;
 | 
			
		||||
        } else {
 | 
			
		||||
            existingEntityFields = allowedEntityFieldMap.get(entityType);
 | 
			
		||||
            if (existingEntityFields == null) {
 | 
			
		||||
                existingEntityFields = commonEntityFieldsSet;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return existingEntityFields;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String getAliasByEntityKeyAndType(String key, EntityType entityType) {
 | 
			
		||||
        String alias;
 | 
			
		||||
        Map<String, String> entityAliases = aliases.get(entityType);
 | 
			
		||||
        if (entityAliases != null) {
 | 
			
		||||
            alias = entityAliases.get(key);
 | 
			
		||||
        } else {
 | 
			
		||||
            alias = null;
 | 
			
		||||
        }
 | 
			
		||||
        if (alias == null) {
 | 
			
		||||
            alias = key;
 | 
			
		||||
        }
 | 
			
		||||
        return alias;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Stream<String> toQueries(QueryContext ctx, EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        if (hasFilter()) {
 | 
			
		||||
            String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias;
 | 
			
		||||
            return keyFilters.stream().map(keyFilter ->
 | 
			
		||||
                    this.buildKeyQuery(ctx, keyAlias, keyFilter));
 | 
			
		||||
                    this.buildKeyQuery(ctx, keyAlias, keyFilter, filterType, entityType));
 | 
			
		||||
        } else {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
@ -244,8 +263,8 @@ public class EntityKeyMapping {
 | 
			
		||||
                Collectors.joining(" "));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings) {
 | 
			
		||||
        return mappings.stream().flatMap(mapping -> mapping.toQueries(ctx)).collect(
 | 
			
		||||
    public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings, EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        return mappings.stream().flatMap(mapping -> mapping.toQueries(ctx, filterType, entityType)).filter(Objects::nonNull).collect(
 | 
			
		||||
                Collectors.joining(" AND "));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -357,47 +376,63 @@ public class EntityKeyMapping {
 | 
			
		||||
        return String.join(", ", attrValSelection, attrTsSelection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String buildKeyQuery(QueryContext ctx, String alias, KeyFilter keyFilter) {
 | 
			
		||||
        return this.buildPredicateQuery(ctx, alias, keyFilter.getKey(), keyFilter.getPredicate());
 | 
			
		||||
    private String buildKeyQuery(QueryContext ctx, String alias, KeyFilter keyFilter,
 | 
			
		||||
                                 EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        return this.buildPredicateQuery(ctx, alias, keyFilter.getKey(), keyFilter.getPredicate(), filterType, entityType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String buildPredicateQuery(QueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) {
 | 
			
		||||
    private String buildPredicateQuery(QueryContext ctx, String alias, EntityKey key,
 | 
			
		||||
                                       KeyFilterPredicate predicate, EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        if (predicate.getType().equals(FilterPredicateType.COMPLEX)) {
 | 
			
		||||
            return this.buildComplexPredicateQuery(ctx, alias, key, (ComplexFilterPredicate) predicate);
 | 
			
		||||
            return this.buildComplexPredicateQuery(ctx, alias, key, (ComplexFilterPredicate) predicate, filterType, entityType);
 | 
			
		||||
        } else {
 | 
			
		||||
            return this.buildSimplePredicateQuery(ctx, alias, key, predicate);
 | 
			
		||||
            return this.buildSimplePredicateQuery(ctx, alias, key, predicate, filterType, entityType);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String buildComplexPredicateQuery(QueryContext ctx, String alias, EntityKey key, ComplexFilterPredicate predicate) {
 | 
			
		||||
    private String buildComplexPredicateQuery(QueryContext ctx, String alias, EntityKey key,
 | 
			
		||||
                                              ComplexFilterPredicate predicate, EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        return predicate.getPredicates().stream()
 | 
			
		||||
                .map(keyFilterPredicate -> this.buildPredicateQuery(ctx, alias, key, keyFilterPredicate)).collect(Collectors.joining(
 | 
			
		||||
                .map(keyFilterPredicate -> this.buildPredicateQuery(ctx, alias, key, keyFilterPredicate, filterType, entityType))
 | 
			
		||||
                .filter(Objects::nonNull).collect(Collectors.joining(
 | 
			
		||||
                        " " + predicate.getOperation().name() + " "
 | 
			
		||||
                ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String buildSimplePredicateQuery(QueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) {
 | 
			
		||||
        if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
 | 
			
		||||
            if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
 | 
			
		||||
                String column = entityFieldColumnMap.get(key.getKey());
 | 
			
		||||
                return this.buildNumericPredicateQuery(ctx, alias + "." + column, (NumericFilterPredicate) predicate);
 | 
			
		||||
    private String buildSimplePredicateQuery(QueryContext ctx, String alias, EntityKey key,
 | 
			
		||||
                                             KeyFilterPredicate predicate, EntityFilterType filterType, EntityType entityType) {
 | 
			
		||||
        if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
 | 
			
		||||
            Set<String> existingEntityFields = getExistingEntityFields(filterType, entityType);
 | 
			
		||||
            String entityFieldAlias = getEntityFieldAlias(filterType, entityType);
 | 
			
		||||
            String column = null;
 | 
			
		||||
            if (existingEntityFields.contains(entityFieldAlias)) {
 | 
			
		||||
                column = entityFieldColumnMap.get(key.getKey());
 | 
			
		||||
            }
 | 
			
		||||
            if (column != null) {
 | 
			
		||||
                String field = alias + "." + column;
 | 
			
		||||
                if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
 | 
			
		||||
                    return this.buildNumericPredicateQuery(ctx, field, (NumericFilterPredicate) predicate);
 | 
			
		||||
                } else if (predicate.getType().equals(FilterPredicateType.STRING)) {
 | 
			
		||||
                    return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
 | 
			
		||||
                } else {
 | 
			
		||||
                    return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
 | 
			
		||||
                String longQuery = this.buildNumericPredicateQuery(ctx, alias + ".long_v", (NumericFilterPredicate) predicate);
 | 
			
		||||
                String doubleQuery = this.buildNumericPredicateQuery(ctx, alias + ".dbl_v", (NumericFilterPredicate) predicate);
 | 
			
		||||
                return String.format("(%s or %s)", longQuery, doubleQuery);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            String column;
 | 
			
		||||
            if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
 | 
			
		||||
                column = entityFieldColumnMap.get(key.getKey());
 | 
			
		||||
            } else {
 | 
			
		||||
                column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v";
 | 
			
		||||
            }
 | 
			
		||||
            String field = alias + "." + column;
 | 
			
		||||
            if (predicate.getType().equals(FilterPredicateType.STRING)) {
 | 
			
		||||
                return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
 | 
			
		||||
            } else {
 | 
			
		||||
                return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
 | 
			
		||||
                String column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v";
 | 
			
		||||
                String field = alias + "." + column;
 | 
			
		||||
                if (predicate.getType().equals(FilterPredicateType.STRING)) {
 | 
			
		||||
                    return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
 | 
			
		||||
                } else {
 | 
			
		||||
                    return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -59,6 +59,16 @@ public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> imple
 | 
			
		||||
        return DaoUtil.getData(userRepository.findByEmail(email));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageData<User> findByTenantId(UUID tenantId, PageLink pageLink) {
 | 
			
		||||
        return DaoUtil.toPageData(
 | 
			
		||||
                userRepository
 | 
			
		||||
                        .findByTenantId(
 | 
			
		||||
                                tenantId,
 | 
			
		||||
                                Objects.toString(pageLink.getTextSearch(), ""),
 | 
			
		||||
                                DaoUtil.toPageable(pageLink)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageData<User> findTenantAdmins(UUID tenantId, PageLink pageLink) {
 | 
			
		||||
        return DaoUtil.toPageData(
 | 
			
		||||
 | 
			
		||||
@ -43,4 +43,10 @@ public interface UserRepository extends PagingAndSortingRepository<UserEntity, U
 | 
			
		||||
                                          @Param("authority") Authority authority,
 | 
			
		||||
                                          Pageable pageable);
 | 
			
		||||
 | 
			
		||||
    @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " +
 | 
			
		||||
            "AND LOWER(u.searchText) LIKE LOWER(CONCAT(:searchText, '%'))")
 | 
			
		||||
    Page<UserEntity> findByTenantId(@Param("tenantId") UUID tenantId,
 | 
			
		||||
                                    @Param("searchText") String searchText,
 | 
			
		||||
                                    Pageable pageable);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,15 @@ public interface UserDao extends Dao<User> {
 | 
			
		||||
     * @return the user entity
 | 
			
		||||
     */
 | 
			
		||||
    User findByEmail(TenantId tenantId, String email);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find users by tenantId and page link.
 | 
			
		||||
     *
 | 
			
		||||
     * @param tenantId the tenantId
 | 
			
		||||
     * @param pageLink the page link
 | 
			
		||||
     * @return the list of user entities
 | 
			
		||||
     */
 | 
			
		||||
    PageData<User> findByTenantId(UUID tenantId, PageLink pageLink);
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Find tenant admin users by tenantId and page link.
 | 
			
		||||
 | 
			
		||||
@ -219,6 +219,14 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
 | 
			
		||||
        userDao.removeById(tenantId, userId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink) {
 | 
			
		||||
        log.trace("Executing findUsersByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
 | 
			
		||||
        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
 | 
			
		||||
        validatePageLink(pageLink);
 | 
			
		||||
        return userDao.findByTenantId(tenantId.getId(), pageLink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageData<User> findTenantAdmins(TenantId tenantId, PageLink pageLink) {
 | 
			
		||||
        log.trace("Executing findTenantAdmins, tenantId [{}], pageLink [{}]", tenantId, pageLink);
 | 
			
		||||
 | 
			
		||||
@ -310,7 +310,8 @@ export class EntityService {
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case EntityType.USER:
 | 
			
		||||
        console.error('Get User Entities is not implemented!');
 | 
			
		||||
        pageLink.sortOrder.property = 'email';
 | 
			
		||||
        entitiesObservable = this.userService.getUsers(pageLink);
 | 
			
		||||
        break;
 | 
			
		||||
      case EntityType.ALARM:
 | 
			
		||||
        console.error('Get Alarm Entities is not implemented!');
 | 
			
		||||
@ -548,6 +549,7 @@ export class EntityService {
 | 
			
		||||
        entityTypes.push(EntityType.ENTITY_VIEW);
 | 
			
		||||
        entityTypes.push(EntityType.TENANT);
 | 
			
		||||
        entityTypes.push(EntityType.CUSTOMER);
 | 
			
		||||
        entityTypes.push(EntityType.USER);
 | 
			
		||||
        entityTypes.push(EntityType.DASHBOARD);
 | 
			
		||||
        if (useAliasEntityTypes) {
 | 
			
		||||
          entityTypes.push(AliasEntityType.CURRENT_CUSTOMER);
 | 
			
		||||
@ -559,12 +561,16 @@ export class EntityService {
 | 
			
		||||
        entityTypes.push(EntityType.ASSET);
 | 
			
		||||
        entityTypes.push(EntityType.ENTITY_VIEW);
 | 
			
		||||
        entityTypes.push(EntityType.CUSTOMER);
 | 
			
		||||
        entityTypes.push(EntityType.USER);
 | 
			
		||||
        entityTypes.push(EntityType.DASHBOARD);
 | 
			
		||||
        if (useAliasEntityTypes) {
 | 
			
		||||
          entityTypes.push(AliasEntityType.CURRENT_CUSTOMER);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (useAliasEntityTypes) {
 | 
			
		||||
      entityTypes.push(AliasEntityType.CURRENT_USER);
 | 
			
		||||
    }
 | 
			
		||||
    if (allowedEntityTypes && allowedEntityTypes.length) {
 | 
			
		||||
      for (let index = entityTypes.length - 1; index >= 0; index--) {
 | 
			
		||||
        if (allowedEntityTypes.indexOf(entityTypes[index]) === -1) {
 | 
			
		||||
@ -961,6 +967,10 @@ export class EntityService {
 | 
			
		||||
      const authUser =  getCurrentAuthUser(this.store);
 | 
			
		||||
      entityId.entityType = EntityType.TENANT;
 | 
			
		||||
      entityId.id = authUser.tenantId;
 | 
			
		||||
    } else if (entityType === AliasEntityType.CURRENT_USER){
 | 
			
		||||
      const authUser =  getCurrentAuthUser(this.store);
 | 
			
		||||
      entityId.entityType = EntityType.USER;
 | 
			
		||||
      entityId.id = authUser.userId;
 | 
			
		||||
    }
 | 
			
		||||
    return entityId;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,12 @@ export class UserService {
 | 
			
		||||
    private http: HttpClient
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  public getUsers(pageLink: PageLink,
 | 
			
		||||
                  config?: RequestConfig): Observable<PageData<User>> {
 | 
			
		||||
    return this.http.get<PageData<User>>(`/api/users${pageLink.toQuery()}`,
 | 
			
		||||
      defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getTenantAdmins(tenantId: string, pageLink: PageLink,
 | 
			
		||||
                         config?: RequestConfig): Observable<PageData<User>> {
 | 
			
		||||
    return this.http.get<PageData<User>>(`/api/tenant/${tenantId}/users${pageLink.toQuery()}`,
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ import {
 | 
			
		||||
  AliasesEntitySelectPanelData
 | 
			
		||||
} from './aliases-entity-select-panel.component';
 | 
			
		||||
import { deepClone } from '@core/utils';
 | 
			
		||||
import { AliasFilterType } from '@shared/models/alias.models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-aliases-entity-select',
 | 
			
		||||
@ -178,7 +179,7 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy {
 | 
			
		||||
    for (const aliasId of Object.keys(allEntityAliases)) {
 | 
			
		||||
      const aliasInfo = this.aliasController.getInstantAliasInfo(aliasId);
 | 
			
		||||
      if (aliasInfo && !aliasInfo.resolveMultiple && aliasInfo.currentEntity
 | 
			
		||||
        && aliasInfo.entityFilter) {
 | 
			
		||||
        && aliasInfo.entityFilter && aliasInfo.entityFilter.type !== AliasFilterType.singleEntity) {
 | 
			
		||||
        this.entityAliasesInfo[aliasId] = deepClone(aliasInfo);
 | 
			
		||||
        this.hasSelectableAliasEntities = true;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -84,6 +84,9 @@ import {
 | 
			
		||||
  KeyFilter
 | 
			
		||||
} from '@shared/models/query/query.models';
 | 
			
		||||
import { sortItems } from '@shared/models/page/page-link';
 | 
			
		||||
import { entityFields } from '@shared/models/entity.models';
 | 
			
		||||
import { alarmFields } from '@shared/models/alarm.models';
 | 
			
		||||
import { DatePipe } from '@angular/common';
 | 
			
		||||
 | 
			
		||||
interface EntitiesTableWidgetSettings extends TableWidgetSettings {
 | 
			
		||||
  entitiesTitle: string;
 | 
			
		||||
@ -153,6 +156,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
 | 
			
		||||
              private overlay: Overlay,
 | 
			
		||||
              private viewContainerRef: ViewContainerRef,
 | 
			
		||||
              private utils: UtilsService,
 | 
			
		||||
              private datePipe: DatePipe,
 | 
			
		||||
              private translate: TranslateService,
 | 
			
		||||
              private domSanitizer: DomSanitizer) {
 | 
			
		||||
    super(store);
 | 
			
		||||
@ -511,9 +515,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
 | 
			
		||||
            content = '' + value;
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals;
 | 
			
		||||
        const units = contentInfo.units || this.ctx.widgetConfig.units;
 | 
			
		||||
        content = this.ctx.utils.formatValue(value, decimals, units, true);
 | 
			
		||||
        content = this.defaultContent(key, contentInfo, value);
 | 
			
		||||
      }
 | 
			
		||||
      return isDefined(content) ? this.domSanitizer.bypassSecurityTrustHtml(content) : '';
 | 
			
		||||
    } else {
 | 
			
		||||
@ -521,6 +523,22 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any {
 | 
			
		||||
    if (isDefined(value)) {
 | 
			
		||||
      const entityField = entityFields[key.name];
 | 
			
		||||
      if (entityField) {
 | 
			
		||||
        if (entityField.time) {
 | 
			
		||||
          return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss');
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals;
 | 
			
		||||
      const units = contentInfo.units || this.ctx.widgetConfig.units;
 | 
			
		||||
      return this.ctx.utils.formatValue(value, decimals, units, true);
 | 
			
		||||
    } else {
 | 
			
		||||
      return '';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public onRowClick($event: Event, entity: EntityData, isDouble?: boolean) {
 | 
			
		||||
    if ($event) {
 | 
			
		||||
      $event.stopPropagation();
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,23 @@
 | 
			
		||||
    limitations under the License.
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<mat-tab *ngIf="entity"
 | 
			
		||||
         label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab">
 | 
			
		||||
  <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"
 | 
			
		||||
                      [active]="attributesTab.isActive"
 | 
			
		||||
                      [entityId]="entity.id"
 | 
			
		||||
                      [entityName]="entity.name">
 | 
			
		||||
  </tb-attribute-table>
 | 
			
		||||
</mat-tab>
 | 
			
		||||
<mat-tab *ngIf="entity"
 | 
			
		||||
         label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab">
 | 
			
		||||
  <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY"
 | 
			
		||||
                      disableAttributeScopeSelection
 | 
			
		||||
                      [active]="telemetryTab.isActive"
 | 
			
		||||
                      [entityId]="entity.id"
 | 
			
		||||
                      [entityName]="entity.name">
 | 
			
		||||
  </tb-attribute-table>
 | 
			
		||||
</mat-tab>
 | 
			
		||||
<mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN"
 | 
			
		||||
         label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab">
 | 
			
		||||
  <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.USER" [userId]="entity.id" detailsMode="true"></tb-audit-log-table>
 | 
			
		||||
 | 
			
		||||
@ -175,6 +175,7 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
 | 
			
		||||
          this.entityRequiredText = 'customer.customer-required';
 | 
			
		||||
          break;
 | 
			
		||||
        case EntityType.USER:
 | 
			
		||||
        case AliasEntityType.CURRENT_USER:
 | 
			
		||||
          this.entityText = 'user.user';
 | 
			
		||||
          this.noEntitiesMatchingText = 'user.no-users-matching';
 | 
			
		||||
          this.entityRequiredText = 'user.user-required';
 | 
			
		||||
@ -324,6 +325,8 @@ export class EntityAutocompleteComponent implements ControlValueAccessor, OnInit
 | 
			
		||||
      return EntityType.CUSTOMER;
 | 
			
		||||
    } else if (entityType === AliasEntityType.CURRENT_TENANT) {
 | 
			
		||||
      return EntityType.TENANT;
 | 
			
		||||
    } else if (entityType === AliasEntityType.CURRENT_USER) {
 | 
			
		||||
      return EntityType.USER;
 | 
			
		||||
    }
 | 
			
		||||
    return entityType;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,8 @@
 | 
			
		||||
  </tb-entity-type-select>
 | 
			
		||||
  <tb-entity-autocomplete
 | 
			
		||||
    fxFlex
 | 
			
		||||
    *ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT"
 | 
			
		||||
    *ngIf="modelValue.entityType && modelValue.entityType !== AliasEntityType.CURRENT_TENANT
 | 
			
		||||
                                 && modelValue.entityType !== AliasEntityType.CURRENT_USER"
 | 
			
		||||
    [required]="required"
 | 
			
		||||
    [entityType]="modelValue.entityType"
 | 
			
		||||
    formControlName="entityId">
 | 
			
		||||
 | 
			
		||||
@ -97,7 +97,7 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.entitySelectFormGroup.get('entityType').valueChanges.subscribe(
 | 
			
		||||
      (value) => {
 | 
			
		||||
        if(value === AliasEntityType.CURRENT_TENANT){
 | 
			
		||||
        if(value === AliasEntityType.CURRENT_TENANT || value === AliasEntityType.CURRENT_USER) {
 | 
			
		||||
          this.modelValue.id = NULL_UUID;
 | 
			
		||||
        }
 | 
			
		||||
        this.updateView(value, this.modelValue.id);
 | 
			
		||||
@ -145,7 +145,9 @@ export class EntitySelectComponent implements ControlValueAccessor, OnInit, Afte
 | 
			
		||||
          entityType,
 | 
			
		||||
          id: this.modelValue.entityType !== entityType ? null : entityId
 | 
			
		||||
        };
 | 
			
		||||
        if (this.modelValue.entityType && (this.modelValue.id || this.modelValue.entityType === AliasEntityType.CURRENT_TENANT)) {
 | 
			
		||||
        if (this.modelValue.entityType && (this.modelValue.id ||
 | 
			
		||||
          this.modelValue.entityType === AliasEntityType.CURRENT_TENANT ||
 | 
			
		||||
          this.modelValue.entityType === AliasEntityType.CURRENT_USER)) {
 | 
			
		||||
          this.propagateChange(this.modelValue);
 | 
			
		||||
        } else {
 | 
			
		||||
          this.propagateChange(null);
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,8 @@ export enum EntityType {
 | 
			
		||||
 | 
			
		||||
export enum AliasEntityType {
 | 
			
		||||
  CURRENT_CUSTOMER = 'CURRENT_CUSTOMER',
 | 
			
		||||
  CURRENT_TENANT = 'CURRENT_TENANT'
 | 
			
		||||
  CURRENT_TENANT = 'CURRENT_TENANT',
 | 
			
		||||
  CURRENT_USER = 'CURRENT_USER'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface EntityTypeTranslation {
 | 
			
		||||
@ -229,6 +230,13 @@ export const entityTypeTranslations = new Map<EntityType | AliasEntityType, Enti
 | 
			
		||||
        type: 'entity.type-current-tenant',
 | 
			
		||||
        list: 'entity.type-current-tenant'
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
      AliasEntityType.CURRENT_USER,
 | 
			
		||||
      {
 | 
			
		||||
        type: 'entity.type-current-user',
 | 
			
		||||
        list: 'entity.type-current-user'
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  ]
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
@ -841,6 +841,7 @@
 | 
			
		||||
        "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'",
 | 
			
		||||
        "type-current-customer": "Current Customer",
 | 
			
		||||
        "type-current-tenant": "Current Tenant",
 | 
			
		||||
        "type-current-user": "Current User",
 | 
			
		||||
        "search": "Search entities",
 | 
			
		||||
        "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected",
 | 
			
		||||
        "entity-name": "Entity name",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user