diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index b1b6b6b1e3..c67f260422 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -17,11 +17,13 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.ListenableFuture; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -53,6 +55,7 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -347,14 +350,7 @@ public class EntityViewController extends BaseController { checkNotNull(query.getEntityViewTypes()); checkEntityId(query.getParameters().getEntityId(), Operation.READ); List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(getTenantId(), query).get()); - entityViews = entityViews.stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); + entityViews = filterEntityViewsByReadPermission(entityViews); return entityViews; } @@ -459,18 +455,39 @@ public class EntityViewController extends BaseController { } else { nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); } - List filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> { - try { - accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); - return true; - } catch (ThingsboardException e) { - return false; - } - }).collect(Collectors.toList()); + List filteredEntityViews = filterEntityViewsByReadPermission(nonFilteredResult.getData()); PageData filteredResult = new PageData<>(filteredEntityViews, nonFilteredResult.getTotalPages(), nonFilteredResult.getTotalElements(), nonFilteredResult.hasNext()); return checkNotNull(filteredResult); } + + @ApiOperation(value = "Get Entity Views By Ids (getEntityViewsByIds)", + notes = "Requested entity views must be owned by tenant or assigned to customer which user is performing the request. ") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = "/entityViews", params = {"entityViewIds"}) + public List getEntityViewsByIds(@Parameter(description = "A list of entity view ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true) + @RequestParam("entityViewIds") String[] strEntityViewIds) throws ThingsboardException, ExecutionException, InterruptedException { + checkArrayParameter("entityViewIds", strEntityViewIds); + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + List entityViewIds = new ArrayList<>(); + for (String strEntityViewId : strEntityViewIds) { + entityViewIds.add(new EntityViewId(toUUID(strEntityViewId))); + } + List entityViews = checkNotNull(entityViewService.findEntityViewsByTenantIdAndIdsAsync(tenantId, entityViewIds).get()); + return filterEntityViewsByReadPermission(entityViews); + } + + private List filterEntityViewsByReadPermission(List entityViews) { + return entityViews.stream().filter(entityView -> { + try { + return accessControlService.hasPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView); + } catch (ThingsboardException e) { + return false; + } + }).collect(Collectors.toList()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java index 10b49dfe1d..5b4c9d017b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java @@ -68,11 +68,14 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.stream.Collectors; import static java.util.concurrent.TimeUnit.HOURS; @@ -148,6 +151,36 @@ public class EntityViewControllerTest extends AbstractControllerTest { assertEquals(savedView, foundView); } + @Test + public void testFindEntityViewByIds() throws Exception { + List assetProfiles = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + assetProfiles.add(getNewSavedEntityView("Test entity view " + i)); + } + + List expected = assetProfiles.subList(5, 15); + + String idsParam = expected.stream() + .map(ap -> ap.getId().getId().toString()) + .collect(Collectors.joining(",")); + EntityView[] foundEntityViews = doGet("/api/entityViews?entityViewIds=" + idsParam, EntityView[].class); + + Assert.assertNotNull(foundEntityViews); + Assert.assertEquals(expected.size(), foundEntityViews.length); + + Map infoById = Arrays.stream(foundEntityViews) + .collect(Collectors.toMap(info -> info.getId().getId(), Function.identity())); + + for (EntityView entityView : expected) { + UUID id = entityView.getId().getId(); + EntityView view = infoById.get(id); + Assert.assertNotNull("Entity view not found for id " + id, view); + + Assert.assertEquals(entityView.getId(), view.getId()); + Assert.assertEquals(entityView.getName(), view.getName()); + } + } + @Test public void testSaveEntityView() throws Exception { String name = "Test entity view"; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java index 5ec2dfc20e..e64cd0dd6d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java @@ -65,6 +65,8 @@ public interface EntityViewService extends EntityDaoService { PageData findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink); + ListenableFuture> findEntityViewsByTenantIdAndIdsAsync(TenantId tenantId, List entityViewIds); + PageData findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink); PageData findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, PageLink pageLink, String type); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java index 17f84c7b06..52d077a3bb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java @@ -169,6 +169,8 @@ public interface EntityViewDao extends Dao, ExportableEntityDao> findEntityViewsByTenantIdAndIdsAsync(UUID tenantId, List entityViewIds); + /** * Find entity views by tenantId, edgeId, type and page link. * diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 3128036e0e..e135555659 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -62,7 +62,9 @@ import java.util.Optional; import java.util.stream.Collectors; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateIds; import static org.thingsboard.server.dao.service.Validator.validatePageLink; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -255,6 +257,14 @@ public class EntityViewServiceImpl extends CachedVersionedEntityService> findEntityViewsByTenantIdAndIdsAsync(TenantId tenantId, List entityViewIds) { + log.trace("Executing findEntityViewsByTenantIdAndIdsAsync, tenantId [{}], entityViewIds [{}]", tenantId, entityViewIds); + validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + validateIds(entityViewIds, ids -> "Incorrect entityViewIds " + ids); + return entityViewDao.findEntityViewsByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(entityViewIds)); + } + @Override public PageData findEntityViewInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) { log.trace("Executing findEntityViewInfosByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java index 6094e9b171..c61ec83335 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -122,6 +122,8 @@ public interface EntityViewRepository extends JpaRepository findEntityViewsByTenantIdAndIdIn(UUID tenantId, List entityViewIds); + @Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId") List findTenantEntityViewTypes(@Param("tenantId") UUID tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index 44d8a09ff4..fe3f63825f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -187,6 +187,11 @@ public class JpaEntityViewDao extends JpaAbstractDao> findEntityViewsByTenantIdAndIdsAsync(UUID tenantId, List entityViewIds) { + return service.submit(() -> DaoUtil.convertDataList(entityViewRepository.findEntityViewsByTenantIdAndIdIn(tenantId, entityViewIds))); + } + @Override public PageData findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink) { log.debug("Try to find entity views by tenantId [{}], edgeId [{}], type [{}] and pageLink [{}]", tenantId, edgeId, type, pageLink);