Commit 94cb347899bab68fde840a9841853b0e7d1b2b55

Authored by Volodymyr Babak
1 parent 6b4a2ff1

Added entity view type

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",