thingsboard-developers
Changes
application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java 2(+2 -0)
common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java 5(+0 -5)
Details
diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql
index d9ba517..1329a0b 100644
--- a/application/src/main/data/upgrade/2.1.1/schema_update.cql
+++ b/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 (
id timeuuid,
entity_id timeuuid,
+ entity_type text,
tenant_id timeuuid,
customer_id timeuuid,
name text,
@@ -33,7 +34,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
search_text text,
additional_info text,
PRIMARY KEY (id, entity_id, tenant_id, customer_id)
- );
+);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS
SELECT *
diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
index 600820e..7f4f23a 100644
--- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
+++ b/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.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.asset.Asset;
import org.thingsboard.server.common.data.exception.ThingsboardException;
-import org.thingsboard.server.common.data.id.AssetId;
-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.id.*;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleNode;
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.customer.CustomerService;
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.tenant.TenantService;
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 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 ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND = "Entity-view with requested id wasn't found!";
@Autowired
protected TenantService tenantService;
@@ -88,6 +84,9 @@ public class AccessValidator {
@Autowired
protected RuleChainService ruleChainService;
+ @Autowired
+ protected EntityViewService entityViewService;
+
private ExecutorService executor;
@PostConstruct
@@ -158,6 +157,9 @@ public class AccessValidator {
case TENANT:
validateTenant(currentUser, entityId, callback);
return;
+ case ENTITY_VIEW:
+ validateEntityView(currentUser, entityId, callback);
+ return;
default:
//TODO: add support of other entities
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) {
return new FutureCallback<T>() {
@Override
diff --git a/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
index a9e94e9..c8a5da8 100644
--- a/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
+++ b/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
@@ -24,7 +24,7 @@ import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
- "org.thingsboard.server.controller.sql.EntityViewControllerSqlTest",
+ "org.thingsboard.server.controller.sql.*Test",
})
public class ControllerSqlTestSuite {
diff --git a/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java
index ad066fc..404e4e2 100644
--- a/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java
@@ -16,9 +16,11 @@
package org.thingsboard.server.controller.nosql;
import org.thingsboard.server.controller.BaseEntityViewControllerTest;
+import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Victor Basanets on 8/27/2017.
*/
+@DaoNoSqlTest
public class EntityViewControllerNoSqlTest extends BaseEntityViewControllerTest {
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
index 813a9ac..49dd209 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
+++ b/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 String name;
private TelemetryEntityView keys;
- private Long tsBegin;
- private Long tsEnd;
+ private long startTs;
+ private long endTs;
public EntityView() {
super();
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java
index b1d270c..1c32579 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java
@@ -44,9 +44,4 @@ public class AttributesEntityView {
public AttributesEntityView(AttributesEntityView obj) {
this(obj.getCs(), obj.getSs(), obj.getSh());
}
-
- @Override
- public String toString() {
- return "{cs=" + cs + ", ss=" + ss + ", sh=" + sh + '}';
- }
}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java
index e7398e6..c899c65 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java
@@ -40,9 +40,4 @@ public class TelemetryEntityView {
public TelemetryEntityView(TelemetryEntityView obj) {
this(obj.getTimeseries(), obj.getAttributes());
}
-
- @Override
- public String toString() {
- return "{timeseries=" + timeseries + ", attributes=" + attributes + '}';
- }
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java
new file mode 100644
index 0000000..5716460
--- /dev/null
+++ b/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;
+ }
+}
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 25404db..5cbbef8 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
@@ -30,6 +30,14 @@ import java.util.UUID;
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.
*
* @param tenantId the tenantId
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
index 943d35e..6de86be 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
@@ -15,12 +15,10 @@
*/
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.EntityView;
-import org.thingsboard.server.common.data.id.CustomerId;
-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.id.*;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@@ -57,4 +55,6 @@ public interface EntityViewService {
TextPageLink pageLink);
void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId);
+
+ ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId);
}
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 6554083..31d3b30 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
@@ -15,11 +15,14 @@
*/
package org.thingsboard.server.dao.entityview;
+import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Customer;
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.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.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@@ -67,6 +72,10 @@ public class EntityViewServiceImpl extends AbstractEntityService
@Autowired
private CustomerDao customerDao;
+ @Autowired
+ private CacheManager cacheManager;
+
+ @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView findEntityViewById(EntityViewId entityViewId) {
log.trace("Executing findEntityViewById [{}]", entityViewId);
@@ -82,6 +91,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
.orElse(null);
}
+ @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")
@Override
public EntityView saveEntityView(EntityView entityView) {
log.trace("Executing save entity view [{}]", entityView);
@@ -106,12 +116,14 @@ public class EntityViewServiceImpl extends AbstractEntityService
@Override
public void deleteEntityView(EntityViewId entityViewId) {
log.trace("Executing deleteEntityView [{}]", entityViewId);
+ Cache cache = cacheManager.getCache(ENTITY_VIEW_CACHE);
validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId);
deleteEntityRelations(entityViewId);
EntityView entityView = entityViewDao.findById(entityViewId.getId());
List<Object> list = new ArrayList<>();
list.add(entityView.getTenantId());
list.add(entityView.getName());
+ cache.evict(list);
entityViewDao.removeById(entityViewId.getId());
}
@@ -124,6 +136,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
return new TextPageData<>(entityViews, pageLink);
}
+ @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #entityId, #pageLink}")
@Override
public TextPageData<EntityView> findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId,
TextPageLink pageLink) {
@@ -163,6 +176,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
return new TextPageData<>(entityViews, pageLink);
}
+ @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #customerId, #entityId, #pageLink}")
@Override
public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId,
CustomerId customerId,
@@ -190,6 +204,13 @@ public class EntityViewServiceImpl extends AbstractEntityService
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 =
new DataValidator<EntityView>() {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index d06ddbd..6932bea 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/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_TYPE_PROPERTY = "type";
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_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";
@@ -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_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_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_TS_BEGIN_PROPERTY = "ts_begin";
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_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "entity_view_by_tenant_and_search_text";
/**
* Cassandra audit log constants.
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
index 65914dd..990ff70 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
+++ b/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.Table;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
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.id.CustomerId;
-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.id.*;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.model.SearchTextEntity;
import javax.persistence.Column;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
import java.io.IOException;
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.ID_PROPERTY;
@@ -57,6 +59,10 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY)
private UUID entityId;
+ @Enumerated(EnumType.STRING)
+ @Column(name = ENTITY_TYPE_PROPERTY)
+ private EntityType entityType;
+
@PartitionKey(value = 2)
@Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY)
private UUID tenantId;
@@ -68,9 +74,8 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY)
private String name;
- @Type(type = "json")
@Column(name = ModelConstants.ENTITY_VIEW_KEYS_PROPERTY)
- private JsonNode keys;
+ private String keys;
@Column(name = ModelConstants.ENTITY_VIEW_TS_BEGIN_PROPERTY)
private String tsBegin;
@@ -85,6 +90,8 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
@Column(name = ModelConstants.ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo;
+ private static final ObjectMapper mapper = new ObjectMapper();
+
public EntityViewEntity() {
super();
}
@@ -95,6 +102,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
}
if (entityView.getEntityId() != null) {
this.entityId = entityView.getEntityId().getId();
+ this.entityType = entityView.getEntityId().getEntityType();
}
if (entityView.getTenantId() != null) {
this.tenantId = entityView.getTenantId().getId();
@@ -103,13 +111,13 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
this.customerId = entityView.getCustomerId().getId();
}
this.name = entityView.getName();
-// try {
-// this.keys = entityView.getKeys();
-// } catch (IOException e) {
-// e.printStackTrace();
-// }
- this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0";
- this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0";
+ try {
+ this.keys = mapper.writeValueAsString(entityView.getKeys());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ this.tsBegin = entityView.getStartTs() != 0L ? String.valueOf(entityView.getStartTs()) : "0";
+ this.tsEnd = entityView.getEndTs() != 0L ? String.valueOf(entityView.getEndTs()) : "0";
this.searchText = entityView.getSearchText();
this.additionalInfo = entityView.getAdditionalInfo();
}
@@ -124,7 +132,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
EntityView entityView = new EntityView(new EntityViewId(id));
entityView.setCreatedTime(UUIDs.unixTimestamp(id));
if (entityId != null) {
- entityView.setEntityId(new DeviceId(entityId));
+ entityView.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), entityId.toString()));
}
if (tenantId != null) {
entityView.setTenantId(new TenantId(tenantId));
@@ -133,13 +141,13 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
entityView.setCustomerId(new CustomerId(customerId));
}
entityView.setName(name);
-// try {
-// entityView.setKeys((TelemetryEntityView) entityView.getKeys().toObject(keys));
-// } catch (IOException e) {
-// e.printStackTrace();
-// }
- entityView.setTsBegin(Long.parseLong(tsBegin));
- entityView.setTsEnd(Long.parseLong(tsEnd));
+ try {
+ entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ entityView.setStartTs(Long.parseLong(tsBegin));
+ entityView.setEndTs(Long.parseLong(tsEnd));
entityView.setAdditionalInfo(additionalInfo);
return entityView;
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java
index 9136717..a89d3de 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java
+++ b/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 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;
/**
@@ -106,8 +105,8 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
} catch (IOException e) {
e.printStackTrace();
}
- this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0";
- this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0";
+ this.tsBegin = entityView.getStartTs() != 0L ? String.valueOf(entityView.getStartTs()) : "0";
+ this.tsEnd = entityView.getEndTs() != 0L ? String.valueOf(entityView.getEndTs()) : "0";
this.searchText = entityView.getSearchText();
this.additionalInfo = entityView.getAdditionalInfo();
}
@@ -142,8 +141,8 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc
} catch (IOException e) {
e.printStackTrace();
}
- entityView.setTsBegin(Long.parseLong(tsBegin));
- entityView.setTsEnd(Long.parseLong(tsEnd));
+ entityView.setStartTs(Long.parseLong(tsBegin));
+ entityView.setEndTs(Long.parseLong(tsEnd));
entityView.setAdditionalInfo(additionalInfo);
return entityView;
}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
index c981378..9b837b2 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
+++ b/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 org.springframework.beans.factory.annotation.Autowired;
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.EntityViewId;
+import org.thingsboard.server.common.data.kv.BaseTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
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.service.Validator;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -44,10 +50,17 @@ public class BaseTimeseriesService implements TimeseriesService {
@Autowired
private TimeseriesDao timeseriesDao;
+ @Autowired
+ private EntityViewService entityViewService;
+
@Override
public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) {
validate(entityId);
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);
}
@@ -56,7 +69,13 @@ public class BaseTimeseriesService implements TimeseriesService {
validate(entityId);
List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size());
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);
}
@@ -69,6 +88,11 @@ public class BaseTimeseriesService implements TimeseriesService {
@Override
public ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry) {
validate(entityId);
+ try {
+ checkForNonEntityView(entityId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
if (tsKvEntry == null) {
throw new IncorrectParameterException("Key value entry can't be null");
}
@@ -79,6 +103,11 @@ public class BaseTimeseriesService implements TimeseriesService {
@Override
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);
for (TsKvEntry tsKvEntry : tsKvEntries) {
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) {
+ try {
+ checkForNonEntityView(entityId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
futures.add(timeseriesDao.savePartition(entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
futures.add(timeseriesDao.saveLatest(entityId, tsKvEntry));
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) {
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");
}
}
+
+ 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");
+ }
+ }
}
diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql
index bdd413d..68f196f 100644
--- a/dao/src/main/resources/cassandra/schema.cql
+++ b/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 (
id timeuuid,
entity_id timeuuid,
+ entity_type text,
tenant_id timeuuid,
customer_id timeuuid,
name text,