Showing
32 changed files
with
727 additions
and
34 deletions
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 | + | ||
17 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name; | ||
18 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text; | ||
19 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer; | ||
20 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id; | ||
21 | + | ||
22 | +DROP TABLE IF EXISTS thingsboard.entity_views; | ||
23 | + | ||
24 | +CREATE TABLE IF NOT EXISTS thingsboard.entity_view ( | ||
25 | + id timeuuid, | ||
26 | + entity_id timeuuid, | ||
27 | + entity_type text, | ||
28 | + tenant_id timeuuid, | ||
29 | + customer_id timeuuid, | ||
30 | + name text, | ||
31 | + type text, | ||
32 | + keys text, | ||
33 | + start_ts bigint, | ||
34 | + end_ts bigint, | ||
35 | + search_text text, | ||
36 | + additional_info text, | ||
37 | + PRIMARY KEY (id, entity_id, tenant_id, customer_id, type) | ||
38 | +); | ||
39 | + | ||
40 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS | ||
41 | + SELECT * | ||
42 | + from thingsboard.entity_view | ||
43 | + WHERE tenant_id IS NOT NULL | ||
44 | + AND entity_id IS NOT NULL | ||
45 | + AND customer_id IS NOT NULL | ||
46 | + AND type IS NOT NULL | ||
47 | + AND name IS NOT NULL | ||
48 | + AND id IS NOT NULL | ||
49 | + PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type) | ||
50 | + WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); | ||
51 | + | ||
52 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS | ||
53 | + SELECT * | ||
54 | + from thingsboard.entity_view | ||
55 | + WHERE tenant_id IS NOT NULL | ||
56 | + AND entity_id IS NOT NULL | ||
57 | + AND customer_id IS NOT NULL | ||
58 | + AND type IS NOT NULL | ||
59 | + AND search_text IS NOT NULL | ||
60 | + AND id IS NOT NULL | ||
61 | + PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type) | ||
62 | + WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); | ||
63 | + | ||
64 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS | ||
65 | + SELECT * | ||
66 | + from thingsboard.entity_view | ||
67 | + WHERE tenant_id IS NOT NULL | ||
68 | + AND entity_id IS NOT NULL | ||
69 | + AND customer_id IS NOT NULL | ||
70 | + AND type IS NOT NULL | ||
71 | + AND search_text IS NOT NULL | ||
72 | + AND id IS NOT NULL | ||
73 | + PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id) | ||
74 | + WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC); | ||
75 | + | ||
76 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS | ||
77 | + SELECT * | ||
78 | + from thingsboard.entity_view | ||
79 | + WHERE tenant_id IS NOT NULL | ||
80 | + AND customer_id IS NOT NULL | ||
81 | + AND entity_id IS NOT NULL | ||
82 | + AND type IS NOT NULL | ||
83 | + AND search_text IS NOT NULL | ||
84 | + AND id IS NOT NULL | ||
85 | + PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type) | ||
86 | + WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); | ||
87 | + | ||
88 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS | ||
89 | + SELECT * | ||
90 | + from thingsboard.entity_view | ||
91 | + WHERE tenant_id IS NOT NULL | ||
92 | + AND customer_id IS NOT NULL | ||
93 | + AND entity_id IS NOT NULL | ||
94 | + AND type IS NOT NULL | ||
95 | + AND search_text IS NOT NULL | ||
96 | + AND id IS NOT NULL | ||
97 | + PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id) | ||
98 | + WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC); | ||
99 | + | ||
100 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS | ||
101 | + SELECT * | ||
102 | + from thingsboard.entity_view | ||
103 | + WHERE tenant_id IS NOT NULL | ||
104 | + AND customer_id IS NOT NULL | ||
105 | + AND entity_id IS NOT NULL | ||
106 | + AND type IS NOT NULL | ||
107 | + AND search_text IS NOT NULL | ||
108 | + AND id IS NOT NULL | ||
109 | + PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) | ||
110 | + WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); |
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 | + | ||
17 | +DROP TABLE IF EXISTS entity_views; | ||
18 | + | ||
19 | +CREATE TABLE IF NOT EXISTS entity_view ( | ||
20 | + id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, | ||
21 | + entity_id varchar(31), | ||
22 | + entity_type varchar(255), | ||
23 | + tenant_id varchar(31), | ||
24 | + customer_id varchar(31), | ||
25 | + type varchar(255), | ||
26 | + name varchar(255), | ||
27 | + keys varchar(255), | ||
28 | + start_ts bigint, | ||
29 | + end_ts bigint, | ||
30 | + search_text varchar(255), | ||
31 | + additional_info varchar | ||
32 | +); |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.controller; | 16 | package org.thingsboard.server.controller; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import org.springframework.http.HttpStatus; | 19 | import org.springframework.http.HttpStatus; |
19 | import org.springframework.security.access.prepost.PreAuthorize; | 20 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | import org.springframework.web.bind.annotation.PathVariable; | 21 | import org.springframework.web.bind.annotation.PathVariable; |
@@ -26,6 +27,7 @@ import org.springframework.web.bind.annotation.ResponseBody; | @@ -26,6 +27,7 @@ import org.springframework.web.bind.annotation.ResponseBody; | ||
26 | import org.springframework.web.bind.annotation.ResponseStatus; | 27 | import org.springframework.web.bind.annotation.ResponseStatus; |
27 | import org.springframework.web.bind.annotation.RestController; | 28 | import org.springframework.web.bind.annotation.RestController; |
28 | import org.thingsboard.server.common.data.Customer; | 29 | import org.thingsboard.server.common.data.Customer; |
30 | +import org.thingsboard.server.common.data.EntitySubtype; | ||
29 | import org.thingsboard.server.common.data.EntityType; | 31 | import org.thingsboard.server.common.data.EntityType; |
30 | import org.thingsboard.server.common.data.EntityView; | 32 | import org.thingsboard.server.common.data.EntityView; |
31 | import org.thingsboard.server.common.data.audit.ActionType; | 33 | import org.thingsboard.server.common.data.audit.ActionType; |
@@ -38,6 +40,7 @@ import org.thingsboard.server.common.data.page.TextPageData; | @@ -38,6 +40,7 @@ import org.thingsboard.server.common.data.page.TextPageData; | ||
38 | import org.thingsboard.server.common.data.page.TextPageLink; | 40 | import org.thingsboard.server.common.data.page.TextPageLink; |
39 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 41 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
40 | import org.thingsboard.server.dao.model.ModelConstants; | 42 | import org.thingsboard.server.dao.model.ModelConstants; |
43 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
41 | 44 | ||
42 | import java.util.List; | 45 | import java.util.List; |
43 | import java.util.stream.Collectors; | 46 | import java.util.stream.Collectors; |
@@ -161,6 +164,7 @@ public class EntityViewController extends BaseController { | @@ -161,6 +164,7 @@ public class EntityViewController extends BaseController { | ||
161 | public TextPageData<EntityView> getCustomerEntityViews( | 164 | public TextPageData<EntityView> getCustomerEntityViews( |
162 | @PathVariable("customerId") String strCustomerId, | 165 | @PathVariable("customerId") String strCustomerId, |
163 | @RequestParam int limit, | 166 | @RequestParam int limit, |
167 | + @RequestParam(required = false) String type, | ||
164 | @RequestParam(required = false) String textSearch, | 168 | @RequestParam(required = false) String textSearch, |
165 | @RequestParam(required = false) String idOffset, | 169 | @RequestParam(required = false) String idOffset, |
166 | @RequestParam(required = false) String textOffset) throws ThingsboardException { | 170 | @RequestParam(required = false) String textOffset) throws ThingsboardException { |
@@ -170,7 +174,11 @@ public class EntityViewController extends BaseController { | @@ -170,7 +174,11 @@ public class EntityViewController extends BaseController { | ||
170 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | 174 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
171 | checkCustomerId(customerId); | 175 | checkCustomerId(customerId); |
172 | TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | 176 | TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); |
173 | - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | 177 | + if (type != null && type.trim().length() > 0) { |
178 | + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); | ||
179 | + } else { | ||
180 | + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | ||
181 | + } | ||
174 | } catch (Exception e) { | 182 | } catch (Exception e) { |
175 | throw handleException(e); | 183 | throw handleException(e); |
176 | } | 184 | } |
@@ -181,13 +189,19 @@ public class EntityViewController extends BaseController { | @@ -181,13 +189,19 @@ public class EntityViewController extends BaseController { | ||
181 | @ResponseBody | 189 | @ResponseBody |
182 | public TextPageData<EntityView> getTenantEntityViews( | 190 | public TextPageData<EntityView> getTenantEntityViews( |
183 | @RequestParam int limit, | 191 | @RequestParam int limit, |
192 | + @RequestParam(required = false) String type, | ||
184 | @RequestParam(required = false) String textSearch, | 193 | @RequestParam(required = false) String textSearch, |
185 | @RequestParam(required = false) String idOffset, | 194 | @RequestParam(required = false) String idOffset, |
186 | @RequestParam(required = false) String textOffset) throws ThingsboardException { | 195 | @RequestParam(required = false) String textOffset) throws ThingsboardException { |
187 | try { | 196 | try { |
188 | TenantId tenantId = getCurrentUser().getTenantId(); | 197 | TenantId tenantId = getCurrentUser().getTenantId(); |
189 | TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | 198 | TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); |
190 | - return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); | 199 | + |
200 | + if (type != null && type.trim().length() > 0) { | ||
201 | + return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); | ||
202 | + } else { | ||
203 | + return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); | ||
204 | + } | ||
191 | } catch (Exception e) { | 205 | } catch (Exception e) { |
192 | throw handleException(e); | 206 | throw handleException(e); |
193 | } | 207 | } |
@@ -199,6 +213,7 @@ public class EntityViewController extends BaseController { | @@ -199,6 +213,7 @@ public class EntityViewController extends BaseController { | ||
199 | public List<EntityView> findByQuery(@RequestBody EntityViewSearchQuery query) throws ThingsboardException { | 213 | public List<EntityView> findByQuery(@RequestBody EntityViewSearchQuery query) throws ThingsboardException { |
200 | checkNotNull(query); | 214 | checkNotNull(query); |
201 | checkNotNull(query.getParameters()); | 215 | checkNotNull(query.getParameters()); |
216 | + checkNotNull(query.getEntityViewTypes()); | ||
202 | checkEntityId(query.getParameters().getEntityId()); | 217 | checkEntityId(query.getParameters().getEntityId()); |
203 | try { | 218 | try { |
204 | List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get()); | 219 | List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get()); |
@@ -215,4 +230,18 @@ public class EntityViewController extends BaseController { | @@ -215,4 +230,18 @@ public class EntityViewController extends BaseController { | ||
215 | throw handleException(e); | 230 | throw handleException(e); |
216 | } | 231 | } |
217 | } | 232 | } |
233 | + | ||
234 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
235 | + @RequestMapping(value = "/entityView/types", method = RequestMethod.GET) | ||
236 | + @ResponseBody | ||
237 | + public List<EntitySubtype> getEntityViewTypes() throws ThingsboardException { | ||
238 | + try { | ||
239 | + SecurityUser user = getCurrentUser(); | ||
240 | + TenantId tenantId = user.getTenantId(); | ||
241 | + ListenableFuture<List<EntitySubtype>> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId); | ||
242 | + return checkNotNull(entityViewTypes.get()); | ||
243 | + } catch (Exception e) { | ||
244 | + throw handleException(e); | ||
245 | + } | ||
246 | + } | ||
218 | } | 247 | } |
@@ -39,10 +39,19 @@ import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATIO | @@ -39,10 +39,19 @@ import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATIO | ||
39 | import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; | 39 | import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; |
40 | import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; | 40 | import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; |
41 | import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE; | 41 | import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE; |
42 | +import static org.thingsboard.server.service.install.DatabaseHelper.END_TS; | ||
43 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID; | ||
44 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE; | ||
45 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW; | ||
46 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS; | ||
42 | import static org.thingsboard.server.service.install.DatabaseHelper.ID; | 47 | import static org.thingsboard.server.service.install.DatabaseHelper.ID; |
48 | +import static org.thingsboard.server.service.install.DatabaseHelper.KEYS; | ||
49 | +import static org.thingsboard.server.service.install.DatabaseHelper.NAME; | ||
43 | import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; | 50 | import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; |
51 | +import static org.thingsboard.server.service.install.DatabaseHelper.START_TS; | ||
44 | import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; | 52 | import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; |
45 | import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; | 53 | import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; |
54 | +import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; | ||
46 | 55 | ||
47 | @Service | 56 | @Service |
48 | @NoSqlDao | 57 | @NoSqlDao |
@@ -213,6 +222,36 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -213,6 +222,36 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
213 | 222 | ||
214 | break; | 223 | break; |
215 | 224 | ||
225 | + case "2.1.1": | ||
226 | + | ||
227 | + log.info("Upgrading Cassandara DataBase from version {} to 2.1.1 ...", fromVersion); | ||
228 | + | ||
229 | + cluster.getSession(); | ||
230 | + | ||
231 | + ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); | ||
232 | + | ||
233 | + log.info("Dumping entity views ..."); | ||
234 | + Path entityViewsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ENTITY_VIEWS, | ||
235 | + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, | ||
236 | + new String[]{"", "", "", "", "", "", "default", "", "0", "0", "", ""}, | ||
237 | + "tb-entity-views"); | ||
238 | + log.info("Entity views dumped."); | ||
239 | + | ||
240 | + log.info("Updating schema ..."); | ||
241 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_CQL); | ||
242 | + loadCql(schemaUpdateFile); | ||
243 | + log.info("Schema updated."); | ||
244 | + | ||
245 | + log.info("Restoring entity views ..."); | ||
246 | + if (entityViewsDump != null) { | ||
247 | + CassandraDbHelper.loadCf(ks, cluster.getSession(), ENTITY_VIEW, | ||
248 | + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump); | ||
249 | + Files.deleteIfExists(entityViewsDump); | ||
250 | + } | ||
251 | + log.info("Entity views restored."); | ||
252 | + | ||
253 | + break; | ||
254 | + | ||
216 | default: | 255 | default: |
217 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | 256 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
218 | } | 257 | } |
@@ -45,14 +45,23 @@ public class DatabaseHelper { | @@ -45,14 +45,23 @@ public class DatabaseHelper { | ||
45 | public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); | 45 | public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); |
46 | 46 | ||
47 | public static final String DEVICE = "device"; | 47 | public static final String DEVICE = "device"; |
48 | + public static final String ENTITY_ID = "entity_id"; | ||
48 | public static final String TENANT_ID = "tenant_id"; | 49 | public static final String TENANT_ID = "tenant_id"; |
50 | + public static final String ENTITY_TYPE = "entity_type"; | ||
49 | public static final String CUSTOMER_ID = "customer_id"; | 51 | public static final String CUSTOMER_ID = "customer_id"; |
50 | public static final String SEARCH_TEXT = "search_text"; | 52 | public static final String SEARCH_TEXT = "search_text"; |
51 | public static final String ADDITIONAL_INFO = "additional_info"; | 53 | public static final String ADDITIONAL_INFO = "additional_info"; |
52 | public static final String ASSET = "asset"; | 54 | public static final String ASSET = "asset"; |
53 | public static final String DASHBOARD = "dashboard"; | 55 | public static final String DASHBOARD = "dashboard"; |
56 | + public static final String ENTITY_VIEWS = "entity_views"; | ||
57 | + public static final String ENTITY_VIEW = "entity_view"; | ||
54 | public static final String ID = "id"; | 58 | public static final String ID = "id"; |
55 | public static final String TITLE = "title"; | 59 | public static final String TITLE = "title"; |
60 | + public static final String TYPE = "type"; | ||
61 | + public static final String NAME = "name"; | ||
62 | + public static final String KEYS = "keys"; | ||
63 | + public static final String START_TS = "start_ts"; | ||
64 | + public static final String END_TS = "end_ts"; | ||
56 | public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; | 65 | public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; |
57 | public static final String CONFIGURATION = "configuration"; | 66 | public static final String CONFIGURATION = "configuration"; |
58 | 67 |
@@ -31,14 +31,24 @@ import java.nio.file.Paths; | @@ -31,14 +31,24 @@ import java.nio.file.Paths; | ||
31 | import java.sql.Connection; | 31 | import java.sql.Connection; |
32 | import java.sql.DriverManager; | 32 | import java.sql.DriverManager; |
33 | 33 | ||
34 | +import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; | ||
34 | import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; | 35 | import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; |
35 | import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; | 36 | import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; |
36 | import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; | 37 | import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; |
37 | import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; | 38 | import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; |
39 | +import static org.thingsboard.server.service.install.DatabaseHelper.END_TS; | ||
40 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID; | ||
41 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE; | ||
42 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW; | ||
43 | +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS; | ||
38 | import static org.thingsboard.server.service.install.DatabaseHelper.ID; | 44 | import static org.thingsboard.server.service.install.DatabaseHelper.ID; |
45 | +import static org.thingsboard.server.service.install.DatabaseHelper.KEYS; | ||
46 | +import static org.thingsboard.server.service.install.DatabaseHelper.NAME; | ||
39 | import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; | 47 | import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; |
48 | +import static org.thingsboard.server.service.install.DatabaseHelper.START_TS; | ||
40 | import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; | 49 | import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; |
41 | import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; | 50 | import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; |
51 | +import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; | ||
42 | 52 | ||
43 | @Service | 53 | @Service |
44 | @Profile("install") | 54 | @Profile("install") |
@@ -115,6 +125,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -115,6 +125,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
115 | log.info("Schema updated."); | 125 | log.info("Schema updated."); |
116 | } | 126 | } |
117 | break; | 127 | break; |
128 | + case "2.1.1": | ||
129 | + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | ||
130 | + | ||
131 | + log.info("Dumping entity views ..."); | ||
132 | + Path entityViewsDump = SqlDbHelper.dumpTableIfExists(conn, ENTITY_VIEWS, | ||
133 | + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, | ||
134 | + new String[]{"", "", "", "", "", "default", "", "", "0", "0", "", ""}, | ||
135 | + "tb-entity-views", true); | ||
136 | + log.info("Entity views dumped."); | ||
137 | + | ||
138 | + log.info("Updating schema ..."); | ||
139 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_SQL); | ||
140 | + loadSql(schemaUpdateFile, conn); | ||
141 | + log.info("Schema updated."); | ||
142 | + | ||
143 | + log.info("Restoring entity views ..."); | ||
144 | + if (entityViewsDump != null) { | ||
145 | + SqlDbHelper.loadTable(conn, ENTITY_VIEW, | ||
146 | + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump, true); | ||
147 | + Files.deleteIfExists(entityViewsDump); | ||
148 | + } | ||
149 | + log.info("Entity views restored."); | ||
150 | + } | ||
151 | + break; | ||
118 | 152 | ||
119 | default: | 153 | default: |
120 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 154 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
@@ -144,7 +144,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -144,7 +144,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
144 | 144 | ||
145 | @Test | 145 | @Test |
146 | public void testSaveEntityViewWithEmptyName() throws Exception { | 146 | public void testSaveEntityViewWithEmptyName() throws Exception { |
147 | - doPost("/api/entityView", new EntityView()) | 147 | + EntityView entityView = new EntityView(); |
148 | + entityView.setType("default"); | ||
149 | + doPost("/api/entityView", entityView) | ||
148 | .andExpect(status().isBadRequest()) | 150 | .andExpect(status().isBadRequest()) |
149 | .andExpect(statusReason(containsString("Entity view name should be specified!"))); | 151 | .andExpect(statusReason(containsString("Entity view name should be specified!"))); |
150 | } | 152 | } |
@@ -355,6 +357,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -355,6 +357,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
355 | view.setEntityId(testDevice.getId()); | 357 | view.setEntityId(testDevice.getId()); |
356 | view.setTenantId(savedTenant.getId()); | 358 | view.setTenantId(savedTenant.getId()); |
357 | view.setName("Test entity view"); | 359 | view.setName("Test entity view"); |
360 | + view.setType("default"); | ||
358 | view.setKeys(telemetry); | 361 | view.setKeys(telemetry); |
359 | view.setStartTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") * 10); | 362 | view.setStartTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") * 10); |
360 | view.setEndTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") / 10); | 363 | view.setEndTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") / 10); |
@@ -402,6 +405,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | @@ -402,6 +405,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes | ||
402 | view.setEntityId(testDevice.getId()); | 405 | view.setEntityId(testDevice.getId()); |
403 | view.setTenantId(savedTenant.getId()); | 406 | view.setTenantId(savedTenant.getId()); |
404 | view.setName(name); | 407 | view.setName(name); |
408 | + view.setType("default"); | ||
405 | view.setKeys(telemetry); | 409 | view.setKeys(telemetry); |
406 | return doPost("/api/entityView", view, EntityView.class); | 410 | return doPost("/api/entityView", view, EntityView.class); |
407 | } | 411 | } |
@@ -26,8 +26,8 @@ import java.util.Arrays; | @@ -26,8 +26,8 @@ import java.util.Arrays; | ||
26 | 26 | ||
27 | @RunWith(ClasspathSuite.class) | 27 | @RunWith(ClasspathSuite.class) |
28 | @ClasspathSuite.ClassnameFilters({ | 28 | @ClasspathSuite.ClassnameFilters({ |
29 | - "org.thingsboard.server.rules.flow.nosql.*Test", | ||
30 | - "org.thingsboard.server.rules.lifecycle.nosql.*Test" | 29 | + "org.thingsboard.server.rules.flow.nosql.RuleEngineFlowNoSqlIntegrationTest", |
30 | +// "org.thingsboard.server.rules.lifecycle.nosql.*Test" | ||
31 | }) | 31 | }) |
32 | public class RuleEngineNoSqlTestSuite { | 32 | public class RuleEngineNoSqlTestSuite { |
33 | 33 |
@@ -40,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> | @@ -40,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> | ||
40 | private TenantId tenantId; | 40 | private TenantId tenantId; |
41 | private CustomerId customerId; | 41 | private CustomerId customerId; |
42 | private String name; | 42 | private String name; |
43 | + private String type; | ||
43 | private TelemetryEntityView keys; | 44 | private TelemetryEntityView keys; |
44 | private long startTimeMs; | 45 | private long startTimeMs; |
45 | private long endTimeMs; | 46 | private long endTimeMs; |
@@ -30,6 +30,7 @@ public class EntityViewSearchQuery { | @@ -30,6 +30,7 @@ public class EntityViewSearchQuery { | ||
30 | 30 | ||
31 | private RelationsSearchParameters parameters; | 31 | private RelationsSearchParameters parameters; |
32 | private String relationType; | 32 | private String relationType; |
33 | + private List<String> entityViewTypes; | ||
33 | 34 | ||
34 | public EntityRelationsQuery toEntitySearchQuery() { | 35 | public EntityRelationsQuery toEntitySearchQuery() { |
35 | EntityRelationsQuery query = new EntityRelationsQuery(); | 36 | EntityRelationsQuery query = new EntityRelationsQuery(); |
@@ -15,8 +15,13 @@ | @@ -15,8 +15,13 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.entityview; | 16 | package org.thingsboard.server.dao.entityview; |
17 | 17 | ||
18 | +import com.datastax.driver.core.ResultSet; | ||
19 | +import com.datastax.driver.core.ResultSetFuture; | ||
18 | import com.datastax.driver.core.Statement; | 20 | import com.datastax.driver.core.Statement; |
19 | import com.datastax.driver.core.querybuilder.Select; | 21 | import com.datastax.driver.core.querybuilder.Select; |
22 | +import com.datastax.driver.mapping.Result; | ||
23 | +import com.google.common.base.Function; | ||
24 | +import com.google.common.util.concurrent.Futures; | ||
20 | import com.google.common.util.concurrent.ListenableFuture; | 25 | import com.google.common.util.concurrent.ListenableFuture; |
21 | import lombok.extern.slf4j.Slf4j; | 26 | import lombok.extern.slf4j.Slf4j; |
22 | import org.springframework.stereotype.Component; | 27 | import org.springframework.stereotype.Component; |
@@ -30,6 +35,8 @@ import org.thingsboard.server.dao.model.nosql.EntityViewEntity; | @@ -30,6 +35,8 @@ import org.thingsboard.server.dao.model.nosql.EntityViewEntity; | ||
30 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; | 35 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; |
31 | import org.thingsboard.server.dao.util.NoSqlDao; | 36 | import org.thingsboard.server.dao.util.NoSqlDao; |
32 | 37 | ||
38 | +import javax.annotation.Nullable; | ||
39 | +import java.util.ArrayList; | ||
33 | import java.util.Arrays; | 40 | import java.util.Arrays; |
34 | import java.util.Collections; | 41 | import java.util.Collections; |
35 | import java.util.List; | 42 | import java.util.List; |
@@ -39,14 +46,21 @@ import java.util.UUID; | @@ -39,14 +46,21 @@ import java.util.UUID; | ||
39 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; | 46 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
40 | import static com.datastax.driver.core.querybuilder.QueryBuilder.select; | 47 | import static com.datastax.driver.core.querybuilder.QueryBuilder.select; |
41 | import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY; | 48 | import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY; |
49 | +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; | ||
42 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; | 50 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; |
51 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_COLUMN_FAMILY_NAME; | ||
52 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY; | ||
53 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_TENANT_ID_PROPERTY; | ||
54 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF; | ||
43 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF; | 55 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF; |
44 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF; | 56 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF; |
45 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME; | 57 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME; |
46 | -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; | 58 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF; |
59 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF; | ||
47 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY; | 60 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY; |
48 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; | 61 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; |
49 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY; | 62 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY; |
63 | +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TYPE_PROPERTY; | ||
50 | import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_PROPERTY; | 64 | import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_PROPERTY; |
51 | 65 | ||
52 | /** | 66 | /** |
@@ -82,7 +96,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | @@ -82,7 +96,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | ||
82 | public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) { | 96 | public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) { |
83 | log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink); | 97 | log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink); |
84 | List<EntityViewEntity> entityViewEntities = | 98 | List<EntityViewEntity> entityViewEntities = |
85 | - findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | 99 | + findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF, |
86 | Collections.singletonList(eq(TENANT_ID_PROPERTY, tenantId)), pageLink); | 100 | Collections.singletonList(eq(TENANT_ID_PROPERTY, tenantId)), pageLink); |
87 | log.trace("Found entity views [{}] by tenantId [{}] and pageLink [{}]", | 101 | log.trace("Found entity views [{}] by tenantId [{}] and pageLink [{}]", |
88 | entityViewEntities, tenantId, pageLink); | 102 | entityViewEntities, tenantId, pageLink); |
@@ -90,6 +104,18 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | @@ -90,6 +104,18 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | ||
90 | } | 104 | } |
91 | 105 | ||
92 | @Override | 106 | @Override |
107 | + public List<EntityView> findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { | ||
108 | + log.debug("Try to find entity views by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); | ||
109 | + List<EntityViewEntity> entityViewEntities = | ||
110 | + findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF, | ||
111 | + Arrays.asList(eq(ENTITY_VIEW_TYPE_PROPERTY, type), | ||
112 | + eq(TENANT_ID_PROPERTY, tenantId)), pageLink); | ||
113 | + log.trace("Found entity views [{}] by tenantId [{}], type [{}] and pageLink [{}]", | ||
114 | + entityViewEntities, tenantId, type, pageLink); | ||
115 | + return DaoUtil.convertDataList(entityViewEntities); | ||
116 | + } | ||
117 | + | ||
118 | + @Override | ||
93 | public Optional<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String name) { | 119 | public Optional<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String name) { |
94 | Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_NAME).where(); | 120 | Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_NAME).where(); |
95 | query.and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId)); | 121 | query.and(eq(ENTITY_VIEW_TENANT_ID_PROPERTY, tenantId)); |
@@ -111,6 +137,19 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | @@ -111,6 +137,19 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | ||
111 | } | 137 | } |
112 | 138 | ||
113 | @Override | 139 | @Override |
140 | + public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { | ||
141 | + log.debug("Try to find entity views by tenantId [{}], customerId[{}], type [{}] and pageLink [{}]", | ||
142 | + tenantId, customerId, type, pageLink); | ||
143 | + List<EntityViewEntity> entityViewEntities = findPageWithTextSearch( | ||
144 | + ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF, | ||
145 | + Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), | ||
146 | + pageLink); | ||
147 | + log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", | ||
148 | + entityViewEntities, tenantId, customerId, type, pageLink); | ||
149 | + return DaoUtil.convertDataList(entityViewEntities); | ||
150 | + } | ||
151 | + | ||
152 | + @Override | ||
114 | public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { | 153 | public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { |
115 | log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId); | 154 | log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId); |
116 | Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF).where(); | 155 | Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF).where(); |
@@ -118,4 +157,30 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | @@ -118,4 +157,30 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit | ||
118 | query.and(eq(ENTITY_ID_COLUMN, entityId)); | 157 | query.and(eq(ENTITY_ID_COLUMN, entityId)); |
119 | return findListByStatementAsync(query); | 158 | return findListByStatementAsync(query); |
120 | } | 159 | } |
160 | + | ||
161 | + @Override | ||
162 | + public ListenableFuture<List<EntitySubtype>> findTenantEntityViewTypesAsync(UUID tenantId) { | ||
163 | + Select select = select().from(ENTITY_SUBTYPE_COLUMN_FAMILY_NAME); | ||
164 | + Select.Where query = select.where(); | ||
165 | + query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId)); | ||
166 | + query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ENTITY_VIEW)); | ||
167 | + query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); | ||
168 | + ResultSetFuture resultSetFuture = executeAsyncRead(query); | ||
169 | + return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() { | ||
170 | + @Nullable | ||
171 | + @Override | ||
172 | + public List<EntitySubtype> apply(@Nullable ResultSet resultSet) { | ||
173 | + Result<EntitySubtypeEntity> result = cluster.getMapper(EntitySubtypeEntity.class).map(resultSet); | ||
174 | + if (result != null) { | ||
175 | + List<EntitySubtype> entitySubtypes = new ArrayList<>(); | ||
176 | + result.all().forEach((entitySubtypeEntity) -> | ||
177 | + entitySubtypes.add(entitySubtypeEntity.toEntitySubtype()) | ||
178 | + ); | ||
179 | + return entitySubtypes; | ||
180 | + } else { | ||
181 | + return Collections.emptyList(); | ||
182 | + } | ||
183 | + } | ||
184 | + }); | ||
185 | + } | ||
121 | } | 186 | } |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.entityview; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.entityview; | ||
17 | 17 | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.Device; | 19 | import org.thingsboard.server.common.data.Device; |
20 | +import org.thingsboard.server.common.data.EntitySubtype; | ||
20 | import org.thingsboard.server.common.data.EntityView; | 21 | import org.thingsboard.server.common.data.EntityView; |
21 | import org.thingsboard.server.common.data.page.TextPageLink; | 22 | import org.thingsboard.server.common.data.page.TextPageLink; |
22 | import org.thingsboard.server.dao.Dao; | 23 | import org.thingsboard.server.dao.Dao; |
@@ -48,6 +49,16 @@ public interface EntityViewDao extends Dao<EntityView> { | @@ -48,6 +49,16 @@ public interface EntityViewDao extends Dao<EntityView> { | ||
48 | List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink); | 49 | List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink); |
49 | 50 | ||
50 | /** | 51 | /** |
52 | + * Find entity views by tenantId, type and page link. | ||
53 | + * | ||
54 | + * @param tenantId the tenantId | ||
55 | + * @param type the type | ||
56 | + * @param pageLink the page link | ||
57 | + * @return the list of entity view objects | ||
58 | + */ | ||
59 | + List<EntityView> findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink); | ||
60 | + | ||
61 | + /** | ||
51 | * Find entity views by tenantId and entity view name. | 62 | * Find entity views by tenantId and entity view name. |
52 | * | 63 | * |
53 | * @param tenantId the tenantId | 64 | * @param tenantId the tenantId |
@@ -68,6 +79,27 @@ public interface EntityViewDao extends Dao<EntityView> { | @@ -68,6 +79,27 @@ public interface EntityViewDao extends Dao<EntityView> { | ||
68 | UUID customerId, | 79 | UUID customerId, |
69 | TextPageLink pageLink); | 80 | TextPageLink pageLink); |
70 | 81 | ||
82 | + /** | ||
83 | + * Find entity views by tenantId, customerId, type and page link. | ||
84 | + * | ||
85 | + * @param tenantId the tenantId | ||
86 | + * @param customerId the customerId | ||
87 | + * @param type the type | ||
88 | + * @param pageLink the page link | ||
89 | + * @return the list of entity view objects | ||
90 | + */ | ||
91 | + List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, | ||
92 | + UUID customerId, | ||
93 | + String type, | ||
94 | + TextPageLink pageLink); | ||
71 | 95 | ||
72 | ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId); | 96 | ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId); |
97 | + | ||
98 | + /** | ||
99 | + * Find tenants entity view types. | ||
100 | + * | ||
101 | + * @return the list of tenant entity view type objects | ||
102 | + */ | ||
103 | + ListenableFuture<List<EntitySubtype>> findTenantEntityViewTypesAsync(UUID tenantId); | ||
104 | + | ||
73 | } | 105 | } |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.server.dao.entityview; | 16 | package org.thingsboard.server.dao.entityview; |
17 | 17 | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | +import org.thingsboard.server.common.data.EntitySubtype; | ||
19 | import org.thingsboard.server.common.data.EntityView; | 20 | import org.thingsboard.server.common.data.EntityView; |
20 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; | 21 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
21 | import org.thingsboard.server.common.data.id.CustomerId; | 22 | import org.thingsboard.server.common.data.id.CustomerId; |
@@ -44,8 +45,12 @@ public interface EntityViewService { | @@ -44,8 +45,12 @@ public interface EntityViewService { | ||
44 | 45 | ||
45 | TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); | 46 | TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); |
46 | 47 | ||
48 | + TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); | ||
49 | + | ||
47 | TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); | 50 | TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); |
48 | 51 | ||
52 | + TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type); | ||
53 | + | ||
49 | ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query); | 54 | ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query); |
50 | 55 | ||
51 | ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId); | 56 | ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId); |
@@ -55,4 +60,6 @@ public interface EntityViewService { | @@ -55,4 +60,6 @@ public interface EntityViewService { | ||
55 | void deleteEntityView(EntityViewId entityViewId); | 60 | void deleteEntityView(EntityViewId entityViewId); |
56 | 61 | ||
57 | void deleteEntityViewsByTenantId(TenantId tenantId); | 62 | void deleteEntityViewsByTenantId(TenantId tenantId); |
63 | + | ||
64 | + ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId); | ||
58 | } | 65 | } |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.entityview; | 16 | package org.thingsboard.server.dao.entityview; |
17 | 17 | ||
18 | +import com.google.common.base.Function; | ||
18 | import com.google.common.util.concurrent.FutureCallback; | 19 | import com.google.common.util.concurrent.FutureCallback; |
19 | import com.google.common.util.concurrent.Futures; | 20 | import com.google.common.util.concurrent.Futures; |
20 | import com.google.common.util.concurrent.ListenableFuture; | 21 | import com.google.common.util.concurrent.ListenableFuture; |
@@ -29,6 +30,8 @@ import org.springframework.cache.annotation.Caching; | @@ -29,6 +30,8 @@ import org.springframework.cache.annotation.Caching; | ||
29 | import org.springframework.stereotype.Service; | 30 | import org.springframework.stereotype.Service; |
30 | import org.thingsboard.server.common.data.Customer; | 31 | import org.thingsboard.server.common.data.Customer; |
31 | import org.thingsboard.server.common.data.DataConstants; | 32 | import org.thingsboard.server.common.data.DataConstants; |
33 | +import org.thingsboard.server.common.data.Device; | ||
34 | +import org.thingsboard.server.common.data.EntitySubtype; | ||
32 | import org.thingsboard.server.common.data.EntityType; | 35 | import org.thingsboard.server.common.data.EntityType; |
33 | import org.thingsboard.server.common.data.EntityView; | 36 | import org.thingsboard.server.common.data.EntityView; |
34 | import org.thingsboard.server.common.data.Tenant; | 37 | import org.thingsboard.server.common.data.Tenant; |
@@ -54,6 +57,8 @@ import javax.annotation.Nullable; | @@ -54,6 +57,8 @@ import javax.annotation.Nullable; | ||
54 | import java.util.ArrayList; | 57 | import java.util.ArrayList; |
55 | import java.util.Arrays; | 58 | import java.util.Arrays; |
56 | import java.util.Collection; | 59 | import java.util.Collection; |
60 | +import java.util.Collections; | ||
61 | +import java.util.Comparator; | ||
57 | import java.util.List; | 62 | import java.util.List; |
58 | import java.util.concurrent.ExecutionException; | 63 | import java.util.concurrent.ExecutionException; |
59 | import java.util.stream.Collectors; | 64 | import java.util.stream.Collectors; |
@@ -63,6 +68,7 @@ import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; | @@ -63,6 +68,7 @@ import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; | ||
63 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; | 68 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
64 | import static org.thingsboard.server.dao.service.Validator.validateId; | 69 | import static org.thingsboard.server.dao.service.Validator.validateId; |
65 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; | 70 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; |
71 | +import static org.thingsboard.server.dao.service.Validator.validateString; | ||
66 | 72 | ||
67 | /** | 73 | /** |
68 | * Created by Victor Basanets on 8/28/2017. | 74 | * Created by Victor Basanets on 8/28/2017. |
@@ -158,6 +164,16 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -158,6 +164,16 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
158 | } | 164 | } |
159 | 165 | ||
160 | @Override | 166 | @Override |
167 | + public TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type) { | ||
168 | + log.trace("Executing findEntityViewByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type); | ||
169 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
170 | + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); | ||
171 | + validateString(type, "Incorrect type " + type); | ||
172 | + List<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndType(tenantId.getId(), type, pageLink); | ||
173 | + return new TextPageData<>(entityViews, pageLink); | ||
174 | + } | ||
175 | + | ||
176 | + @Override | ||
161 | public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, | 177 | public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, |
162 | TextPageLink pageLink) { | 178 | TextPageLink pageLink) { |
163 | log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + | 179 | log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + |
@@ -171,6 +187,19 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -171,6 +187,19 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
171 | } | 187 | } |
172 | 188 | ||
173 | @Override | 189 | @Override |
190 | + public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type) { | ||
191 | + log.trace("Executing findEntityViewsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + | ||
192 | + " pageLink [{}], type [{}]", tenantId, customerId, pageLink, type); | ||
193 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
194 | + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); | ||
195 | + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); | ||
196 | + validateString(type, "Incorrect type " + type); | ||
197 | + List<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId.getId(), | ||
198 | + customerId.getId(), type, pageLink); | ||
199 | + return new TextPageData<>(entityViews, pageLink); | ||
200 | + } | ||
201 | + | ||
202 | + @Override | ||
174 | public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) { | 203 | public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) { |
175 | ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); | 204 | ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery()); |
176 | ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> { | 205 | ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> { |
@@ -184,6 +213,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -184,6 +213,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
184 | } | 213 | } |
185 | return Futures.successfulAsList(futures); | 214 | return Futures.successfulAsList(futures); |
186 | }); | 215 | }); |
216 | + | ||
217 | + entityViews = Futures.transform(entityViews, new Function<List<EntityView>, List<EntityView>>() { | ||
218 | + @Nullable | ||
219 | + @Override | ||
220 | + public List<EntityView> apply(@Nullable List<EntityView> entityViewList) { | ||
221 | + return entityViewList == null ? Collections.emptyList() : entityViewList.stream().filter(entityView -> query.getEntityViewTypes().contains(entityView.getType())).collect(Collectors.toList()); | ||
222 | + } | ||
223 | + }); | ||
224 | + | ||
187 | return entityViews; | 225 | return entityViews; |
188 | } | 226 | } |
189 | 227 | ||
@@ -216,6 +254,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -216,6 +254,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
216 | public void onSuccess(@Nullable List<EntityView> result) { | 254 | public void onSuccess(@Nullable List<EntityView> result) { |
217 | cache.putIfAbsent(tenantIdAndEntityId, result); | 255 | cache.putIfAbsent(tenantIdAndEntityId, result); |
218 | } | 256 | } |
257 | + | ||
219 | @Override | 258 | @Override |
220 | public void onFailure(Throwable t) { | 259 | public void onFailure(Throwable t) { |
221 | log.error("Error while finding entity views by tenantId and entityId", t); | 260 | log.error("Error while finding entity views by tenantId and entityId", t); |
@@ -243,6 +282,18 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -243,6 +282,18 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
243 | tenantEntityViewRemover.removeEntities(tenantId); | 282 | tenantEntityViewRemover.removeEntities(tenantId); |
244 | } | 283 | } |
245 | 284 | ||
285 | + @Override | ||
286 | + public ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId) { | ||
287 | + log.trace("Executing findEntityViewTypesByTenantId, tenantId [{}]", tenantId); | ||
288 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
289 | + ListenableFuture<List<EntitySubtype>> tenantEntityViewTypes = entityViewDao.findTenantEntityViewTypesAsync(tenantId.getId()); | ||
290 | + return Futures.transform(tenantEntityViewTypes, | ||
291 | + entityViewTypes -> { | ||
292 | + entityViewTypes.sort(Comparator.comparing(EntitySubtype::getType)); | ||
293 | + return entityViewTypes; | ||
294 | + }); | ||
295 | + } | ||
296 | + | ||
246 | private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys) { | 297 | private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys) { |
247 | if (keys != null && !keys.isEmpty()) { | 298 | if (keys != null && !keys.isEmpty()) { |
248 | ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); | 299 | ListenableFuture<List<AttributeKvEntry>> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); |
@@ -296,6 +347,9 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -296,6 +347,9 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
296 | 347 | ||
297 | @Override | 348 | @Override |
298 | protected void validateDataImpl(EntityView entityView) { | 349 | protected void validateDataImpl(EntityView entityView) { |
350 | + if (StringUtils.isEmpty(entityView.getType())) { | ||
351 | + throw new DataValidationException("Entity View type should be specified!"); | ||
352 | + } | ||
299 | if (StringUtils.isEmpty(entityView.getName())) { | 353 | if (StringUtils.isEmpty(entityView.getName())) { |
300 | throw new DataValidationException("Entity view name should be specified!"); | 354 | throw new DataValidationException("Entity view name should be specified!"); |
301 | } | 355 | } |
@@ -145,18 +145,21 @@ public class ModelConstants { | @@ -145,18 +145,21 @@ public class ModelConstants { | ||
145 | /** | 145 | /** |
146 | * Cassandra entityView constants. | 146 | * Cassandra entityView constants. |
147 | */ | 147 | */ |
148 | - public static final String ENTITY_VIEW_TABLE_FAMILY_NAME = "entity_views"; | 148 | + public static final String ENTITY_VIEW_TABLE_FAMILY_NAME = "entity_view"; |
149 | public static final String ENTITY_VIEW_ENTITY_ID_PROPERTY = ENTITY_ID_COLUMN; | 149 | public static final String ENTITY_VIEW_ENTITY_ID_PROPERTY = ENTITY_ID_COLUMN; |
150 | public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; | 150 | public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; |
151 | public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; | 151 | public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; |
152 | public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; | 152 | public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; |
153 | public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer"; | 153 | public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer"; |
154 | + public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF = "entity_view_by_tenant_and_customer_and_type"; | ||
154 | public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id"; | 155 | public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id"; |
155 | public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; | 156 | public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; |
157 | + public static final String ENTITY_VIEW_TYPE_PROPERTY = "type"; | ||
156 | public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts"; | 158 | public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts"; |
157 | public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts"; | 159 | public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts"; |
158 | public static final String ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; | 160 | 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"; | 161 | + public static final String ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF = "entity_view_by_tenant_and_search_text"; |
162 | + public static final String ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF = "entity_view_by_tenant_by_type_and_search_text"; | ||
160 | public static final String ENTITY_VIEW_BY_TENANT_AND_NAME = "entity_view_by_tenant_and_name"; | 163 | public static final String ENTITY_VIEW_BY_TENANT_AND_NAME = "entity_view_by_tenant_and_name"; |
161 | 164 | ||
162 | /** | 165 | /** |
@@ -41,6 +41,7 @@ import javax.persistence.Enumerated; | @@ -41,6 +41,7 @@ import javax.persistence.Enumerated; | ||
41 | import java.io.IOException; | 41 | import java.io.IOException; |
42 | import java.util.UUID; | 42 | import java.util.UUID; |
43 | 43 | ||
44 | +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; | ||
44 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; | 45 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; |
45 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; | 46 | import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; |
46 | import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; | 47 | import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; |
@@ -71,6 +72,10 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { | @@ -71,6 +72,10 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { | ||
71 | @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) | 72 | @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) |
72 | private UUID customerId; | 73 | private UUID customerId; |
73 | 74 | ||
75 | + @PartitionKey(value = 3) | ||
76 | + @Column(name = DEVICE_TYPE_PROPERTY) | ||
77 | + private String type; | ||
78 | + | ||
74 | @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) | 79 | @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) |
75 | private UUID entityId; | 80 | private UUID entityId; |
76 | 81 | ||
@@ -113,6 +118,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { | @@ -113,6 +118,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { | ||
113 | if (entityView.getCustomerId() != null) { | 118 | if (entityView.getCustomerId() != null) { |
114 | this.customerId = entityView.getCustomerId().getId(); | 119 | this.customerId = entityView.getCustomerId().getId(); |
115 | } | 120 | } |
121 | + this.type = entityView.getType(); | ||
116 | this.name = entityView.getName(); | 122 | this.name = entityView.getName(); |
117 | try { | 123 | try { |
118 | this.keys = mapper.writeValueAsString(entityView.getKeys()); | 124 | this.keys = mapper.writeValueAsString(entityView.getKeys()); |
@@ -143,6 +149,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { | @@ -143,6 +149,7 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> { | ||
143 | if (customerId != null) { | 149 | if (customerId != null) { |
144 | entityView.setCustomerId(new CustomerId(customerId)); | 150 | entityView.setCustomerId(new CustomerId(customerId)); |
145 | } | 151 | } |
152 | + entityView.setType(type); | ||
146 | entityView.setName(name); | 153 | entityView.setName(name); |
147 | try { | 154 | try { |
148 | entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); | 155 | entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); |
@@ -69,6 +69,9 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc | @@ -69,6 +69,9 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc | ||
69 | @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) | 69 | @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) |
70 | private String customerId; | 70 | private String customerId; |
71 | 71 | ||
72 | + @Column(name = ModelConstants.DEVICE_TYPE_PROPERTY) | ||
73 | + private String type; | ||
74 | + | ||
72 | @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) | 75 | @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) |
73 | private String name; | 76 | private String name; |
74 | 77 | ||
@@ -108,6 +111,7 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc | @@ -108,6 +111,7 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc | ||
108 | if (entityView.getCustomerId() != null) { | 111 | if (entityView.getCustomerId() != null) { |
109 | this.customerId = toString(entityView.getCustomerId().getId()); | 112 | this.customerId = toString(entityView.getCustomerId().getId()); |
110 | } | 113 | } |
114 | + this.type = entityView.getType(); | ||
111 | this.name = entityView.getName(); | 115 | this.name = entityView.getName(); |
112 | try { | 116 | try { |
113 | this.keys = mapper.writeValueAsString(entityView.getKeys()); | 117 | this.keys = mapper.writeValueAsString(entityView.getKeys()); |
@@ -144,6 +148,7 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc | @@ -144,6 +148,7 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> implements Searc | ||
144 | if (customerId != null) { | 148 | if (customerId != null) { |
145 | entityView.setCustomerId(new CustomerId(toUUID(customerId))); | 149 | entityView.setCustomerId(new CustomerId(toUUID(customerId))); |
146 | } | 150 | } |
151 | + entityView.setType(type); | ||
147 | entityView.setName(name); | 152 | entityView.setName(name); |
148 | try { | 153 | try { |
149 | entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); | 154 | entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); |
@@ -19,8 +19,6 @@ import org.springframework.data.domain.Pageable; | @@ -19,8 +19,6 @@ import org.springframework.data.domain.Pageable; | ||
19 | import org.springframework.data.jpa.repository.Query; | 19 | import org.springframework.data.jpa.repository.Query; |
20 | import org.springframework.data.repository.CrudRepository; | 20 | import org.springframework.data.repository.CrudRepository; |
21 | import org.springframework.data.repository.query.Param; | 21 | import org.springframework.data.repository.query.Param; |
22 | -import org.thingsboard.server.common.data.EntityView; | ||
23 | -import org.thingsboard.server.common.data.id.EntityId; | ||
24 | import org.thingsboard.server.dao.model.sql.EntityViewEntity; | 22 | import org.thingsboard.server.dao.model.sql.EntityViewEntity; |
25 | import org.thingsboard.server.dao.util.SqlDao; | 23 | import org.thingsboard.server.dao.util.SqlDao; |
26 | 24 | ||
@@ -36,21 +34,46 @@ public interface EntityViewRepository extends CrudRepository<EntityViewEntity, S | @@ -36,21 +34,46 @@ public interface EntityViewRepository extends CrudRepository<EntityViewEntity, S | ||
36 | "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + | 34 | "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + |
37 | "AND e.id > :idOffset ORDER BY e.id") | 35 | "AND e.id > :idOffset ORDER BY e.id") |
38 | List<EntityViewEntity> findByTenantId(@Param("tenantId") String tenantId, | 36 | List<EntityViewEntity> findByTenantId(@Param("tenantId") String tenantId, |
39 | - @Param("textSearch") String textSearch, | ||
40 | - @Param("idOffset") String idOffset, | ||
41 | - Pageable pageable); | 37 | + @Param("textSearch") String textSearch, |
38 | + @Param("idOffset") String idOffset, | ||
39 | + Pageable pageable); | ||
40 | + | ||
41 | + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + | ||
42 | + "AND e.type = :type " + | ||
43 | + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + | ||
44 | + "AND e.id > :idOffset ORDER BY e.id") | ||
45 | + List<EntityViewEntity> findByTenantIdAndType(@Param("tenantId") String tenantId, | ||
46 | + @Param("type") String type, | ||
47 | + @Param("textSearch") String textSearch, | ||
48 | + @Param("idOffset") String idOffset, | ||
49 | + Pageable pageable); | ||
42 | 50 | ||
43 | @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + | 51 | @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + |
44 | "AND e.customerId = :customerId " + | 52 | "AND e.customerId = :customerId " + |
45 | "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + | 53 | "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + |
46 | "AND e.id > :idOffset ORDER BY e.id") | 54 | "AND e.id > :idOffset ORDER BY e.id") |
47 | List<EntityViewEntity> findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, | 55 | List<EntityViewEntity> findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, |
48 | - @Param("customerId") String customerId, | ||
49 | - @Param("searchText") String searchText, | ||
50 | - @Param("idOffset") String idOffset, | ||
51 | - Pageable pageable); | 56 | + @Param("customerId") String customerId, |
57 | + @Param("searchText") String searchText, | ||
58 | + @Param("idOffset") String idOffset, | ||
59 | + Pageable pageable); | ||
60 | + | ||
61 | + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + | ||
62 | + "AND e.customerId = :customerId " + | ||
63 | + "AND e.type = :type " + | ||
64 | + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + | ||
65 | + "AND e.id > :idOffset ORDER BY e.id") | ||
66 | + List<EntityViewEntity> findByTenantIdAndCustomerIdAndType(@Param("tenantId") String tenantId, | ||
67 | + @Param("customerId") String customerId, | ||
68 | + @Param("type") String type, | ||
69 | + @Param("searchText") String searchText, | ||
70 | + @Param("idOffset") String idOffset, | ||
71 | + Pageable pageable); | ||
52 | 72 | ||
53 | EntityViewEntity findByTenantIdAndName(String tenantId, String name); | 73 | EntityViewEntity findByTenantIdAndName(String tenantId, String name); |
54 | 74 | ||
55 | List<EntityViewEntity> findAllByTenantIdAndEntityId(String tenantId, String entityId); | 75 | List<EntityViewEntity> findAllByTenantIdAndEntityId(String tenantId, String entityId); |
76 | + | ||
77 | + @Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId") | ||
78 | + List<String> findTenantEntityViewTypes(@Param("tenantId") String tenantId); | ||
56 | } | 79 | } |
@@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.EntitySubtype; | @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.EntitySubtype; | ||
24 | import org.thingsboard.server.common.data.EntityType; | 24 | import org.thingsboard.server.common.data.EntityType; |
25 | import org.thingsboard.server.common.data.EntityView; | 25 | import org.thingsboard.server.common.data.EntityView; |
26 | import org.thingsboard.server.common.data.UUIDConverter; | 26 | import org.thingsboard.server.common.data.UUIDConverter; |
27 | -import org.thingsboard.server.common.data.id.EntityId; | ||
28 | import org.thingsboard.server.common.data.id.TenantId; | 27 | import org.thingsboard.server.common.data.id.TenantId; |
29 | import org.thingsboard.server.common.data.page.TextPageLink; | 28 | import org.thingsboard.server.common.data.page.TextPageLink; |
30 | import org.thingsboard.server.dao.DaoUtil; | 29 | import org.thingsboard.server.dao.DaoUtil; |
@@ -41,7 +40,6 @@ import java.util.Optional; | @@ -41,7 +40,6 @@ import java.util.Optional; | ||
41 | import java.util.UUID; | 40 | import java.util.UUID; |
42 | 41 | ||
43 | import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; | 42 | import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; |
44 | -import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; | ||
45 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; | 43 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; |
46 | 44 | ||
47 | /** | 45 | /** |
@@ -76,6 +74,17 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, | @@ -76,6 +74,17 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, | ||
76 | } | 74 | } |
77 | 75 | ||
78 | @Override | 76 | @Override |
77 | + public List<EntityView> findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { | ||
78 | + return DaoUtil.convertDataList( | ||
79 | + entityViewRepository.findByTenantIdAndType( | ||
80 | + fromTimeUUID(tenantId), | ||
81 | + type, | ||
82 | + Objects.toString(pageLink.getTextSearch(), ""), | ||
83 | + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), | ||
84 | + new PageRequest(0, pageLink.getLimit()))); | ||
85 | + } | ||
86 | + | ||
87 | + @Override | ||
79 | public Optional<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String name) { | 88 | public Optional<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String name) { |
80 | return Optional.ofNullable( | 89 | return Optional.ofNullable( |
81 | DaoUtil.getData(entityViewRepository.findByTenantIdAndName(fromTimeUUID(tenantId), name))); | 90 | DaoUtil.getData(entityViewRepository.findByTenantIdAndName(fromTimeUUID(tenantId), name))); |
@@ -96,8 +105,37 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, | @@ -96,8 +105,37 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, | ||
96 | } | 105 | } |
97 | 106 | ||
98 | @Override | 107 | @Override |
108 | + public List<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { | ||
109 | + return DaoUtil.convertDataList( | ||
110 | + entityViewRepository.findByTenantIdAndCustomerIdAndType( | ||
111 | + fromTimeUUID(tenantId), | ||
112 | + fromTimeUUID(customerId), | ||
113 | + type, | ||
114 | + Objects.toString(pageLink.getTextSearch(), ""), | ||
115 | + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), | ||
116 | + new PageRequest(0, pageLink.getLimit()) | ||
117 | + )); | ||
118 | + } | ||
119 | + | ||
120 | + @Override | ||
99 | public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { | 121 | public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { |
100 | return service.submit(() -> DaoUtil.convertDataList( | 122 | return service.submit(() -> DaoUtil.convertDataList( |
101 | entityViewRepository.findAllByTenantIdAndEntityId(UUIDConverter.fromTimeUUID(tenantId), UUIDConverter.fromTimeUUID(entityId)))); | 123 | entityViewRepository.findAllByTenantIdAndEntityId(UUIDConverter.fromTimeUUID(tenantId), UUIDConverter.fromTimeUUID(entityId)))); |
102 | } | 124 | } |
125 | + | ||
126 | + @Override | ||
127 | + public ListenableFuture<List<EntitySubtype>> findTenantEntityViewTypesAsync(UUID tenantId) { | ||
128 | + return service.submit(() -> convertTenantEntityViewTypesToDto(tenantId, entityViewRepository.findTenantEntityViewTypes(fromTimeUUID(tenantId)))); | ||
129 | + } | ||
130 | + | ||
131 | + private List<EntitySubtype> convertTenantEntityViewTypesToDto(UUID tenantId, List<String> types) { | ||
132 | + List<EntitySubtype> list = Collections.emptyList(); | ||
133 | + if (types != null && !types.isEmpty()) { | ||
134 | + list = new ArrayList<>(); | ||
135 | + for (String type : types) { | ||
136 | + list.add(new EntitySubtype(new TenantId(tenantId), EntityType.ENTITY_VIEW, type)); | ||
137 | + } | ||
138 | + } | ||
139 | + return list; | ||
140 | + } | ||
103 | } | 141 | } |
@@ -624,61 +624,90 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( | @@ -624,61 +624,90 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( | ||
624 | PRIMARY KEY (id) | 624 | PRIMARY KEY (id) |
625 | ); | 625 | ); |
626 | 626 | ||
627 | -CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( | 627 | +CREATE TABLE IF NOT EXISTS thingsboard.entity_view ( |
628 | id timeuuid, | 628 | id timeuuid, |
629 | entity_id timeuuid, | 629 | entity_id timeuuid, |
630 | entity_type text, | 630 | entity_type text, |
631 | tenant_id timeuuid, | 631 | tenant_id timeuuid, |
632 | customer_id timeuuid, | 632 | customer_id timeuuid, |
633 | name text, | 633 | name text, |
634 | + type text, | ||
634 | keys text, | 635 | keys text, |
635 | start_ts bigint, | 636 | start_ts bigint, |
636 | end_ts bigint, | 637 | end_ts bigint, |
637 | search_text text, | 638 | search_text text, |
638 | additional_info text, | 639 | additional_info text, |
639 | - PRIMARY KEY (id, entity_id, tenant_id, customer_id) | 640 | + PRIMARY KEY (id, entity_id, tenant_id, customer_id, type) |
640 | ); | 641 | ); |
641 | 642 | ||
642 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS | 643 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS |
643 | SELECT * | 644 | SELECT * |
644 | - from thingsboard.entity_views | 645 | + from thingsboard.entity_view |
645 | WHERE tenant_id IS NOT NULL | 646 | WHERE tenant_id IS NOT NULL |
646 | AND entity_id IS NOT NULL | 647 | AND entity_id IS NOT NULL |
647 | AND customer_id IS NOT NULL | 648 | AND customer_id IS NOT NULL |
649 | + AND type IS NOT NULL | ||
648 | AND name IS NOT NULL | 650 | AND name IS NOT NULL |
649 | AND id IS NOT NULL | 651 | AND id IS NOT NULL |
650 | - PRIMARY KEY (tenant_id, name, id, customer_id, entity_id) | 652 | + PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type) |
651 | WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); | 653 | WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); |
652 | 654 | ||
653 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS | 655 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS |
654 | SELECT * | 656 | SELECT * |
655 | - from thingsboard.entity_views | 657 | + from thingsboard.entity_view |
656 | WHERE tenant_id IS NOT NULL | 658 | WHERE tenant_id IS NOT NULL |
657 | AND entity_id IS NOT NULL | 659 | AND entity_id IS NOT NULL |
658 | AND customer_id IS NOT NULL | 660 | AND customer_id IS NOT NULL |
661 | + AND type IS NOT NULL | ||
659 | AND search_text IS NOT NULL | 662 | AND search_text IS NOT NULL |
660 | AND id IS NOT NULL | 663 | AND id IS NOT NULL |
661 | - PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id) | 664 | + PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type) |
662 | WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); | 665 | WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); |
663 | 666 | ||
667 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS | ||
668 | + SELECT * | ||
669 | + from thingsboard.entity_view | ||
670 | + WHERE tenant_id IS NOT NULL | ||
671 | + AND entity_id IS NOT NULL | ||
672 | + AND customer_id IS NOT NULL | ||
673 | + AND type IS NOT NULL | ||
674 | + AND search_text IS NOT NULL | ||
675 | + AND id IS NOT NULL | ||
676 | + PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id) | ||
677 | + WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC); | ||
678 | + | ||
664 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS | 679 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS |
665 | SELECT * | 680 | SELECT * |
666 | - from thingsboard.entity_views | 681 | + from thingsboard.entity_view |
667 | WHERE tenant_id IS NOT NULL | 682 | WHERE tenant_id IS NOT NULL |
668 | AND customer_id IS NOT NULL | 683 | AND customer_id IS NOT NULL |
669 | AND entity_id IS NOT NULL | 684 | AND entity_id IS NOT NULL |
685 | + AND type IS NOT NULL | ||
670 | AND search_text IS NOT NULL | 686 | AND search_text IS NOT NULL |
671 | AND id IS NOT NULL | 687 | AND id IS NOT NULL |
672 | - PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) | 688 | + PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type) |
673 | WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); | 689 | WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); |
674 | 690 | ||
691 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS | ||
692 | + SELECT * | ||
693 | + from thingsboard.entity_view | ||
694 | + WHERE tenant_id IS NOT NULL | ||
695 | + AND customer_id IS NOT NULL | ||
696 | + AND entity_id IS NOT NULL | ||
697 | + AND type IS NOT NULL | ||
698 | + AND search_text IS NOT NULL | ||
699 | + AND id IS NOT NULL | ||
700 | + PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id) | ||
701 | + WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC); | ||
702 | + | ||
675 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS | 703 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS |
676 | SELECT * | 704 | SELECT * |
677 | - from thingsboard.entity_views | 705 | + from thingsboard.entity_view |
678 | WHERE tenant_id IS NOT NULL | 706 | WHERE tenant_id IS NOT NULL |
679 | AND customer_id IS NOT NULL | 707 | AND customer_id IS NOT NULL |
680 | AND entity_id IS NOT NULL | 708 | AND entity_id IS NOT NULL |
709 | + AND type IS NOT NULL | ||
681 | AND search_text IS NOT NULL | 710 | AND search_text IS NOT NULL |
682 | AND id IS NOT NULL | 711 | AND id IS NOT NULL |
683 | - PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id) | 712 | + PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) |
684 | WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); | 713 | WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); |
@@ -228,12 +228,13 @@ CREATE TABLE IF NOT EXISTS rule_node ( | @@ -228,12 +228,13 @@ CREATE TABLE IF NOT EXISTS rule_node ( | ||
228 | search_text varchar(255) | 228 | search_text varchar(255) |
229 | ); | 229 | ); |
230 | 230 | ||
231 | -CREATE TABLE IF NOT EXISTS entity_views ( | 231 | +CREATE TABLE IF NOT EXISTS entity_view ( |
232 | id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, | 232 | id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, |
233 | entity_id varchar(31), | 233 | entity_id varchar(31), |
234 | entity_type varchar(255), | 234 | entity_type varchar(255), |
235 | tenant_id varchar(31), | 235 | tenant_id varchar(31), |
236 | customer_id varchar(31), | 236 | customer_id varchar(31), |
237 | + type varchar(255), | ||
237 | name varchar(255), | 238 | name varchar(255), |
238 | keys varchar(255), | 239 | keys varchar(255), |
239 | start_ts bigint, | 240 | start_ts bigint, |
@@ -19,4 +19,4 @@ DROP TABLE IF EXISTS widget_type; | @@ -19,4 +19,4 @@ DROP TABLE IF EXISTS widget_type; | ||
19 | DROP TABLE IF EXISTS widgets_bundle; | 19 | DROP TABLE IF EXISTS widgets_bundle; |
20 | DROP TABLE IF EXISTS rule_node; | 20 | DROP TABLE IF EXISTS rule_node; |
21 | DROP TABLE IF EXISTS rule_chain; | 21 | DROP TABLE IF EXISTS rule_chain; |
22 | -DROP TABLE IF EXISTS entity_views; | 22 | +DROP TABLE IF EXISTS entity_view; |
@@ -533,6 +533,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -533,6 +533,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
533 | } | 533 | } |
534 | ); | 534 | ); |
535 | break; | 535 | break; |
536 | + case types.aliasFilterType.entityViewType.value: | ||
537 | + getEntitiesByNameFilter(types.entityType.entityView, filter.entityViewNameFilter, maxItems, {ignoreLoading: true}, filter.entityViewType).then( | ||
538 | + function success(entities) { | ||
539 | + if (entities && entities.length || !failOnEmpty) { | ||
540 | + result.entities = entitiesToEntitiesInfo(entities); | ||
541 | + deferred.resolve(result); | ||
542 | + } else { | ||
543 | + deferred.reject(); | ||
544 | + } | ||
545 | + }, | ||
546 | + function fail() { | ||
547 | + deferred.reject(); | ||
548 | + } | ||
549 | + ); | ||
550 | + break; | ||
536 | case types.aliasFilterType.relationsQuery.value: | 551 | case types.aliasFilterType.relationsQuery.value: |
537 | result.stateEntity = filter.rootStateEntity; | 552 | result.stateEntity = filter.rootStateEntity; |
538 | var rootEntityType; | 553 | var rootEntityType; |
@@ -578,6 +593,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -578,6 +593,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
578 | break; | 593 | break; |
579 | case types.aliasFilterType.assetSearchQuery.value: | 594 | case types.aliasFilterType.assetSearchQuery.value: |
580 | case types.aliasFilterType.deviceSearchQuery.value: | 595 | case types.aliasFilterType.deviceSearchQuery.value: |
596 | + case types.aliasFilterType.entityViewSearchQuery.value: | ||
581 | result.stateEntity = filter.rootStateEntity; | 597 | result.stateEntity = filter.rootStateEntity; |
582 | if (result.stateEntity && stateEntityId) { | 598 | if (result.stateEntity && stateEntityId) { |
583 | rootEntityType = stateEntityId.entityType; | 599 | rootEntityType = stateEntityId.entityType; |
@@ -604,6 +620,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -604,6 +620,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
604 | } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) { | 620 | } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) { |
605 | searchQuery.deviceTypes = filter.deviceTypes; | 621 | searchQuery.deviceTypes = filter.deviceTypes; |
606 | findByQueryPromise = deviceService.findByQuery(searchQuery, false, {ignoreLoading: true}); | 622 | findByQueryPromise = deviceService.findByQuery(searchQuery, false, {ignoreLoading: true}); |
623 | + } else if (filter.type == types.aliasFilterType.entityViewSearchQuery.value) { | ||
624 | + searchQuery.entityViewTypes = filter.entityViewTypes; | ||
625 | + findByQueryPromise = entityViewService.findByQuery(searchQuery, false, {ignoreLoading: true}); | ||
607 | } | 626 | } |
608 | findByQueryPromise.then( | 627 | findByQueryPromise.then( |
609 | function success(entities) { | 628 | function success(entities) { |
@@ -646,6 +665,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -646,6 +665,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
646 | return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; | 665 | return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; |
647 | case types.aliasFilterType.deviceType.value: | 666 | case types.aliasFilterType.deviceType.value: |
648 | return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; | 667 | return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; |
668 | + case types.aliasFilterType.entityViewType.value: | ||
669 | + return entityTypes.indexOf(types.entityType.entityView) > -1 ? true : false; | ||
649 | case types.aliasFilterType.relationsQuery.value: | 670 | case types.aliasFilterType.relationsQuery.value: |
650 | if (filter.filters && filter.filters.length) { | 671 | if (filter.filters && filter.filters.length) { |
651 | var match = false; | 672 | var match = false; |
@@ -671,6 +692,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -671,6 +692,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
671 | return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; | 692 | return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; |
672 | case types.aliasFilterType.deviceSearchQuery.value: | 693 | case types.aliasFilterType.deviceSearchQuery.value: |
673 | return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; | 694 | return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; |
695 | + case types.aliasFilterType.entityViewSearchQuery.value: | ||
696 | + return entityTypes.indexOf(types.entityType.entityView) > -1 ? true : false; | ||
674 | } | 697 | } |
675 | } | 698 | } |
676 | return false; | 699 | return false; |
@@ -690,12 +713,16 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -690,12 +713,16 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
690 | return entityType === types.entityType.asset; | 713 | return entityType === types.entityType.asset; |
691 | case types.aliasFilterType.deviceType.value: | 714 | case types.aliasFilterType.deviceType.value: |
692 | return entityType === types.entityType.device; | 715 | return entityType === types.entityType.device; |
716 | + case types.aliasFilterType.entityViewType.value: | ||
717 | + return entityType === types.entityType.entityView; | ||
693 | case types.aliasFilterType.relationsQuery.value: | 718 | case types.aliasFilterType.relationsQuery.value: |
694 | return true; | 719 | return true; |
695 | case types.aliasFilterType.assetSearchQuery.value: | 720 | case types.aliasFilterType.assetSearchQuery.value: |
696 | return entityType === types.entityType.asset; | 721 | return entityType === types.entityType.asset; |
697 | case types.aliasFilterType.deviceSearchQuery.value: | 722 | case types.aliasFilterType.deviceSearchQuery.value: |
698 | return entityType === types.entityType.device; | 723 | return entityType === types.entityType.device; |
724 | + case types.aliasFilterType.entityViewSearchQuery.value: | ||
725 | + return entityType === types.entityType.entityView; | ||
699 | } | 726 | } |
700 | return false; | 727 | return false; |
701 | } | 728 | } |
@@ -1046,6 +1073,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -1046,6 +1073,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
1046 | return assetService.deleteAsset(entityId.id); | 1073 | return assetService.deleteAsset(entityId.id); |
1047 | } else if (entityId.entityType == types.entityType.device) { | 1074 | } else if (entityId.entityType == types.entityType.device) { |
1048 | return deviceService.deleteDevice(entityId.id); | 1075 | return deviceService.deleteDevice(entityId.id); |
1076 | + } else if (entityId.entityType == types.entityType.entityView) { | ||
1077 | + return entityViewService.deleteEntityView(entityId.id); | ||
1049 | } | 1078 | } |
1050 | } | 1079 | } |
1051 | 1080 | ||
@@ -1151,6 +1180,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -1151,6 +1180,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
1151 | return assetService.saveAsset(entity); | 1180 | return assetService.saveAsset(entity); |
1152 | } else if (entityType == types.entityType.device) { | 1181 | } else if (entityType == types.entityType.device) { |
1153 | return deviceService.saveDevice(entity); | 1182 | return deviceService.saveDevice(entity); |
1183 | + } else if (entityType == types.entityType.entityView) { | ||
1184 | + return entityViewService.saveEntityView(entity); | ||
1154 | } | 1185 | } |
1155 | } | 1186 | } |
1156 | 1187 | ||
@@ -1279,6 +1310,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -1279,6 +1310,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
1279 | searchQuery.assetTypes = entitySubTypes; | 1310 | searchQuery.assetTypes = entitySubTypes; |
1280 | } else if (entityType == types.entityType.device) { | 1311 | } else if (entityType == types.entityType.device) { |
1281 | searchQuery.deviceTypes = entitySubTypes; | 1312 | searchQuery.deviceTypes = entitySubTypes; |
1313 | + } else if (entityType == types.entityType.entityView) { | ||
1314 | + searchQuery.entityViewTypes = entitySubTypes; | ||
1282 | } else { | 1315 | } else { |
1283 | return null; //Not supported | 1316 | return null; //Not supported |
1284 | } | 1317 | } |
@@ -253,6 +253,10 @@ export default angular.module('thingsboard.types', []) | @@ -253,6 +253,10 @@ export default angular.module('thingsboard.types', []) | ||
253 | value: 'deviceType', | 253 | value: 'deviceType', |
254 | name: 'alias.filter-type-device-type' | 254 | name: 'alias.filter-type-device-type' |
255 | }, | 255 | }, |
256 | + entityViewType: { | ||
257 | + value: 'entityViewType', | ||
258 | + name: 'alias.filter-type-entity-view-type' | ||
259 | + }, | ||
256 | relationsQuery: { | 260 | relationsQuery: { |
257 | value: 'relationsQuery', | 261 | value: 'relationsQuery', |
258 | name: 'alias.filter-type-relations-query' | 262 | name: 'alias.filter-type-relations-query' |
@@ -264,6 +268,10 @@ export default angular.module('thingsboard.types', []) | @@ -264,6 +268,10 @@ export default angular.module('thingsboard.types', []) | ||
264 | deviceSearchQuery: { | 268 | deviceSearchQuery: { |
265 | value: 'deviceSearchQuery', | 269 | value: 'deviceSearchQuery', |
266 | name: 'alias.filter-type-device-search-query' | 270 | name: 'alias.filter-type-device-search-query' |
271 | + }, | ||
272 | + entityViewSearchQuery: { | ||
273 | + value: 'entityViewSearchQuery', | ||
274 | + name: 'alias.filter-type-entity-view-search-query' | ||
267 | } | 275 | } |
268 | }, | 276 | }, |
269 | position: { | 277 | position: { |
@@ -52,6 +52,13 @@ | @@ -52,6 +52,13 @@ | ||
52 | <div translate ng-message="required">entity-view.name-required</div> | 52 | <div translate ng-message="required">entity-view.name-required</div> |
53 | </div> | 53 | </div> |
54 | </md-input-container> | 54 | </md-input-container> |
55 | + <tb-entity-subtype-autocomplete | ||
56 | + ng-disabled="$root.loading || !isEdit" | ||
57 | + tb-required="true" | ||
58 | + the-form="theForm" | ||
59 | + ng-model="entityView.type" | ||
60 | + entity-type="types.entityType.entityView"> | ||
61 | + </tb-entity-subtype-autocomplete> | ||
55 | <tb-entity-select flex ng-disabled="!isEdit" | 62 | <tb-entity-select flex ng-disabled="!isEdit" |
56 | the-form="theForm" | 63 | the-form="theForm" |
57 | tb-required="true" | 64 | tb-required="true" |
@@ -77,6 +77,15 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | @@ -77,6 +77,15 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | ||
77 | scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType}); | 77 | scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType}); |
78 | } | 78 | } |
79 | break; | 79 | break; |
80 | + case types.aliasFilterType.entityViewType.value: | ||
81 | + var entityViewType = scope.filter.entityViewType; | ||
82 | + prefix = scope.filter.entityViewNameFilter; | ||
83 | + if (prefix && prefix.length) { | ||
84 | + scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-type-and-name-description', {entityViewType: entityViewType, prefix: prefix}); | ||
85 | + } else { | ||
86 | + scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-type-description', {entityViewType: entityViewType}); | ||
87 | + } | ||
88 | + break; | ||
80 | case types.aliasFilterType.relationsQuery.value: | 89 | case types.aliasFilterType.relationsQuery.value: |
81 | var rootEntityText; | 90 | var rootEntityText; |
82 | var directionText; | 91 | var directionText; |
@@ -134,6 +143,7 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | @@ -134,6 +143,7 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | ||
134 | break; | 143 | break; |
135 | case types.aliasFilterType.assetSearchQuery.value: | 144 | case types.aliasFilterType.assetSearchQuery.value: |
136 | case types.aliasFilterType.deviceSearchQuery.value: | 145 | case types.aliasFilterType.deviceSearchQuery.value: |
146 | + case types.aliasFilterType.entityViewSearchQuery.value: | ||
137 | allEntitiesText = $translate.instant('alias.all-entities'); | 147 | allEntitiesText = $translate.instant('alias.all-entities'); |
138 | anyRelationText = $translate.instant('alias.any-relation'); | 148 | anyRelationText = $translate.instant('alias.any-relation'); |
139 | if (scope.filter.rootStateEntity) { | 149 | if (scope.filter.rootStateEntity) { |
@@ -165,7 +175,7 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | @@ -165,7 +175,7 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | ||
165 | scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-search-query-description', | 175 | scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-search-query-description', |
166 | translationValues | 176 | translationValues |
167 | ); | 177 | ); |
168 | - } else { | 178 | + } else if (scope.filter.type == types.aliasFilterType.deviceSearchQuery.value) { |
169 | var deviceTypesQuoted = []; | 179 | var deviceTypesQuoted = []; |
170 | scope.filter.deviceTypes.forEach(function(deviceType) { | 180 | scope.filter.deviceTypes.forEach(function(deviceType) { |
171 | deviceTypesQuoted.push("'"+deviceType+"'"); | 181 | deviceTypesQuoted.push("'"+deviceType+"'"); |
@@ -175,6 +185,16 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | @@ -175,6 +185,16 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, | ||
175 | scope.filterDisplayValue = $translate.instant('alias.filter-type-device-search-query-description', | 185 | scope.filterDisplayValue = $translate.instant('alias.filter-type-device-search-query-description', |
176 | translationValues | 186 | translationValues |
177 | ); | 187 | ); |
188 | + } else if (scope.filter.type == types.aliasFilterType.entityViewSearchQuery.value) { | ||
189 | + var entityViewTypesQuoted = []; | ||
190 | + scope.filter.entityViewTypes.forEach(function(entityViewType) { | ||
191 | + entityViewTypesQuoted.push("'"+entityViewType+"'"); | ||
192 | + }); | ||
193 | + var entityViewTypesText = entityViewTypesQuoted.join(', '); | ||
194 | + translationValues.entityViewTypes = entityViewTypesText; | ||
195 | + scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-search-query-description', | ||
196 | + translationValues | ||
197 | + ); | ||
178 | } | 198 | } |
179 | break; | 199 | break; |
180 | default: | 200 | default: |
@@ -69,9 +69,14 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc | @@ -69,9 +69,14 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc | ||
69 | filter.deviceType = null; | 69 | filter.deviceType = null; |
70 | filter.deviceNameFilter = ''; | 70 | filter.deviceNameFilter = ''; |
71 | break; | 71 | break; |
72 | + case types.aliasFilterType.entityViewType.value: | ||
73 | + filter.entityViewType = null; | ||
74 | + filter.entityViewNameFilter = ''; | ||
75 | + break; | ||
72 | case types.aliasFilterType.relationsQuery.value: | 76 | case types.aliasFilterType.relationsQuery.value: |
73 | case types.aliasFilterType.assetSearchQuery.value: | 77 | case types.aliasFilterType.assetSearchQuery.value: |
74 | case types.aliasFilterType.deviceSearchQuery.value: | 78 | case types.aliasFilterType.deviceSearchQuery.value: |
79 | + case types.aliasFilterType.entityViewSearchQuery.value: | ||
75 | filter.rootStateEntity = false; | 80 | filter.rootStateEntity = false; |
76 | filter.stateEntityParamName = null; | 81 | filter.stateEntityParamName = null; |
77 | filter.defaultStateEntity = null; | 82 | filter.defaultStateEntity = null; |
@@ -86,6 +91,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc | @@ -86,6 +91,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc | ||
86 | } else if (filter.type === types.aliasFilterType.deviceSearchQuery.value) { | 91 | } else if (filter.type === types.aliasFilterType.deviceSearchQuery.value) { |
87 | filter.relationType = null; | 92 | filter.relationType = null; |
88 | filter.deviceTypes = []; | 93 | filter.deviceTypes = []; |
94 | + } else if (filter.type === types.aliasFilterType.entityViewSearchQuery.value) { | ||
95 | + filter.relationType = null; | ||
96 | + filter.entityViewTypes = []; | ||
89 | } | 97 | } |
90 | break; | 98 | break; |
91 | } | 99 | } |
@@ -112,6 +112,20 @@ | @@ -112,6 +112,20 @@ | ||
112 | aria-label="{{ 'device.name-starts-with' | translate }}"> | 112 | aria-label="{{ 'device.name-starts-with' | translate }}"> |
113 | </md-input-container> | 113 | </md-input-container> |
114 | </section> | 114 | </section> |
115 | + <section layout="column" ng-if="filter.type == types.aliasFilterType.entityViewType.value" id="entityViewTypeFilter"> | ||
116 | + <tb-entity-subtype-autocomplete | ||
117 | + tb-required="true" | ||
118 | + the-form="theForm" | ||
119 | + ng-model="filter.entityViewType" | ||
120 | + entity-type="types.entityType.entityView"> | ||
121 | + </tb-entity-subtype-autocomplete> | ||
122 | + <md-input-container class="md-block"> | ||
123 | + <label translate>entity-view.name-starts-with</label> | ||
124 | + <input name="entityViewNameFilter" | ||
125 | + ng-model="filter.entityViewNameFilter" | ||
126 | + aria-label="{{ 'entity-view.name-starts-with' | translate }}"> | ||
127 | + </md-input-container> | ||
128 | + </section> | ||
115 | <section layout="column" ng-if="filter.type == types.aliasFilterType.relationsQuery.value" id="relationsQueryFilter"> | 129 | <section layout="column" ng-if="filter.type == types.aliasFilterType.relationsQuery.value" id="relationsQueryFilter"> |
116 | <label class="tb-small">{{ 'alias.root-entity' | translate }}</label> | 130 | <label class="tb-small">{{ 'alias.root-entity' | translate }}</label> |
117 | <section class="tb-root-state-entity-switch" layout="row" layout-align="start center" style="padding-left: 0px;"> | 131 | <section class="tb-root-state-entity-switch" layout="row" layout-align="start center" style="padding-left: 0px;"> |
@@ -311,4 +325,73 @@ | @@ -311,4 +325,73 @@ | ||
311 | ng-model="filter.deviceTypes"> | 325 | ng-model="filter.deviceTypes"> |
312 | </tb-entity-subtype-list> | 326 | </tb-entity-subtype-list> |
313 | </section> | 327 | </section> |
328 | + <section layout="column" ng-if="filter.type == types.aliasFilterType.entityViewSearchQuery.value" id="entityViewSearchQueryFilter"> | ||
329 | + <label class="tb-small">{{ 'alias.root-entity' | translate }}</label> | ||
330 | + <section class="tb-root-state-entity-switch" layout="row" layout-align="start center" style="padding-left: 0px;"> | ||
331 | + <md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity" | ||
332 | + aria-label="{{ 'alias.root-state-entity' | translate }}"> | ||
333 | + </md-switch> | ||
334 | + <label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label> | ||
335 | + </section> | ||
336 | + <div flex layout="row" ng-if="!filter.rootStateEntity"> | ||
337 | + <tb-entity-select flex | ||
338 | + the-form="theForm" | ||
339 | + tb-required="!filter.rootStateEntity" | ||
340 | + ng-disabled="filter.rootStateEntity" | ||
341 | + use-alias-entity-types="true" | ||
342 | + ng-model="filter.rootEntity"> | ||
343 | + </tb-entity-select> | ||
344 | + </div> | ||
345 | + <div flex layout="row" ng-if="filter.rootStateEntity"> | ||
346 | + <md-input-container class="md-block" style="margin-top: 32px;"> | ||
347 | + <label translate>alias.state-entity-parameter-name</label> | ||
348 | + <input name="stateEntityParamName" | ||
349 | + placeholder="{{ 'alias.default-entity-parameter-name' | translate }}" | ||
350 | + ng-model="filter.stateEntityParamName" | ||
351 | + aria-label="{{ 'alias.state-entity-parameter-name' | translate }}"> | ||
352 | + </md-input-container> | ||
353 | + <div flex layout="column"> | ||
354 | + <label class="tb-small">{{ 'alias.default-state-entity' | translate }}</label> | ||
355 | + <tb-entity-select flex | ||
356 | + the-form="theForm" | ||
357 | + tb-required="false" | ||
358 | + use-alias-entity-types="true" | ||
359 | + ng-model="filter.defaultStateEntity"> | ||
360 | + </tb-entity-select> | ||
361 | + </div> | ||
362 | + </div> | ||
363 | + <div flex layout="row"> | ||
364 | + <md-input-container class="md-block" style="min-width: 100px;"> | ||
365 | + <label translate>relation.direction</label> | ||
366 | + <md-select required ng-model="filter.direction"> | ||
367 | + <md-option ng-repeat="direction in types.entitySearchDirection" ng-value="direction"> | ||
368 | + {{ ('relation.search-direction.' + direction) | translate}} | ||
369 | + </md-option> | ||
370 | + </md-select> | ||
371 | + </md-input-container> | ||
372 | + <md-input-container flex class="md-block"> | ||
373 | + <label translate>alias.max-relation-level</label> | ||
374 | + <input name="maxRelationLevel" | ||
375 | + type="number" | ||
376 | + min="1" | ||
377 | + step="1" | ||
378 | + placeholder="{{ 'alias.unlimited-level' | translate }}" | ||
379 | + ng-model="filter.maxLevel" | ||
380 | + aria-label="{{ 'alias.max-relation-level' | translate }}"> | ||
381 | + </md-input-container> | ||
382 | + </div> | ||
383 | + <div class="md-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-type</div> | ||
384 | + <tb-relation-type-autocomplete flex | ||
385 | + hide-label | ||
386 | + the-form="theForm" | ||
387 | + ng-model="filter.relationType" | ||
388 | + tb-required="false"> | ||
389 | + </tb-relation-type-autocomplete> | ||
390 | + <div class="md-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>entity-view.entity-view-types</div> | ||
391 | + <tb-entity-subtype-list | ||
392 | + tb-required="true" | ||
393 | + entity-type="types.entityType.entityView" | ||
394 | + ng-model="filter.entityViewTypes"> | ||
395 | + </tb-entity-subtype-list> | ||
396 | + </section> | ||
314 | </div> | 397 | </div> |
@@ -96,6 +96,8 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, | @@ -96,6 +96,8 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, | ||
96 | entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); | 96 | entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); |
97 | } else if (scope.entityType == types.entityType.device) { | 97 | } else if (scope.entityType == types.entityType.device) { |
98 | entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); | 98 | entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); |
99 | + } else if (scope.entityType == types.entityType.entityView) { | ||
100 | + entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true}); | ||
99 | } | 101 | } |
100 | if (entitySubtypesPromise) { | 102 | if (entitySubtypesPromise) { |
101 | entitySubtypesPromise.then( | 103 | entitySubtypesPromise.then( |
@@ -97,6 +97,8 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q, | @@ -97,6 +97,8 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q, | ||
97 | entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); | 97 | entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); |
98 | } else if (scope.entityType == types.entityType.device) { | 98 | } else if (scope.entityType == types.entityType.device) { |
99 | entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); | 99 | entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); |
100 | + } else if (scope.entityType == types.entityType.entityView) { | ||
101 | + entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true}); | ||
100 | } | 102 | } |
101 | if (entitySubtypesPromise) { | 103 | if (entitySubtypesPromise) { |
102 | entitySubtypesPromise.then( | 104 | entitySubtypesPromise.then( |
@@ -75,6 +75,8 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate | @@ -75,6 +75,8 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate | ||
75 | entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); | 75 | entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); |
76 | } else if (scope.entityType == types.entityType.device) { | 76 | } else if (scope.entityType == types.entityType.device) { |
77 | entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); | 77 | entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); |
78 | + } else if (scope.entityType == types.entityType.entityView) { | ||
79 | + entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true}); | ||
78 | } | 80 | } |
79 | if (entitySubtypesPromise) { | 81 | if (entitySubtypesPromise) { |
80 | entitySubtypesPromise.then( | 82 | entitySubtypesPromise.then( |
@@ -158,12 +158,17 @@ | @@ -158,12 +158,17 @@ | ||
158 | "filter-type-device-type": "Device type", | 158 | "filter-type-device-type": "Device type", |
159 | "filter-type-device-type-description": "Devices of type '{{deviceType}}'", | 159 | "filter-type-device-type-description": "Devices of type '{{deviceType}}'", |
160 | "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'", | 160 | "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'", |
161 | + "filter-type-entity-view-type": "Entity View type", | ||
162 | + "filter-type-entity-view-type-description": "Entity Views of type '{{entityView}}'", | ||
163 | + "filter-type-entity-view-type-and-name-description": "Entity Views of type '{{entityView}}' and with name starting with '{{prefix}}'", | ||
161 | "filter-type-relations-query": "Relations query", | 164 | "filter-type-relations-query": "Relations query", |
162 | "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}", | 165 | "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}", |
163 | "filter-type-asset-search-query": "Asset search query", | 166 | "filter-type-asset-search-query": "Asset search query", |
164 | "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", | 167 | "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", |
165 | "filter-type-device-search-query": "Device search query", | 168 | "filter-type-device-search-query": "Device search query", |
166 | "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", | 169 | "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", |
170 | + "filter-type-entity-view-search-query": "Entity view search query", | ||
171 | + "filter-type-entity-view-search-query-description": "Entity views with types {{entityViewTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", | ||
167 | "entity-filter": "Entity filter", | 172 | "entity-filter": "Entity filter", |
168 | "resolve-multiple": "Resolve as multiple entities", | 173 | "resolve-multiple": "Resolve as multiple entities", |
169 | "filter-type": "Filter type", | 174 | "filter-type": "Filter type", |
@@ -839,7 +844,7 @@ | @@ -839,7 +844,7 @@ | ||
839 | "client-attributes": "Client attributes", | 844 | "client-attributes": "Client attributes", |
840 | "shared-attributes": "Shared attributes", | 845 | "shared-attributes": "Shared attributes", |
841 | "server-attributes": "Server attributes", | 846 | "server-attributes": "Server attributes", |
842 | - "latest-timeseries": "Latest timeseries" | 847 | + "latest-timeseries": "Latest timeseries", |
843 | }, | 848 | }, |
844 | "event": { | 849 | "event": { |
845 | "event-type": "Event type", | 850 | "event-type": "Event type", |