Browse Source

Merge pull request #1043 from ViktorBasanets/master

Entity View
pull/1070/head
VoBa 8 years ago
committed by GitHub
parent
commit
009da885ad
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      application/src/main/data/upgrade/2.1.1/schema_update.cql
  2. 39
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  3. 2
      application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
  4. 2
      application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java
  5. 4
      common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
  6. 5
      common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java
  7. 5
      common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java
  8. 108
      dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
  9. 8
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
  10. 8
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
  11. 21
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  12. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  13. 50
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
  14. 9
      dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java
  15. 78
      dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
  16. 1
      dao/src/main/resources/cassandra/schema.cql

3
application/src/main/data/upgrade/2.1.1/schema_update.cql

@ -24,6 +24,7 @@ DROP TABLE IF EXISTS thingsboard.entity_views;
CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
id timeuuid, id timeuuid,
entity_id timeuuid, entity_id timeuuid,
entity_type text,
tenant_id timeuuid, tenant_id timeuuid,
customer_id timeuuid, customer_id timeuuid,
name text, name text,
@ -33,7 +34,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
search_text text, search_text text,
additional_info text, additional_info text,
PRIMARY KEY (id, entity_id, tenant_id, customer_id) PRIMARY KEY (id, entity_id, tenant_id, customer_id)
); );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS
SELECT * SELECT *

39
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -26,17 +26,11 @@ import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device; 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.Tenant;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
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.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.controller.HttpValidationCallback; import org.thingsboard.server.controller.HttpValidationCallback;
@ -44,6 +38,7 @@ import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
@ -66,6 +61,7 @@ public class AccessValidator {
public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";
public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";
public static final String ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND = "Entity-view with requested id wasn't found!";
@Autowired @Autowired
protected TenantService tenantService; protected TenantService tenantService;
@ -88,6 +84,9 @@ public class AccessValidator {
@Autowired @Autowired
protected RuleChainService ruleChainService; protected RuleChainService ruleChainService;
@Autowired
protected EntityViewService entityViewService;
private ExecutorService executor; private ExecutorService executor;
@PostConstruct @PostConstruct
@ -158,6 +157,9 @@ public class AccessValidator {
case TENANT: case TENANT:
validateTenant(currentUser, entityId, callback); validateTenant(currentUser, entityId, callback);
return; return;
case ENTITY_VIEW:
validateEntityView(currentUser, entityId, callback);
return;
default: default:
//TODO: add support of other entities //TODO: add support of other entities
throw new IllegalStateException("Not Implemented!"); throw new IllegalStateException("Not Implemented!");
@ -293,6 +295,27 @@ public class AccessValidator {
} }
} }
private void validateEntityView(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else {
ListenableFuture<EntityView> entityViewFuture = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId()));
Futures.addCallback(entityViewFuture, getCallback(callback, entityView -> {
if (entityView == null) {
return ValidationResult.entityNotFound(ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND);
} else {
if (!entityView.getTenantId().equals(currentUser.getTenantId())) {
return ValidationResult.accessDenied("Entity-view doesn't belong to the current Tenant!");
} else if (currentUser.isCustomerUser() && !entityView.getCustomerId().equals(currentUser.getCustomerId())) {
return ValidationResult.accessDenied("Entity-view doesn't belong to the current Customer!");
} else {
return ValidationResult.ok(entityView);
}
}
}), executor);
}
}
private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) { private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) {
return new FutureCallback<T>() { return new FutureCallback<T>() {
@Override @Override

2
application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java

@ -24,7 +24,7 @@ import java.util.Arrays;
@RunWith(ClasspathSuite.class) @RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({ @ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.controller.sql.EntityViewControllerSqlTest", "org.thingsboard.server.controller.sql.*Test",
}) })
public class ControllerSqlTestSuite { public class ControllerSqlTestSuite {

2
application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java

@ -16,9 +16,11 @@
package org.thingsboard.server.controller.nosql; package org.thingsboard.server.controller.nosql;
import org.thingsboard.server.controller.BaseEntityViewControllerTest; import org.thingsboard.server.controller.BaseEntityViewControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/** /**
* Created by Victor Basanets on 8/27/2017. * Created by Victor Basanets on 8/27/2017.
*/ */
@DaoNoSqlTest
public class EntityViewControllerNoSqlTest extends BaseEntityViewControllerTest { public class EntityViewControllerNoSqlTest extends BaseEntityViewControllerTest {
} }

4
common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java

@ -39,8 +39,8 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
private CustomerId customerId; private CustomerId customerId;
private String name; private String name;
private TelemetryEntityView keys; private TelemetryEntityView keys;
private Long tsBegin; private long startTs;
private Long tsEnd; private long endTs;
public EntityView() { public EntityView() {
super(); super();

5
common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java

@ -44,9 +44,4 @@ public class AttributesEntityView {
public AttributesEntityView(AttributesEntityView obj) { public AttributesEntityView(AttributesEntityView obj) {
this(obj.getCs(), obj.getSs(), obj.getSh()); this(obj.getCs(), obj.getSs(), obj.getSh());
} }
@Override
public String toString() {
return "{cs=" + cs + ", ss=" + ss + ", sh=" + sh + '}';
}
} }

5
common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java

@ -40,9 +40,4 @@ public class TelemetryEntityView {
public TelemetryEntityView(TelemetryEntityView obj) { public TelemetryEntityView(TelemetryEntityView obj) {
this(obj.getTimeseries(), obj.getAttributes()); this(obj.getTimeseries(), obj.getAttributes());
} }
@Override
public String toString() {
return "{timeseries=" + timeseries + ", attributes=" + attributes + '}';
}
} }

108
dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java

@ -0,0 +1,108 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.entityview;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Select;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.EntitySubtypeEntity;
import org.thingsboard.server.dao.model.nosql.EntityViewEntity;
import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
import org.thingsboard.server.dao.util.NoSqlDao;
import java.util.*;
import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
import static org.thingsboard.server.dao.model.ModelConstants.*;
/**
* Created by Victor Basanets on 9/06/2017.
*/
@Component
@Slf4j
@NoSqlDao
public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<EntityViewEntity, EntityView> implements EntityViewDao {
@Override
protected Class<EntityViewEntity> getColumnFamilyClass() {
return EntityViewEntity.class;
}
@Override
protected String getColumnFamilyName() {
return ENTITY_VIEW_TABLE_FAMILY_NAME;
}
@Override
public EntityView save(EntityView domain) {
EntityView savedEntityView = super.save(domain);
EntitySubtype entitySubtype = new EntitySubtype(savedEntityView.getTenantId(), EntityType.ENTITY_VIEW,
savedEntityView.getId().getEntityType().toString());
EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
executeWrite(saveStatement);
return savedEntityView;
}
@Override
public List<EntityView> findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink) {
log.debug("Try to find entity-views by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
List<EntityViewEntity> entityViewEntities =
findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Collections.singletonList(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found entity-views [{}] by tenantId [{}] and pageLink [{}]", entityViewEntities, tenantId, pageLink);
return DaoUtil.convertDataList(entityViewEntities);
}
@Override
public Optional<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String entityViewName) {
return Optional.ofNullable(DaoUtil.getData(
findOneByStatement(select().from(ENTITY_VIEW_TENANT_AND_NAME_VIEW_NAME).where()
.and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId))
.and(eq(ENTITY_VIEW_NAME_PROPERTY, entityViewName))))
);
}
@Override
public List<EntityView> findEntityViewByTenantIdAndEntityId(UUID tenantId, UUID entityId, TextPageLink pageLink) {
log.debug("Try to find entity-views by tenantId [{}], entityId[{}] and pageLink [{}]", tenantId, entityId, pageLink);
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, entityId),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found entity-views [{}] by tenantId [{}], entityId [{}] and pageLink [{}]", entityViewEntities, tenantId, entityId, pageLink);
return DaoUtil.convertDataList(entityViewEntities);
}
@Override
public List<EntityView> findEntityViewsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
return null;
}
@Override
public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(UUID tenantId, UUID customerId, UUID entityId, TextPageLink pageLink) {
return null;
}
}

8
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java

@ -29,6 +29,14 @@ import java.util.UUID;
*/ */
public interface EntityViewDao extends Dao<EntityView> { public interface EntityViewDao extends Dao<EntityView> {
/**
* Save or update device object
*
* @param entityView the entity-view object
* @return saved entity-view object
*/
EntityView save(EntityView entityView);
/** /**
* Find entity views by tenantId and page link. * Find entity views by tenantId and page link.
* *

8
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java

@ -15,12 +15,10 @@
*/ */
package org.thingsboard.server.dao.entityview; package org.thingsboard.server.dao.entityview;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
@ -57,4 +55,6 @@ public interface EntityViewService {
TextPageLink pageLink); TextPageLink pageLink);
void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId); void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId);
ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId);
} }

21
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java

@ -15,11 +15,14 @@
*/ */
package org.thingsboard.server.dao.entityview; package org.thingsboard.server.dao.entityview;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
@ -40,6 +43,8 @@ import org.thingsboard.server.dao.tenant.TenantDao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE;
import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validatePageLink; import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@ -67,6 +72,10 @@ public class EntityViewServiceImpl extends AbstractEntityService
@Autowired @Autowired
private CustomerDao customerDao; private CustomerDao customerDao;
@Autowired
private CacheManager cacheManager;
@Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override @Override
public EntityView findEntityViewById(EntityViewId entityViewId) { public EntityView findEntityViewById(EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId); log.trace("Executing findEntityViewById [{}]", entityViewId);
@ -82,6 +91,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
.orElse(null); .orElse(null);
} }
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")
@Override @Override
public EntityView saveEntityView(EntityView entityView) { public EntityView saveEntityView(EntityView entityView) {
log.trace("Executing save entity view [{}]", entityView); log.trace("Executing save entity view [{}]", entityView);
@ -106,12 +116,14 @@ public class EntityViewServiceImpl extends AbstractEntityService
@Override @Override
public void deleteEntityView(EntityViewId entityViewId) { public void deleteEntityView(EntityViewId entityViewId) {
log.trace("Executing deleteEntityView [{}]", entityViewId); log.trace("Executing deleteEntityView [{}]", entityViewId);
Cache cache = cacheManager.getCache(ENTITY_VIEW_CACHE);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
deleteEntityRelations(entityViewId); deleteEntityRelations(entityViewId);
EntityView entityView = entityViewDao.findById(entityViewId.getId()); EntityView entityView = entityViewDao.findById(entityViewId.getId());
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
list.add(entityView.getTenantId()); list.add(entityView.getTenantId());
list.add(entityView.getName()); list.add(entityView.getName());
cache.evict(list);
entityViewDao.removeById(entityViewId.getId()); entityViewDao.removeById(entityViewId.getId());
} }
@ -124,6 +136,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
return new TextPageData<>(entityViews, pageLink); return new TextPageData<>(entityViews, pageLink);
} }
@Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #entityId, #pageLink}")
@Override @Override
public TextPageData<EntityView> findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, public TextPageData<EntityView> findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId,
TextPageLink pageLink) { TextPageLink pageLink) {
@ -163,6 +176,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
return new TextPageData<>(entityViews, pageLink); return new TextPageData<>(entityViews, pageLink);
} }
@Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #customerId, #entityId, #pageLink}")
@Override @Override
public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId, public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId,
CustomerId customerId, CustomerId customerId,
@ -190,6 +204,13 @@ public class EntityViewServiceImpl extends AbstractEntityService
new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId); new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId);
} }
@Override
public ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
return entityViewDao.findByIdAsync(entityViewId.getId());
}
private DataValidator<EntityView> entityViewValidator = private DataValidator<EntityView> entityViewValidator =
new DataValidator<EntityView>() { new DataValidator<EntityView>() {

4
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -136,7 +136,6 @@ public class ModelConstants {
public static final String DEVICE_NAME_PROPERTY = "name"; public static final String DEVICE_NAME_PROPERTY = "name";
public static final String DEVICE_TYPE_PROPERTY = "type"; public static final String DEVICE_TYPE_PROPERTY = "type";
public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text"; public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
@ -152,11 +151,12 @@ public class ModelConstants {
public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY;
public static final String ENTITY_VIEW_TYPE_PROPERTY = "type_entity"; public static final String ENTITY_VIEW_TENANT_AND_NAME_VIEW_NAME = "entity_view_by_tenant_and_name";
public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys";
public static final String ENTITY_VIEW_TS_BEGIN_PROPERTY = "ts_begin"; public static final String ENTITY_VIEW_TS_BEGIN_PROPERTY = "ts_begin";
public static final String ENTITY_VIEW_TS_END_PROPERTY = "ts_end"; public static final String ENTITY_VIEW_TS_END_PROPERTY = "ts_end";
public static final String ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; public static final String ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "entity_view_by_tenant_and_search_text";
/** /**
* Cassandra audit log constants. * Cassandra audit log constants.

50
dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java

@ -19,24 +19,26 @@ import com.datastax.driver.core.utils.UUIDs;
import com.datastax.driver.mapping.annotations.PartitionKey; import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table; import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.objects.TelemetryEntityView; import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.SearchTextEntity;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY;
@ -57,6 +59,10 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY)
private UUID entityId; private UUID entityId;
@Enumerated(EnumType.STRING)
@Column(name = ENTITY_TYPE_PROPERTY)
private EntityType entityType;
@PartitionKey(value = 2) @PartitionKey(value = 2)
@Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY) @Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY)
private UUID tenantId; private UUID tenantId;
@ -68,9 +74,8 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY)
private String name; private String name;
@Type(type = "json")
@Column(name = ModelConstants.ENTITY_VIEW_KEYS_PROPERTY) @Column(name = ModelConstants.ENTITY_VIEW_KEYS_PROPERTY)
private JsonNode keys; private String keys;
@Column(name = ModelConstants.ENTITY_VIEW_TS_BEGIN_PROPERTY) @Column(name = ModelConstants.ENTITY_VIEW_TS_BEGIN_PROPERTY)
private String tsBegin; private String tsBegin;
@ -85,6 +90,8 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = ModelConstants.ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY) @Column(name = ModelConstants.ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo; private JsonNode additionalInfo;
private static final ObjectMapper mapper = new ObjectMapper();
public EntityViewEntity() { public EntityViewEntity() {
super(); super();
} }
@ -95,6 +102,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
} }
if (entityView.getEntityId() != null) { if (entityView.getEntityId() != null) {
this.entityId = entityView.getEntityId().getId(); this.entityId = entityView.getEntityId().getId();
this.entityType = entityView.getEntityId().getEntityType();
} }
if (entityView.getTenantId() != null) { if (entityView.getTenantId() != null) {
this.tenantId = entityView.getTenantId().getId(); this.tenantId = entityView.getTenantId().getId();
@ -103,13 +111,13 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
this.customerId = entityView.getCustomerId().getId(); this.customerId = entityView.getCustomerId().getId();
} }
this.name = entityView.getName(); this.name = entityView.getName();
// try { try {
// this.keys = entityView.getKeys(); this.keys = mapper.writeValueAsString(entityView.getKeys());
// } catch (IOException e) { } catch (IOException e) {
// e.printStackTrace(); e.printStackTrace();
// } }
this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0"; this.tsBegin = entityView.getStartTs() != 0L ? String.valueOf(entityView.getStartTs()) : "0";
this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0"; this.tsEnd = entityView.getEndTs() != 0L ? String.valueOf(entityView.getEndTs()) : "0";
this.searchText = entityView.getSearchText(); this.searchText = entityView.getSearchText();
this.additionalInfo = entityView.getAdditionalInfo(); this.additionalInfo = entityView.getAdditionalInfo();
} }
@ -124,7 +132,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
EntityView entityView = new EntityView(new EntityViewId(id)); EntityView entityView = new EntityView(new EntityViewId(id));
entityView.setCreatedTime(UUIDs.unixTimestamp(id)); entityView.setCreatedTime(UUIDs.unixTimestamp(id));
if (entityId != null) { if (entityId != null) {
entityView.setEntityId(new DeviceId(entityId)); entityView.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), entityId.toString()));
} }
if (tenantId != null) { if (tenantId != null) {
entityView.setTenantId(new TenantId(tenantId)); entityView.setTenantId(new TenantId(tenantId));
@ -133,13 +141,13 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
entityView.setCustomerId(new CustomerId(customerId)); entityView.setCustomerId(new CustomerId(customerId));
} }
entityView.setName(name); entityView.setName(name);
// try { try {
// entityView.setKeys((TelemetryEntityView) entityView.getKeys().toObject(keys)); entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class));
// } catch (IOException e) { } catch (IOException e) {
// e.printStackTrace(); e.printStackTrace();
// } }
entityView.setTsBegin(Long.parseLong(tsBegin)); entityView.setStartTs(Long.parseLong(tsBegin));
entityView.setTsEnd(Long.parseLong(tsEnd)); entityView.setEndTs(Long.parseLong(tsEnd));
entityView.setAdditionalInfo(additionalInfo); entityView.setAdditionalInfo(additionalInfo);
return entityView; return entityView;
} }

9
dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java

@ -34,7 +34,6 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.*; import javax.persistence.*;
import java.io.IOException; import java.io.IOException;
import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY;
/** /**
@ -106,8 +105,8 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0"; this.tsBegin = entityView.getStartTs() != 0L ? String.valueOf(entityView.getStartTs()) : "0";
this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0"; this.tsEnd = entityView.getEndTs() != 0L ? String.valueOf(entityView.getEndTs()) : "0";
this.searchText = entityView.getSearchText(); this.searchText = entityView.getSearchText();
this.additionalInfo = entityView.getAdditionalInfo(); this.additionalInfo = entityView.getAdditionalInfo();
} }
@ -142,8 +141,8 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
entityView.setTsBegin(Long.parseLong(tsBegin)); entityView.setStartTs(Long.parseLong(tsBegin));
entityView.setTsEnd(Long.parseLong(tsEnd)); entityView.setEndTs(Long.parseLong(tsEnd));
entityView.setAdditionalInfo(additionalInfo); entityView.setAdditionalInfo(additionalInfo);
return entityView; return entityView;
} }

78
dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java

@ -21,12 +21,18 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.kv.TsKvQuery; import org.thingsboard.server.common.data.kv.TsKvQuery;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.service.Validator;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -44,10 +50,17 @@ public class BaseTimeseriesService implements TimeseriesService {
@Autowired @Autowired
private TimeseriesDao timeseriesDao; private TimeseriesDao timeseriesDao;
@Autowired
private EntityViewService entityViewService;
@Override @Override
public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) { public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) {
validate(entityId); validate(entityId);
queries.forEach(query -> validate(query)); queries.forEach(query -> validate(query));
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries));
}
return timeseriesDao.findAllAsync(entityId, queries); return timeseriesDao.findAllAsync(entityId, queries);
} }
@ -56,7 +69,13 @@ public class BaseTimeseriesService implements TimeseriesService {
validate(entityId); validate(entityId);
List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size()); List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size());
keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key));
keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key))); if (false/*entityId.getEntityType().equals(EntityType.ENTITY_VIEW)*/) {
EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId);
Collection<String> newKeys = chooseKeysForEntityView(entityView, keys);
newKeys.forEach(newKey -> futures.add(timeseriesDao.findLatest(entityView.getEntityId(), newKey)));
} else {
keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key)));
}
return Futures.allAsList(futures); return Futures.allAsList(futures);
} }
@ -69,6 +88,11 @@ public class BaseTimeseriesService implements TimeseriesService {
@Override @Override
public ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry) { public ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry) {
validate(entityId); validate(entityId);
try {
checkForNonEntityView(entityId);
} catch (Exception e) {
e.printStackTrace();
}
if (tsKvEntry == null) { if (tsKvEntry == null) {
throw new IncorrectParameterException("Key value entry can't be null"); throw new IncorrectParameterException("Key value entry can't be null");
} }
@ -79,6 +103,11 @@ public class BaseTimeseriesService implements TimeseriesService {
@Override @Override
public ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) { public ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) {
try {
checkForNonEntityView(entityId);
} catch (Exception e) {
e.printStackTrace();
}
List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * INSERTS_PER_ENTRY); List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * INSERTS_PER_ENTRY);
for (TsKvEntry tsKvEntry : tsKvEntries) { for (TsKvEntry tsKvEntry : tsKvEntries) {
if (tsKvEntry == null) { if (tsKvEntry == null) {
@ -90,11 +119,47 @@ public class BaseTimeseriesService implements TimeseriesService {
} }
private void saveAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) { private void saveAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
try {
checkForNonEntityView(entityId);
} catch (Exception e) {
e.printStackTrace();
}
futures.add(timeseriesDao.savePartition(entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl)); futures.add(timeseriesDao.savePartition(entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
futures.add(timeseriesDao.saveLatest(entityId, tsKvEntry)); futures.add(timeseriesDao.saveLatest(entityId, tsKvEntry));
futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl)); futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl));
} }
private List<TsKvQuery> updateQueriesForEntityView(EntityView entityView, List<TsKvQuery> queries) {
List<TsKvQuery> newQueries = new ArrayList<>();
entityView.getKeys().getTimeseries()
.forEach(viewKey -> queries
.forEach(query -> {
if (query.getKey().equals(viewKey)) {
if (entityView.getStartTs() == 0 && entityView.getEndTs() == 0) {
newQueries.add(updateQuery(query.getStartTs(), query.getEndTs(), viewKey, query));
} else if (entityView.getStartTs() == 0 && entityView.getEndTs() != 0) {
newQueries.add(updateQuery(query.getStartTs(), entityView.getEndTs(), viewKey, query));
} else if (entityView.getStartTs() != 0 && entityView.getEndTs() == 0) {
newQueries.add(updateQuery(entityView.getStartTs(), query.getEndTs(), viewKey, query));
} else {
newQueries.add(updateQuery(entityView.getStartTs(), entityView.getEndTs(), viewKey, query));
}
}}));
return newQueries;
}
@Deprecated /*Will be a modified*/
private Collection<String> chooseKeysForEntityView(EntityView entityView, Collection<String> keys) {
Collection<String> newKeys = new ArrayList<>();
entityView.getKeys().getTimeseries()
.forEach(viewKey -> keys
.forEach(key -> {
if (key.equals(viewKey)) {
newKeys.add(key);
}}));
return newKeys;
}
private static void validate(EntityId entityId) { private static void validate(EntityId entityId) {
Validator.validateEntityId(entityId, "Incorrect entityId " + entityId); Validator.validateEntityId(entityId, "Incorrect entityId " + entityId);
} }
@ -108,4 +173,15 @@ public class BaseTimeseriesService implements TimeseriesService {
throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty"); throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty");
} }
} }
private static TsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, TsKvQuery query) {
return startTs <= query.getStartTs() && endTs >= query.getEndTs() ? query :
new BaseTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation());
}
private static void checkForNonEntityView(EntityId entityId) throws Exception {
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
throw new Exception("Entity-views were read only");
}
}
} }

1
dao/src/main/resources/cassandra/schema.cql

@ -642,6 +642,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node (
CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
id timeuuid, id timeuuid,
entity_id timeuuid, entity_id timeuuid,
entity_type text,
tenant_id timeuuid, tenant_id timeuuid,
customer_id timeuuid, customer_id timeuuid,
name text, name text,

Loading…
Cancel
Save