Commit 009da885ad9486ffaa29325c7349f627d51fdf86
Committed by
GitHub
Merge pull request #1043 from ViktorBasanets/master
Entity View
Showing
16 changed files
with
292 additions
and
55 deletions
... | ... | @@ -24,6 +24,7 @@ DROP TABLE IF EXISTS thingsboard.entity_views; |
24 | 24 | CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( |
25 | 25 | id timeuuid, |
26 | 26 | entity_id timeuuid, |
27 | + entity_type text, | |
27 | 28 | tenant_id timeuuid, |
28 | 29 | customer_id timeuuid, |
29 | 30 | name text, |
... | ... | @@ -33,7 +34,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( |
33 | 34 | search_text text, |
34 | 35 | additional_info text, |
35 | 36 | PRIMARY KEY (id, entity_id, tenant_id, customer_id) |
36 | - ); | |
37 | +); | |
37 | 38 | |
38 | 39 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS |
39 | 40 | SELECT * | ... | ... |
... | ... | @@ -26,17 +26,11 @@ import org.springframework.stereotype.Component; |
26 | 26 | import org.springframework.web.context.request.async.DeferredResult; |
27 | 27 | import org.thingsboard.server.common.data.Customer; |
28 | 28 | import org.thingsboard.server.common.data.Device; |
29 | +import org.thingsboard.server.common.data.EntityView; | |
29 | 30 | import org.thingsboard.server.common.data.Tenant; |
30 | 31 | import org.thingsboard.server.common.data.asset.Asset; |
31 | 32 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
32 | -import org.thingsboard.server.common.data.id.AssetId; | |
33 | -import org.thingsboard.server.common.data.id.CustomerId; | |
34 | -import org.thingsboard.server.common.data.id.DeviceId; | |
35 | -import org.thingsboard.server.common.data.id.EntityId; | |
36 | -import org.thingsboard.server.common.data.id.EntityIdFactory; | |
37 | -import org.thingsboard.server.common.data.id.RuleChainId; | |
38 | -import org.thingsboard.server.common.data.id.RuleNodeId; | |
39 | -import org.thingsboard.server.common.data.id.TenantId; | |
33 | +import org.thingsboard.server.common.data.id.*; | |
40 | 34 | import org.thingsboard.server.common.data.rule.RuleChain; |
41 | 35 | import org.thingsboard.server.common.data.rule.RuleNode; |
42 | 36 | import org.thingsboard.server.controller.HttpValidationCallback; |
... | ... | @@ -44,6 +38,7 @@ import org.thingsboard.server.dao.alarm.AlarmService; |
44 | 38 | import org.thingsboard.server.dao.asset.AssetService; |
45 | 39 | import org.thingsboard.server.dao.customer.CustomerService; |
46 | 40 | import org.thingsboard.server.dao.device.DeviceService; |
41 | +import org.thingsboard.server.dao.entityview.EntityViewService; | |
47 | 42 | import org.thingsboard.server.dao.rule.RuleChainService; |
48 | 43 | import org.thingsboard.server.dao.tenant.TenantService; |
49 | 44 | import org.thingsboard.server.dao.user.UserService; |
... | ... | @@ -66,6 +61,7 @@ public class AccessValidator { |
66 | 61 | public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; |
67 | 62 | public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; |
68 | 63 | public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; |
64 | + public static final String ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND = "Entity-view with requested id wasn't found!"; | |
69 | 65 | |
70 | 66 | @Autowired |
71 | 67 | protected TenantService tenantService; |
... | ... | @@ -88,6 +84,9 @@ public class AccessValidator { |
88 | 84 | @Autowired |
89 | 85 | protected RuleChainService ruleChainService; |
90 | 86 | |
87 | + @Autowired | |
88 | + protected EntityViewService entityViewService; | |
89 | + | |
91 | 90 | private ExecutorService executor; |
92 | 91 | |
93 | 92 | @PostConstruct |
... | ... | @@ -158,6 +157,9 @@ public class AccessValidator { |
158 | 157 | case TENANT: |
159 | 158 | validateTenant(currentUser, entityId, callback); |
160 | 159 | return; |
160 | + case ENTITY_VIEW: | |
161 | + validateEntityView(currentUser, entityId, callback); | |
162 | + return; | |
161 | 163 | default: |
162 | 164 | //TODO: add support of other entities |
163 | 165 | throw new IllegalStateException("Not Implemented!"); |
... | ... | @@ -293,6 +295,27 @@ public class AccessValidator { |
293 | 295 | } |
294 | 296 | } |
295 | 297 | |
298 | + private void validateEntityView(final SecurityUser currentUser, EntityId entityId, FutureCallback<ValidationResult> callback) { | |
299 | + if (currentUser.isSystemAdmin()) { | |
300 | + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); | |
301 | + } else { | |
302 | + ListenableFuture<EntityView> entityViewFuture = entityViewService.findEntityViewByIdAsync(new EntityViewId(entityId.getId())); | |
303 | + Futures.addCallback(entityViewFuture, getCallback(callback, entityView -> { | |
304 | + if (entityView == null) { | |
305 | + return ValidationResult.entityNotFound(ENTITY_VIEW_WITH_REQUESTED_ID_NOT_FOUND); | |
306 | + } else { | |
307 | + if (!entityView.getTenantId().equals(currentUser.getTenantId())) { | |
308 | + return ValidationResult.accessDenied("Entity-view doesn't belong to the current Tenant!"); | |
309 | + } else if (currentUser.isCustomerUser() && !entityView.getCustomerId().equals(currentUser.getCustomerId())) { | |
310 | + return ValidationResult.accessDenied("Entity-view doesn't belong to the current Customer!"); | |
311 | + } else { | |
312 | + return ValidationResult.ok(entityView); | |
313 | + } | |
314 | + } | |
315 | + }), executor); | |
316 | + } | |
317 | + } | |
318 | + | |
296 | 319 | private <T, V> FutureCallback<T> getCallback(FutureCallback<ValidationResult> callback, Function<T, ValidationResult<V>> transformer) { |
297 | 320 | return new FutureCallback<T>() { |
298 | 321 | @Override | ... | ... |
... | ... | @@ -24,7 +24,7 @@ import java.util.Arrays; |
24 | 24 | |
25 | 25 | @RunWith(ClasspathSuite.class) |
26 | 26 | @ClasspathSuite.ClassnameFilters({ |
27 | - "org.thingsboard.server.controller.sql.EntityViewControllerSqlTest", | |
27 | + "org.thingsboard.server.controller.sql.*Test", | |
28 | 28 | }) |
29 | 29 | public class ControllerSqlTestSuite { |
30 | 30 | ... | ... |
application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java
... | ... | @@ -16,9 +16,11 @@ |
16 | 16 | package org.thingsboard.server.controller.nosql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.controller.BaseEntityViewControllerTest; |
19 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | |
19 | 20 | |
20 | 21 | /** |
21 | 22 | * Created by Victor Basanets on 8/27/2017. |
22 | 23 | */ |
24 | +@DaoNoSqlTest | |
23 | 25 | public class EntityViewControllerNoSqlTest extends BaseEntityViewControllerTest { |
24 | 26 | } | ... | ... |
... | ... | @@ -39,8 +39,8 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> |
39 | 39 | private CustomerId customerId; |
40 | 40 | private String name; |
41 | 41 | private TelemetryEntityView keys; |
42 | - private Long tsBegin; | |
43 | - private Long tsEnd; | |
42 | + private long startTs; | |
43 | + private long endTs; | |
44 | 44 | |
45 | 45 | public EntityView() { |
46 | 46 | super(); | ... | ... |
... | ... | @@ -44,9 +44,4 @@ public class AttributesEntityView { |
44 | 44 | public AttributesEntityView(AttributesEntityView obj) { |
45 | 45 | this(obj.getCs(), obj.getSs(), obj.getSh()); |
46 | 46 | } |
47 | - | |
48 | - @Override | |
49 | - public String toString() { | |
50 | - return "{cs=" + cs + ", ss=" + ss + ", sh=" + sh + '}'; | |
51 | - } | |
52 | 47 | } | ... | ... |
... | ... | @@ -40,9 +40,4 @@ public class TelemetryEntityView { |
40 | 40 | public TelemetryEntityView(TelemetryEntityView obj) { |
41 | 41 | this(obj.getTimeseries(), obj.getAttributes()); |
42 | 42 | } |
43 | - | |
44 | - @Override | |
45 | - public String toString() { | |
46 | - return "{timeseries=" + timeseries + ", attributes=" + attributes + '}'; | |
47 | - } | |
48 | 43 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.entityview; | |
17 | + | |
18 | +import com.datastax.driver.core.Statement; | |
19 | +import com.datastax.driver.core.querybuilder.Select; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
21 | +import org.springframework.stereotype.Component; | |
22 | +import org.thingsboard.server.common.data.EntitySubtype; | |
23 | +import org.thingsboard.server.common.data.EntityType; | |
24 | +import org.thingsboard.server.common.data.EntityView; | |
25 | +import org.thingsboard.server.common.data.page.TextPageLink; | |
26 | +import org.thingsboard.server.dao.DaoUtil; | |
27 | +import org.thingsboard.server.dao.model.EntitySubtypeEntity; | |
28 | +import org.thingsboard.server.dao.model.nosql.EntityViewEntity; | |
29 | +import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; | |
30 | +import org.thingsboard.server.dao.util.NoSqlDao; | |
31 | + | |
32 | +import java.util.*; | |
33 | + | |
34 | +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; | |
35 | +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; | |
36 | +import static org.thingsboard.server.dao.model.ModelConstants.*; | |
37 | + | |
38 | +/** | |
39 | + * Created by Victor Basanets on 9/06/2017. | |
40 | + */ | |
41 | +@Component | |
42 | +@Slf4j | |
43 | +@NoSqlDao | |
44 | +public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<EntityViewEntity, EntityView> implements EntityViewDao { | |
45 | + | |
46 | + @Override | |
47 | + protected Class<EntityViewEntity> getColumnFamilyClass() { | |
48 | + return EntityViewEntity.class; | |
49 | + } | |
50 | + | |
51 | + @Override | |
52 | + protected String getColumnFamilyName() { | |
53 | + return ENTITY_VIEW_TABLE_FAMILY_NAME; | |
54 | + } | |
55 | + | |
56 | + @Override | |
57 | + public EntityView save(EntityView domain) { | |
58 | + EntityView savedEntityView = super.save(domain); | |
59 | + EntitySubtype entitySubtype = new EntitySubtype(savedEntityView.getTenantId(), EntityType.ENTITY_VIEW, | |
60 | + savedEntityView.getId().getEntityType().toString()); | |
61 | + EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype); | |
62 | + Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity); | |
63 | + executeWrite(saveStatement); | |
64 | + return savedEntityView; | |
65 | + } | |
66 | + | |
67 | + @Override | |
68 | + public List<EntityView> findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink) { | |
69 | + log.debug("Try to find entity-views by tenantId [{}] and pageLink [{}]", tenantId, pageLink); | |
70 | + List<EntityViewEntity> entityViewEntities = | |
71 | + findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | |
72 | + Collections.singletonList(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId)), pageLink); | |
73 | + | |
74 | + log.trace("Found entity-views [{}] by tenantId [{}] and pageLink [{}]", entityViewEntities, tenantId, pageLink); | |
75 | + return DaoUtil.convertDataList(entityViewEntities); | |
76 | + } | |
77 | + | |
78 | + @Override | |
79 | + public Optional<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String entityViewName) { | |
80 | + return Optional.ofNullable(DaoUtil.getData( | |
81 | + findOneByStatement(select().from(ENTITY_VIEW_TENANT_AND_NAME_VIEW_NAME).where() | |
82 | + .and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId)) | |
83 | + .and(eq(ENTITY_VIEW_NAME_PROPERTY, entityViewName)))) | |
84 | + ); | |
85 | + } | |
86 | + | |
87 | + @Override | |
88 | + public List<EntityView> findEntityViewByTenantIdAndEntityId(UUID tenantId, UUID entityId, TextPageLink pageLink) { | |
89 | + log.debug("Try to find entity-views by tenantId [{}], entityId[{}] and pageLink [{}]", tenantId, entityId, pageLink); | |
90 | + List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | |
91 | + Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, entityId), | |
92 | + eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), | |
93 | + pageLink); | |
94 | + | |
95 | + log.trace("Found entity-views [{}] by tenantId [{}], entityId [{}] and pageLink [{}]", entityViewEntities, tenantId, entityId, pageLink); | |
96 | + return DaoUtil.convertDataList(entityViewEntities); | |
97 | + } | |
98 | + | |
99 | + @Override | |
100 | + public List<EntityView> findEntityViewsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { | |
101 | + return null; | |
102 | + } | |
103 | + | |
104 | + @Override | |
105 | + public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(UUID tenantId, UUID customerId, UUID entityId, TextPageLink pageLink) { | |
106 | + return null; | |
107 | + } | |
108 | +} | ... | ... |
... | ... | @@ -30,6 +30,14 @@ import java.util.UUID; |
30 | 30 | public interface EntityViewDao extends Dao<EntityView> { |
31 | 31 | |
32 | 32 | /** |
33 | + * Save or update device object | |
34 | + * | |
35 | + * @param entityView the entity-view object | |
36 | + * @return saved entity-view object | |
37 | + */ | |
38 | + EntityView save(EntityView entityView); | |
39 | + | |
40 | + /** | |
33 | 41 | * Find entity views by tenantId and page link. |
34 | 42 | * |
35 | 43 | * @param tenantId the tenantId | ... | ... |
... | ... | @@ -15,12 +15,10 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.entityview; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
18 | 19 | import org.thingsboard.server.common.data.EntityType; |
19 | 20 | import org.thingsboard.server.common.data.EntityView; |
20 | -import org.thingsboard.server.common.data.id.CustomerId; | |
21 | -import org.thingsboard.server.common.data.id.EntityId; | |
22 | -import org.thingsboard.server.common.data.id.EntityViewId; | |
23 | -import org.thingsboard.server.common.data.id.TenantId; | |
21 | +import org.thingsboard.server.common.data.id.*; | |
24 | 22 | import org.thingsboard.server.common.data.page.TextPageData; |
25 | 23 | import org.thingsboard.server.common.data.page.TextPageLink; |
26 | 24 | |
... | ... | @@ -57,4 +55,6 @@ public interface EntityViewService { |
57 | 55 | TextPageLink pageLink); |
58 | 56 | |
59 | 57 | void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId); |
58 | + | |
59 | + ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId); | |
60 | 60 | } | ... | ... |
... | ... | @@ -15,11 +15,14 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.entityview; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
18 | 19 | import lombok.extern.slf4j.Slf4j; |
19 | 20 | import org.apache.commons.lang3.StringUtils; |
20 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 22 | import org.springframework.cache.Cache; |
22 | 23 | import org.springframework.cache.CacheManager; |
24 | +import org.springframework.cache.annotation.CacheEvict; | |
25 | +import org.springframework.cache.annotation.Cacheable; | |
23 | 26 | import org.springframework.stereotype.Service; |
24 | 27 | import org.thingsboard.server.common.data.Customer; |
25 | 28 | import org.thingsboard.server.common.data.EntityView; |
... | ... | @@ -40,6 +43,8 @@ import org.thingsboard.server.dao.tenant.TenantDao; |
40 | 43 | import java.util.ArrayList; |
41 | 44 | import java.util.List; |
42 | 45 | |
46 | +import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE; | |
47 | +import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; | |
43 | 48 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
44 | 49 | import static org.thingsboard.server.dao.service.Validator.validateId; |
45 | 50 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; |
... | ... | @@ -67,6 +72,10 @@ public class EntityViewServiceImpl extends AbstractEntityService |
67 | 72 | @Autowired |
68 | 73 | private CustomerDao customerDao; |
69 | 74 | |
75 | + @Autowired | |
76 | + private CacheManager cacheManager; | |
77 | + | |
78 | + @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}") | |
70 | 79 | @Override |
71 | 80 | public EntityView findEntityViewById(EntityViewId entityViewId) { |
72 | 81 | log.trace("Executing findEntityViewById [{}]", entityViewId); |
... | ... | @@ -82,6 +91,7 @@ public class EntityViewServiceImpl extends AbstractEntityService |
82 | 91 | .orElse(null); |
83 | 92 | } |
84 | 93 | |
94 | + @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}") | |
85 | 95 | @Override |
86 | 96 | public EntityView saveEntityView(EntityView entityView) { |
87 | 97 | log.trace("Executing save entity view [{}]", entityView); |
... | ... | @@ -106,12 +116,14 @@ public class EntityViewServiceImpl extends AbstractEntityService |
106 | 116 | @Override |
107 | 117 | public void deleteEntityView(EntityViewId entityViewId) { |
108 | 118 | log.trace("Executing deleteEntityView [{}]", entityViewId); |
119 | + Cache cache = cacheManager.getCache(ENTITY_VIEW_CACHE); | |
109 | 120 | validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); |
110 | 121 | deleteEntityRelations(entityViewId); |
111 | 122 | EntityView entityView = entityViewDao.findById(entityViewId.getId()); |
112 | 123 | List<Object> list = new ArrayList<>(); |
113 | 124 | list.add(entityView.getTenantId()); |
114 | 125 | list.add(entityView.getName()); |
126 | + cache.evict(list); | |
115 | 127 | entityViewDao.removeById(entityViewId.getId()); |
116 | 128 | } |
117 | 129 | |
... | ... | @@ -124,6 +136,7 @@ public class EntityViewServiceImpl extends AbstractEntityService |
124 | 136 | return new TextPageData<>(entityViews, pageLink); |
125 | 137 | } |
126 | 138 | |
139 | + @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #entityId, #pageLink}") | |
127 | 140 | @Override |
128 | 141 | public TextPageData<EntityView> findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, |
129 | 142 | TextPageLink pageLink) { |
... | ... | @@ -163,6 +176,7 @@ public class EntityViewServiceImpl extends AbstractEntityService |
163 | 176 | return new TextPageData<>(entityViews, pageLink); |
164 | 177 | } |
165 | 178 | |
179 | + @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #customerId, #entityId, #pageLink}") | |
166 | 180 | @Override |
167 | 181 | public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId, |
168 | 182 | CustomerId customerId, |
... | ... | @@ -190,6 +204,13 @@ public class EntityViewServiceImpl extends AbstractEntityService |
190 | 204 | new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId); |
191 | 205 | } |
192 | 206 | |
207 | + @Override | |
208 | + public ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId) { | |
209 | + log.trace("Executing findEntityViewById [{}]", entityViewId); | |
210 | + validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); | |
211 | + return entityViewDao.findByIdAsync(entityViewId.getId()); | |
212 | + } | |
213 | + | |
193 | 214 | private DataValidator<EntityView> entityViewValidator = |
194 | 215 | new DataValidator<EntityView>() { |
195 | 216 | ... | ... |
... | ... | @@ -136,7 +136,6 @@ public class ModelConstants { |
136 | 136 | public static final String DEVICE_NAME_PROPERTY = "name"; |
137 | 137 | public static final String DEVICE_TYPE_PROPERTY = "type"; |
138 | 138 | public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; |
139 | - | |
140 | 139 | public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; |
141 | 140 | public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; |
142 | 141 | 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 { |
152 | 151 | public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; |
153 | 152 | public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; |
154 | 153 | public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; |
155 | - public static final String ENTITY_VIEW_TYPE_PROPERTY = "type_entity"; | |
154 | + public static final String ENTITY_VIEW_TENANT_AND_NAME_VIEW_NAME = "entity_view_by_tenant_and_name"; | |
156 | 155 | public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; |
157 | 156 | public static final String ENTITY_VIEW_TS_BEGIN_PROPERTY = "ts_begin"; |
158 | 157 | public static final String ENTITY_VIEW_TS_END_PROPERTY = "ts_end"; |
159 | 158 | public static final String ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; |
159 | + public static final String ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "entity_view_by_tenant_and_search_text"; | |
160 | 160 | |
161 | 161 | /** |
162 | 162 | * Cassandra audit log constants. | ... | ... |
... | ... | @@ -19,24 +19,26 @@ import com.datastax.driver.core.utils.UUIDs; |
19 | 19 | import com.datastax.driver.mapping.annotations.PartitionKey; |
20 | 20 | import com.datastax.driver.mapping.annotations.Table; |
21 | 21 | import com.fasterxml.jackson.databind.JsonNode; |
22 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
22 | 23 | import lombok.Data; |
23 | 24 | import lombok.EqualsAndHashCode; |
24 | 25 | import lombok.ToString; |
25 | 26 | import org.hibernate.annotations.Type; |
27 | +import org.thingsboard.server.common.data.EntityType; | |
26 | 28 | import org.thingsboard.server.common.data.EntityView; |
27 | -import org.thingsboard.server.common.data.id.CustomerId; | |
28 | -import org.thingsboard.server.common.data.id.DeviceId; | |
29 | -import org.thingsboard.server.common.data.id.EntityViewId; | |
30 | -import org.thingsboard.server.common.data.id.TenantId; | |
29 | +import org.thingsboard.server.common.data.id.*; | |
31 | 30 | import org.thingsboard.server.common.data.objects.TelemetryEntityView; |
32 | 31 | import org.thingsboard.server.dao.model.ModelConstants; |
33 | 32 | import org.thingsboard.server.dao.model.SearchTextEntity; |
34 | 33 | |
35 | 34 | import javax.persistence.Column; |
35 | +import javax.persistence.EnumType; | |
36 | +import javax.persistence.Enumerated; | |
36 | 37 | |
37 | 38 | import java.io.IOException; |
38 | 39 | import java.util.UUID; |
39 | 40 | |
41 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; | |
40 | 42 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; |
41 | 43 | import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; |
42 | 44 | |
... | ... | @@ -57,6 +59,10 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
57 | 59 | @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) |
58 | 60 | private UUID entityId; |
59 | 61 | |
62 | + @Enumerated(EnumType.STRING) | |
63 | + @Column(name = ENTITY_TYPE_PROPERTY) | |
64 | + private EntityType entityType; | |
65 | + | |
60 | 66 | @PartitionKey(value = 2) |
61 | 67 | @Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY) |
62 | 68 | private UUID tenantId; |
... | ... | @@ -68,9 +74,8 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
68 | 74 | @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) |
69 | 75 | private String name; |
70 | 76 | |
71 | - @Type(type = "json") | |
72 | 77 | @Column(name = ModelConstants.ENTITY_VIEW_KEYS_PROPERTY) |
73 | - private JsonNode keys; | |
78 | + private String keys; | |
74 | 79 | |
75 | 80 | @Column(name = ModelConstants.ENTITY_VIEW_TS_BEGIN_PROPERTY) |
76 | 81 | private String tsBegin; |
... | ... | @@ -85,6 +90,8 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
85 | 90 | @Column(name = ModelConstants.ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY) |
86 | 91 | private JsonNode additionalInfo; |
87 | 92 | |
93 | + private static final ObjectMapper mapper = new ObjectMapper(); | |
94 | + | |
88 | 95 | public EntityViewEntity() { |
89 | 96 | super(); |
90 | 97 | } |
... | ... | @@ -95,6 +102,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
95 | 102 | } |
96 | 103 | if (entityView.getEntityId() != null) { |
97 | 104 | this.entityId = entityView.getEntityId().getId(); |
105 | + this.entityType = entityView.getEntityId().getEntityType(); | |
98 | 106 | } |
99 | 107 | if (entityView.getTenantId() != null) { |
100 | 108 | this.tenantId = entityView.getTenantId().getId(); |
... | ... | @@ -103,13 +111,13 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
103 | 111 | this.customerId = entityView.getCustomerId().getId(); |
104 | 112 | } |
105 | 113 | this.name = entityView.getName(); |
106 | -// try { | |
107 | -// this.keys = entityView.getKeys(); | |
108 | -// } catch (IOException e) { | |
109 | -// e.printStackTrace(); | |
110 | -// } | |
111 | - this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0"; | |
112 | - this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0"; | |
114 | + try { | |
115 | + this.keys = mapper.writeValueAsString(entityView.getKeys()); | |
116 | + } catch (IOException e) { | |
117 | + e.printStackTrace(); | |
118 | + } | |
119 | + this.tsBegin = entityView.getStartTs() != 0L ? String.valueOf(entityView.getStartTs()) : "0"; | |
120 | + this.tsEnd = entityView.getEndTs() != 0L ? String.valueOf(entityView.getEndTs()) : "0"; | |
113 | 121 | this.searchText = entityView.getSearchText(); |
114 | 122 | this.additionalInfo = entityView.getAdditionalInfo(); |
115 | 123 | } |
... | ... | @@ -124,7 +132,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
124 | 132 | EntityView entityView = new EntityView(new EntityViewId(id)); |
125 | 133 | entityView.setCreatedTime(UUIDs.unixTimestamp(id)); |
126 | 134 | if (entityId != null) { |
127 | - entityView.setEntityId(new DeviceId(entityId)); | |
135 | + entityView.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), entityId.toString())); | |
128 | 136 | } |
129 | 137 | if (tenantId != null) { |
130 | 138 | entityView.setTenantId(new TenantId(tenantId)); |
... | ... | @@ -133,13 +141,13 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { |
133 | 141 | entityView.setCustomerId(new CustomerId(customerId)); |
134 | 142 | } |
135 | 143 | entityView.setName(name); |
136 | -// try { | |
137 | -// entityView.setKeys((TelemetryEntityView) entityView.getKeys().toObject(keys)); | |
138 | -// } catch (IOException e) { | |
139 | -// e.printStackTrace(); | |
140 | -// } | |
141 | - entityView.setTsBegin(Long.parseLong(tsBegin)); | |
142 | - entityView.setTsEnd(Long.parseLong(tsEnd)); | |
144 | + try { | |
145 | + entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); | |
146 | + } catch (IOException e) { | |
147 | + e.printStackTrace(); | |
148 | + } | |
149 | + entityView.setStartTs(Long.parseLong(tsBegin)); | |
150 | + entityView.setEndTs(Long.parseLong(tsEnd)); | |
143 | 151 | entityView.setAdditionalInfo(additionalInfo); |
144 | 152 | return entityView; |
145 | 153 | } | ... | ... |
... | ... | @@ -34,7 +34,6 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; |
34 | 34 | import javax.persistence.*; |
35 | 35 | import java.io.IOException; |
36 | 36 | |
37 | -import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY; | |
38 | 37 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; |
39 | 38 | |
40 | 39 | /** |
... | ... | @@ -106,8 +105,8 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc |
106 | 105 | } catch (IOException e) { |
107 | 106 | e.printStackTrace(); |
108 | 107 | } |
109 | - this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0"; | |
110 | - this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0"; | |
108 | + this.tsBegin = entityView.getStartTs() != 0L ? String.valueOf(entityView.getStartTs()) : "0"; | |
109 | + this.tsEnd = entityView.getEndTs() != 0L ? String.valueOf(entityView.getEndTs()) : "0"; | |
111 | 110 | this.searchText = entityView.getSearchText(); |
112 | 111 | this.additionalInfo = entityView.getAdditionalInfo(); |
113 | 112 | } |
... | ... | @@ -142,8 +141,8 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc |
142 | 141 | } catch (IOException e) { |
143 | 142 | e.printStackTrace(); |
144 | 143 | } |
145 | - entityView.setTsBegin(Long.parseLong(tsBegin)); | |
146 | - entityView.setTsEnd(Long.parseLong(tsEnd)); | |
144 | + entityView.setStartTs(Long.parseLong(tsBegin)); | |
145 | + entityView.setEndTs(Long.parseLong(tsEnd)); | |
147 | 146 | entityView.setAdditionalInfo(additionalInfo); |
148 | 147 | return entityView; |
149 | 148 | } | ... | ... |
... | ... | @@ -21,12 +21,18 @@ import com.google.common.util.concurrent.ListenableFuture; |
21 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
23 | 23 | import org.springframework.stereotype.Service; |
24 | +import org.thingsboard.server.common.data.EntityType; | |
25 | +import org.thingsboard.server.common.data.EntityView; | |
24 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
27 | +import org.thingsboard.server.common.data.id.EntityViewId; | |
28 | +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | |
25 | 29 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
26 | 30 | import org.thingsboard.server.common.data.kv.TsKvQuery; |
31 | +import org.thingsboard.server.dao.entityview.EntityViewService; | |
27 | 32 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
28 | 33 | import org.thingsboard.server.dao.service.Validator; |
29 | 34 | |
35 | +import java.util.ArrayList; | |
30 | 36 | import java.util.Collection; |
31 | 37 | import java.util.List; |
32 | 38 | |
... | ... | @@ -44,10 +50,17 @@ public class BaseTimeseriesService implements TimeseriesService { |
44 | 50 | @Autowired |
45 | 51 | private TimeseriesDao timeseriesDao; |
46 | 52 | |
53 | + @Autowired | |
54 | + private EntityViewService entityViewService; | |
55 | + | |
47 | 56 | @Override |
48 | 57 | public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) { |
49 | 58 | validate(entityId); |
50 | 59 | queries.forEach(query -> validate(query)); |
60 | + if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { | |
61 | + EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); | |
62 | + return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries)); | |
63 | + } | |
51 | 64 | return timeseriesDao.findAllAsync(entityId, queries); |
52 | 65 | } |
53 | 66 | |
... | ... | @@ -56,7 +69,13 @@ public class BaseTimeseriesService implements TimeseriesService { |
56 | 69 | validate(entityId); |
57 | 70 | List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size()); |
58 | 71 | keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); |
59 | - keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key))); | |
72 | + if (false/*entityId.getEntityType().equals(EntityType.ENTITY_VIEW)*/) { | |
73 | + EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); | |
74 | + Collection<String> newKeys = chooseKeysForEntityView(entityView, keys); | |
75 | + newKeys.forEach(newKey -> futures.add(timeseriesDao.findLatest(entityView.getEntityId(), newKey))); | |
76 | + } else { | |
77 | + keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key))); | |
78 | + } | |
60 | 79 | return Futures.allAsList(futures); |
61 | 80 | } |
62 | 81 | |
... | ... | @@ -69,6 +88,11 @@ public class BaseTimeseriesService implements TimeseriesService { |
69 | 88 | @Override |
70 | 89 | public ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry) { |
71 | 90 | validate(entityId); |
91 | + try { | |
92 | + checkForNonEntityView(entityId); | |
93 | + } catch (Exception e) { | |
94 | + e.printStackTrace(); | |
95 | + } | |
72 | 96 | if (tsKvEntry == null) { |
73 | 97 | throw new IncorrectParameterException("Key value entry can't be null"); |
74 | 98 | } |
... | ... | @@ -79,6 +103,11 @@ public class BaseTimeseriesService implements TimeseriesService { |
79 | 103 | |
80 | 104 | @Override |
81 | 105 | public ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) { |
106 | + try { | |
107 | + checkForNonEntityView(entityId); | |
108 | + } catch (Exception e) { | |
109 | + e.printStackTrace(); | |
110 | + } | |
82 | 111 | List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * INSERTS_PER_ENTRY); |
83 | 112 | for (TsKvEntry tsKvEntry : tsKvEntries) { |
84 | 113 | if (tsKvEntry == null) { |
... | ... | @@ -90,11 +119,47 @@ public class BaseTimeseriesService implements TimeseriesService { |
90 | 119 | } |
91 | 120 | |
92 | 121 | private void saveAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) { |
122 | + try { | |
123 | + checkForNonEntityView(entityId); | |
124 | + } catch (Exception e) { | |
125 | + e.printStackTrace(); | |
126 | + } | |
93 | 127 | futures.add(timeseriesDao.savePartition(entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl)); |
94 | 128 | futures.add(timeseriesDao.saveLatest(entityId, tsKvEntry)); |
95 | 129 | futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl)); |
96 | 130 | } |
97 | 131 | |
132 | + private List<TsKvQuery> updateQueriesForEntityView(EntityView entityView, List<TsKvQuery> queries) { | |
133 | + List<TsKvQuery> newQueries = new ArrayList<>(); | |
134 | + entityView.getKeys().getTimeseries() | |
135 | + .forEach(viewKey -> queries | |
136 | + .forEach(query -> { | |
137 | + if (query.getKey().equals(viewKey)) { | |
138 | + if (entityView.getStartTs() == 0 && entityView.getEndTs() == 0) { | |
139 | + newQueries.add(updateQuery(query.getStartTs(), query.getEndTs(), viewKey, query)); | |
140 | + } else if (entityView.getStartTs() == 0 && entityView.getEndTs() != 0) { | |
141 | + newQueries.add(updateQuery(query.getStartTs(), entityView.getEndTs(), viewKey, query)); | |
142 | + } else if (entityView.getStartTs() != 0 && entityView.getEndTs() == 0) { | |
143 | + newQueries.add(updateQuery(entityView.getStartTs(), query.getEndTs(), viewKey, query)); | |
144 | + } else { | |
145 | + newQueries.add(updateQuery(entityView.getStartTs(), entityView.getEndTs(), viewKey, query)); | |
146 | + } | |
147 | + }})); | |
148 | + return newQueries; | |
149 | + } | |
150 | + | |
151 | + @Deprecated /*Will be a modified*/ | |
152 | + private Collection<String> chooseKeysForEntityView(EntityView entityView, Collection<String> keys) { | |
153 | + Collection<String> newKeys = new ArrayList<>(); | |
154 | + entityView.getKeys().getTimeseries() | |
155 | + .forEach(viewKey -> keys | |
156 | + .forEach(key -> { | |
157 | + if (key.equals(viewKey)) { | |
158 | + newKeys.add(key); | |
159 | + }})); | |
160 | + return newKeys; | |
161 | + } | |
162 | + | |
98 | 163 | private static void validate(EntityId entityId) { |
99 | 164 | Validator.validateEntityId(entityId, "Incorrect entityId " + entityId); |
100 | 165 | } |
... | ... | @@ -108,4 +173,15 @@ public class BaseTimeseriesService implements TimeseriesService { |
108 | 173 | throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty"); |
109 | 174 | } |
110 | 175 | } |
176 | + | |
177 | + private static TsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, TsKvQuery query) { | |
178 | + return startTs <= query.getStartTs() && endTs >= query.getEndTs() ? query : | |
179 | + new BaseTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation()); | |
180 | + } | |
181 | + | |
182 | + private static void checkForNonEntityView(EntityId entityId) throws Exception { | |
183 | + if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { | |
184 | + throw new Exception("Entity-views were read only"); | |
185 | + } | |
186 | + } | |
111 | 187 | } | ... | ... |