diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.cql b/application/src/main/data/upgrade/2.1.2/schema_update.cql new file mode 100644 index 0000000000..c02571ada4 --- /dev/null +++ b/application/src/main/data/upgrade/2.1.2/schema_update.cql @@ -0,0 +1,110 @@ +-- +-- Copyright © 2016-2018 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id; + +DROP TABLE IF EXISTS thingsboard.entity_views; + +CREATE TABLE IF NOT EXISTS thingsboard.entity_view ( + id timeuuid, + entity_id timeuuid, + entity_type text, + tenant_id timeuuid, + customer_id timeuuid, + name text, + type text, + keys text, + start_ts bigint, + end_ts bigint, + search_text text, + additional_info text, + PRIMARY KEY (id, entity_id, tenant_id, customer_id, type) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND entity_id IS NOT NULL + AND customer_id IS NOT NULL + AND type IS NOT NULL + AND name IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type) + WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND entity_id IS NOT NULL + AND customer_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type) + WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND entity_id IS NOT NULL + AND customer_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id) + WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND entity_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type) + WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND entity_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id) + WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND entity_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) + WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.1.2/schema_update.sql b/application/src/main/data/upgrade/2.1.2/schema_update.sql new file mode 100644 index 0000000000..14b717c07c --- /dev/null +++ b/application/src/main/data/upgrade/2.1.2/schema_update.sql @@ -0,0 +1,32 @@ +-- +-- Copyright © 2016-2018 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +DROP TABLE IF EXISTS entity_views; + +CREATE TABLE IF NOT EXISTS entity_view ( + id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, + entity_id varchar(31), + entity_type varchar(255), + tenant_id varchar(31), + customer_id varchar(31), + type varchar(255), + name varchar(255), + keys varchar(255), + start_ts bigint, + end_ts bigint, + search_text varchar(255), + additional_info varchar +); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 0ba35e85ad..a1ba1c63f8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import com.google.common.util.concurrent.ListenableFuture; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -26,6 +27,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.audit.ActionType; @@ -38,6 +40,7 @@ import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.service.security.model.SecurityUser; import java.util.List; import java.util.stream.Collectors; @@ -161,6 +164,7 @@ public class EntityViewController extends BaseController { public TextPageData getCustomerEntityViews( @PathVariable("customerId") String strCustomerId, @RequestParam int limit, + @RequestParam(required = false) String type, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String idOffset, @RequestParam(required = false) String textOffset) throws ThingsboardException { @@ -170,7 +174,11 @@ public class EntityViewController extends BaseController { CustomerId customerId = new CustomerId(toUUID(strCustomerId)); checkCustomerId(customerId); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type)); + } else { + return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + } } catch (Exception e) { throw handleException(e); } @@ -181,13 +189,19 @@ public class EntityViewController extends BaseController { @ResponseBody public TextPageData getTenantEntityViews( @RequestParam int limit, + @RequestParam(required = false) String type, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String idOffset, @RequestParam(required = false) String textOffset) throws ThingsboardException { try { TenantId tenantId = getCurrentUser().getTenantId(); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); + + if (type != null && type.trim().length() > 0) { + return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type)); + } else { + return checkNotNull(entityViewService.findEntityViewByTenantId(tenantId, pageLink)); + } } catch (Exception e) { throw handleException(e); } @@ -199,6 +213,7 @@ public class EntityViewController extends BaseController { public List findByQuery(@RequestBody EntityViewSearchQuery query) throws ThingsboardException { checkNotNull(query); checkNotNull(query.getParameters()); + checkNotNull(query.getEntityViewTypes()); checkEntityId(query.getParameters().getEntityId()); try { List entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get()); @@ -215,4 +230,18 @@ public class EntityViewController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/entityView/types", method = RequestMethod.GET) + @ResponseBody + public List getEntityViewTypes() throws ThingsboardException { + try { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId); + return checkNotNull(entityViewTypes.get()); + } catch (Exception e) { + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index da154e9906..45eb66a332 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -97,6 +97,11 @@ public class ThingsboardInstallService { databaseUpgradeService.upgradeDatabase("2.0.0"); + case "2.1.1": + log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ..."); + + databaseUpgradeService.upgradeDatabase("2.1.1"); + log.info("Updating system data..."); systemDataLoaderService.deleteSystemWidgetBundle("charts"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index 934cf8d995..552695314e 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -39,10 +39,19 @@ import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATIO import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE; +import static org.thingsboard.server.service.install.DatabaseHelper.END_TS; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS; import static org.thingsboard.server.service.install.DatabaseHelper.ID; +import static org.thingsboard.server.service.install.DatabaseHelper.KEYS; +import static org.thingsboard.server.service.install.DatabaseHelper.NAME; import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; +import static org.thingsboard.server.service.install.DatabaseHelper.START_TS; import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; +import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; @Service @NoSqlDao @@ -213,6 +222,36 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { break; + case "2.1.1": + + log.info("Upgrading Cassandra DataBase from version {} to 2.1.2 ...", fromVersion); + + cluster.getSession(); + + ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); + + log.info("Dumping entity views ..."); + Path entityViewsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ENTITY_VIEWS, + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, + new String[]{"", "", "", "", "", "", "default", "", "0", "0", "", ""}, + "tb-entity-views"); + log.info("Entity views dumped."); + + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_CQL); + loadCql(schemaUpdateFile); + log.info("Schema updated."); + + log.info("Restoring entity views ..."); + if (entityViewsDump != null) { + CassandraDbHelper.loadCf(ks, cluster.getSession(), ENTITY_VIEW, + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump); + Files.deleteIfExists(entityViewsDump); + } + log.info("Entity views restored."); + + break; + default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java index 2f9b4a5ed3..699eaf1846 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java @@ -45,14 +45,23 @@ public class DatabaseHelper { public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); public static final String DEVICE = "device"; + public static final String ENTITY_ID = "entity_id"; public static final String TENANT_ID = "tenant_id"; + public static final String ENTITY_TYPE = "entity_type"; public static final String CUSTOMER_ID = "customer_id"; public static final String SEARCH_TEXT = "search_text"; public static final String ADDITIONAL_INFO = "additional_info"; public static final String ASSET = "asset"; public static final String DASHBOARD = "dashboard"; + public static final String ENTITY_VIEWS = "entity_views"; + public static final String ENTITY_VIEW = "entity_view"; public static final String ID = "id"; public static final String TITLE = "title"; + public static final String TYPE = "type"; + public static final String NAME = "name"; + public static final String KEYS = "keys"; + public static final String START_TS = "start_ts"; + public static final String END_TS = "end_ts"; public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; public static final String CONFIGURATION = "configuration"; diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 7d701b7a53..2c7f89c75a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -31,14 +31,24 @@ import java.nio.file.Paths; import java.sql.Connection; import java.sql.DriverManager; +import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; +import static org.thingsboard.server.service.install.DatabaseHelper.END_TS; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW; +import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS; import static org.thingsboard.server.service.install.DatabaseHelper.ID; +import static org.thingsboard.server.service.install.DatabaseHelper.KEYS; +import static org.thingsboard.server.service.install.DatabaseHelper.NAME; import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; +import static org.thingsboard.server.service.install.DatabaseHelper.START_TS; import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; +import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; @Service @Profile("install") @@ -115,6 +125,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Schema updated."); } break; + case "2.1.1": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + + log.info("Dumping entity views ..."); + Path entityViewsDump = SqlDbHelper.dumpTableIfExists(conn, ENTITY_VIEWS, + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, + new String[]{"", "", "", "", "", "default", "", "", "0", "0", "", ""}, + "tb-entity-views", true); + log.info("Entity views dumped."); + + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + log.info("Schema updated."); + + log.info("Restoring entity views ..."); + if (entityViewsDump != null) { + SqlDbHelper.loadTable(conn, ENTITY_VIEW, + new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, TYPE, NAME, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump, true); + Files.deleteIfExists(entityViewsDump); + } + log.info("Entity views restored."); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java index bdf980fea2..7c03a719c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java @@ -147,6 +147,8 @@ public class CassandraDbHelper { str = new Double(row.getDouble(index)).toString(); } else if (type == DataType.cint()) { str = new Integer(row.getInt(index)).toString(); + } else if (type == DataType.bigint()) { + str = new Long(row.getLong(index)).toString(); } else if (type == DataType.uuid()) { str = row.getUUID(index).toString(); } else if (type == DataType.timeuuid()) { @@ -193,6 +195,8 @@ public class CassandraDbHelper { boundStatement.setDouble(column, Double.valueOf(value)); } else if (type == DataType.cint()) { boundStatement.setInt(column, Integer.valueOf(value)); + } else if (type == DataType.bigint()) { + boundStatement.setLong(column, Long.valueOf(value)); } else if (type == DataType.uuid()) { boundStatement.setUUID(column, UUID.fromString(value)); } else if (type == DataType.timeuuid()) { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index ee97b97fa0..cb45006c8c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -144,7 +144,9 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testSaveEntityViewWithEmptyName() throws Exception { - doPost("/api/entityView", new EntityView()) + EntityView entityView = new EntityView(); + entityView.setType("default"); + doPost("/api/entityView", entityView) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Entity view name should be specified!"))); } @@ -355,6 +357,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes view.setEntityId(testDevice.getId()); view.setTenantId(savedTenant.getId()); view.setName("Test entity view"); + view.setType("default"); view.setKeys(telemetry); view.setStartTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") * 10); view.setEndTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") / 10); @@ -402,6 +405,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes view.setEntityId(testDevice.getId()); view.setTenantId(savedTenant.getId()); view.setName(name); + view.setType("default"); view.setKeys(telemetry); return doPost("/api/entityView", view, EntityView.class); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index 25b066d2ef..f684e2afa9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -40,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo private TenantId tenantId; private CustomerId customerId; private String name; + private String type; private TelemetryEntityView keys; private long startTimeMs; private long endTimeMs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java index 752eafc6ad..3bf08ea0b4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java @@ -30,6 +30,7 @@ public class EntityViewSearchQuery { private RelationsSearchParameters parameters; private String relationType; + private List entityViewTypes; public EntityRelationsQuery toEntitySearchQuery() { EntityRelationsQuery query = new EntityRelationsQuery(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java index a03dd89815..fe1a8d1f8d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java @@ -15,8 +15,13 @@ */ package org.thingsboard.server.dao.entityview; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Statement; import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.mapping.Result; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -30,6 +35,8 @@ import org.thingsboard.server.dao.model.nosql.EntityViewEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; import org.thingsboard.server.dao.util.NoSqlDao; +import javax.annotation.Nullable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -39,14 +46,21 @@ import java.util.UUID; import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; import static com.datastax.driver.core.querybuilder.QueryBuilder.select; import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_SUBTYPE_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME; -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_PROPERTY; /** @@ -82,13 +96,25 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) { log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink); List entityViewEntities = - findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, + findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF, Collections.singletonList(eq(TENANT_ID_PROPERTY, tenantId)), pageLink); log.trace("Found entity views [{}] by tenantId [{}] and pageLink [{}]", entityViewEntities, tenantId, pageLink); return DaoUtil.convertDataList(entityViewEntities); } + @Override + public List findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { + log.debug("Try to find entity views by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); + List entityViewEntities = + findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF, + Arrays.asList(eq(ENTITY_VIEW_TYPE_PROPERTY, type), + eq(TENANT_ID_PROPERTY, tenantId)), pageLink); + log.trace("Found entity views [{}] by tenantId [{}], type [{}] and pageLink [{}]", + entityViewEntities, tenantId, type, pageLink); + return DaoUtil.convertDataList(entityViewEntities); + } + @Override public Optional findEntityViewByTenantIdAndName(UUID tenantId, String name) { Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_NAME).where(); @@ -110,6 +136,19 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { + log.debug("Try to find entity views by tenantId [{}], customerId[{}], type [{}] and pageLink [{}]", + tenantId, customerId, type, pageLink); + List entityViewEntities = findPageWithTextSearch( + ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF, + Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), + pageLink); + log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", + entityViewEntities, tenantId, customerId, type, pageLink); + return DaoUtil.convertDataList(entityViewEntities); + } + @Override public ListenableFuture> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId); @@ -118,4 +157,30 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao> findTenantEntityViewTypesAsync(UUID tenantId) { + Select select = select().from(ENTITY_SUBTYPE_COLUMN_FAMILY_NAME); + Select.Where query = select.where(); + query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId)); + query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ENTITY_VIEW)); + query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); + ResultSetFuture resultSetFuture = executeAsyncRead(query); + return Futures.transform(resultSetFuture, new Function>() { + @Nullable + @Override + public List apply(@Nullable ResultSet resultSet) { + Result result = cluster.getMapper(EntitySubtypeEntity.class).map(resultSet); + if (result != null) { + List entitySubtypes = new ArrayList<>(); + result.all().forEach((entitySubtypeEntity) -> + entitySubtypes.add(entitySubtypeEntity.toEntitySubtype()) + ); + return entitySubtypes; + } else { + return Collections.emptyList(); + } + } + }); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java index ba433855e2..8147a07a88 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.entityview; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.Dao; @@ -47,6 +48,16 @@ public interface EntityViewDao extends Dao { */ List findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink); + /** + * Find entity views by tenantId, type and page link. + * + * @param tenantId the tenantId + * @param type the type + * @param pageLink the page link + * @return the list of entity view objects + */ + List findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink); + /** * Find entity views by tenantId and entity view name. * @@ -68,6 +79,27 @@ public interface EntityViewDao extends Dao { UUID customerId, TextPageLink pageLink); + /** + * Find entity views by tenantId, customerId, type and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param type the type + * @param pageLink the page link + * @return the list of entity view objects + */ + List findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, + UUID customerId, + String type, + TextPageLink pageLink); ListenableFuture> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId); + + /** + * Find tenants entity view types. + * + * @return the list of tenant entity view type objects + */ + ListenableFuture> findTenantEntityViewTypesAsync(UUID tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java index 19f326c0bc..da87b44322 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.entityview; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; @@ -44,8 +45,12 @@ public interface EntityViewService { TextPageData findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); + TextPageData findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); + TextPageData findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); + TextPageData findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type); + ListenableFuture> findEntityViewsByQuery(EntityViewSearchQuery query); ListenableFuture findEntityViewByIdAsync(EntityViewId entityViewId); @@ -55,4 +60,6 @@ public interface EntityViewService { void deleteEntityView(EntityViewId entityViewId); void deleteEntityViewsByTenantId(TenantId tenantId); + + ListenableFuture> findEntityViewTypesByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 2c5ec75485..6b4ffbcbc4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.entityview; +import com.google.common.base.Function; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -29,6 +30,8 @@ import org.springframework.cache.annotation.Caching; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; @@ -54,6 +57,8 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -63,6 +68,7 @@ import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; +import static org.thingsboard.server.dao.service.Validator.validateString; /** * Created by Victor Basanets on 8/28/2017. @@ -157,6 +163,16 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti return new TextPageData<>(entityViews, pageLink); } + @Override + public TextPageData findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type) { + log.trace("Executing findEntityViewByTenantIdAndType, tenantId [{}], pageLink [{}], type [{}]", tenantId, pageLink, type); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + validateString(type, "Incorrect type " + type); + List entityViews = entityViewDao.findEntityViewsByTenantIdAndType(tenantId.getId(), type, pageLink); + return new TextPageData<>(entityViews, pageLink); + } + @Override public TextPageData findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) { @@ -170,6 +186,19 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti return new TextPageData<>(entityViews, pageLink); } + @Override + public TextPageData findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type) { + log.trace("Executing findEntityViewsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + + " pageLink [{}], type [{}]", tenantId, customerId, pageLink, type); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + validateString(type, "Incorrect type " + type); + List entityViews = entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId.getId(), + customerId.getId(), type, pageLink); + return new TextPageData<>(entityViews, pageLink); + } + @Override public ListenableFuture> findEntityViewsByQuery(EntityViewSearchQuery query) { ListenableFuture> relations = relationService.findByQuery(query.toEntitySearchQuery()); @@ -184,6 +213,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti } return Futures.successfulAsList(futures); }); + + entityViews = Futures.transform(entityViews, new Function, List>() { + @Nullable + @Override + public List apply(@Nullable List entityViewList) { + return entityViewList == null ? Collections.emptyList() : entityViewList.stream().filter(entityView -> query.getEntityViewTypes().contains(entityView.getType())).collect(Collectors.toList()); + } + }); + return entityViews; } @@ -216,6 +254,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti public void onSuccess(@Nullable List result) { cache.putIfAbsent(tenantIdAndEntityId, result); } + @Override public void onFailure(Throwable t) { log.error("Error while finding entity views by tenantId and entityId", t); @@ -243,6 +282,18 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti tenantEntityViewRemover.removeEntities(tenantId); } + @Override + public ListenableFuture> findEntityViewTypesByTenantId(TenantId tenantId) { + log.trace("Executing findEntityViewTypesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + ListenableFuture> tenantEntityViewTypes = entityViewDao.findTenantEntityViewTypesAsync(tenantId.getId()); + return Futures.transform(tenantEntityViewTypes, + entityViewTypes -> { + entityViewTypes.sort(Comparator.comparing(EntitySubtype::getType)); + return entityViewTypes; + }); + } + private ListenableFuture> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection keys) { if (keys != null && !keys.isEmpty()) { ListenableFuture> getAttrFuture = attributesService.find(entityView.getEntityId(), scope, keys); @@ -296,6 +347,9 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti @Override protected void validateDataImpl(EntityView entityView) { + if (StringUtils.isEmpty(entityView.getType())) { + throw new DataValidationException("Entity View type should be specified!"); + } if (StringUtils.isEmpty(entityView.getName())) { throw new DataValidationException("Entity view name should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index a487ede93a..86ba594aa5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -145,18 +145,21 @@ public class ModelConstants { /** * Cassandra entityView constants. */ - public static final String ENTITY_VIEW_TABLE_FAMILY_NAME = "entity_views"; + public static final String ENTITY_VIEW_TABLE_FAMILY_NAME = "entity_view"; public static final String ENTITY_VIEW_ENTITY_ID_PROPERTY = ENTITY_ID_COLUMN; public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer"; + public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF = "entity_view_by_tenant_and_customer_and_type"; public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id"; public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; + public static final String ENTITY_VIEW_TYPE_PROPERTY = "type"; public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts"; public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts"; public static final String ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; - public static final String ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "entity_view_by_tenant_and_search_text"; + public static final String ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_CF = "entity_view_by_tenant_and_search_text"; + public static final String ENTITY_VIEW_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_CF = "entity_view_by_tenant_by_type_and_search_text"; public static final String ENTITY_VIEW_BY_TENANT_AND_NAME = "entity_view_by_tenant_and_name"; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java index eb7f4fe65e..cda4217694 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java @@ -41,6 +41,7 @@ import javax.persistence.Enumerated; import java.io.IOException; import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.DEVICE_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; @@ -71,6 +72,10 @@ public class EntityViewEntity implements SearchTextEntity { @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) private UUID customerId; + @PartitionKey(value = 3) + @Column(name = DEVICE_TYPE_PROPERTY) + private String type; + @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) private UUID entityId; @@ -113,6 +118,7 @@ public class EntityViewEntity implements SearchTextEntity { if (entityView.getCustomerId() != null) { this.customerId = entityView.getCustomerId().getId(); } + this.type = entityView.getType(); this.name = entityView.getName(); try { this.keys = mapper.writeValueAsString(entityView.getKeys()); @@ -143,6 +149,7 @@ public class EntityViewEntity implements SearchTextEntity { if (customerId != null) { entityView.setCustomerId(new CustomerId(customerId)); } + entityView.setType(type); entityView.setName(name); try { entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java index 60f6951b80..6999c0c910 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java @@ -69,6 +69,9 @@ public class EntityViewEntity extends BaseSqlEntity implements Searc @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) private String customerId; + @Column(name = ModelConstants.DEVICE_TYPE_PROPERTY) + private String type; + @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) private String name; @@ -108,6 +111,7 @@ public class EntityViewEntity extends BaseSqlEntity implements Searc if (entityView.getCustomerId() != null) { this.customerId = toString(entityView.getCustomerId().getId()); } + this.type = entityView.getType(); this.name = entityView.getName(); try { this.keys = mapper.writeValueAsString(entityView.getKeys()); @@ -144,6 +148,7 @@ public class EntityViewEntity extends BaseSqlEntity implements Searc if (customerId != null) { entityView.setCustomerId(new CustomerId(toUUID(customerId))); } + entityView.setType(type); entityView.setName(name); try { entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java index 0f5fdf5fba..efd1bd9cd8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -19,8 +19,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.dao.model.sql.EntityViewEntity; import org.thingsboard.server.dao.util.SqlDao; @@ -36,21 +34,46 @@ public interface EntityViewRepository extends CrudRepository :idOffset ORDER BY e.id") List findByTenantId(@Param("tenantId") String tenantId, - @Param("textSearch") String textSearch, - @Param("idOffset") String idOffset, - Pageable pageable); + @Param("textSearch") String textSearch, + @Param("idOffset") String idOffset, + Pageable pageable); + + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + + "AND e.type = :type " + + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + + "AND e.id > :idOffset ORDER BY e.id") + List findByTenantIdAndType(@Param("tenantId") String tenantId, + @Param("type") String type, + @Param("textSearch") String textSearch, + @Param("idOffset") String idOffset, + Pageable pageable); @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + "AND e.customerId = :customerId " + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + "AND e.id > :idOffset ORDER BY e.id") List findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, - @Param("customerId") String customerId, - @Param("searchText") String searchText, - @Param("idOffset") String idOffset, - Pageable pageable); + @Param("customerId") String customerId, + @Param("searchText") String searchText, + @Param("idOffset") String idOffset, + Pageable pageable); + + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + + "AND e.customerId = :customerId " + + "AND e.type = :type " + + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + + "AND e.id > :idOffset ORDER BY e.id") + List findByTenantIdAndCustomerIdAndType(@Param("tenantId") String tenantId, + @Param("customerId") String customerId, + @Param("type") String type, + @Param("searchText") String searchText, + @Param("idOffset") String idOffset, + Pageable pageable); EntityViewEntity findByTenantIdAndName(String tenantId, String name); List findAllByTenantIdAndEntityId(String tenantId, String entityId); + + @Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId") + List findTenantEntityViewTypes(@Param("tenantId") String tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index 912c9d5eed..1ba9b9e3cf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.UUIDConverter; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.DaoUtil; @@ -41,7 +40,6 @@ import java.util.Optional; import java.util.UUID; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; -import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; /** @@ -75,6 +73,17 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { + return DaoUtil.convertDataList( + entityViewRepository.findByTenantIdAndType( + fromTimeUUID(tenantId), + type, + Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()))); + } + @Override public Optional findEntityViewByTenantIdAndName(UUID tenantId, String name) { return Optional.ofNullable( @@ -95,9 +104,38 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { + return DaoUtil.convertDataList( + entityViewRepository.findByTenantIdAndCustomerIdAndType( + fromTimeUUID(tenantId), + fromTimeUUID(customerId), + type, + Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()) + )); + } + @Override public ListenableFuture> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { return service.submit(() -> DaoUtil.convertDataList( entityViewRepository.findAllByTenantIdAndEntityId(UUIDConverter.fromTimeUUID(tenantId), UUIDConverter.fromTimeUUID(entityId)))); } + + @Override + public ListenableFuture> findTenantEntityViewTypesAsync(UUID tenantId) { + return service.submit(() -> convertTenantEntityViewTypesToDto(tenantId, entityViewRepository.findTenantEntityViewTypes(fromTimeUUID(tenantId)))); + } + + private List convertTenantEntityViewTypesToDto(UUID tenantId, List types) { + List list = Collections.emptyList(); + if (types != null && !types.isEmpty()) { + list = new ArrayList<>(); + for (String type : types) { + list.add(new EntitySubtype(new TenantId(tenantId), EntityType.ENTITY_VIEW, type)); + } + } + return list; + } } diff --git a/dao/src/main/resources/cassandra/schema-entities.cql b/dao/src/main/resources/cassandra/schema-entities.cql index 7ccd41f03a..ef27c70625 100644 --- a/dao/src/main/resources/cassandra/schema-entities.cql +++ b/dao/src/main/resources/cassandra/schema-entities.cql @@ -624,61 +624,90 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( PRIMARY KEY (id) ); -CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( +CREATE TABLE IF NOT EXISTS thingsboard.entity_view ( id timeuuid, entity_id timeuuid, entity_type text, tenant_id timeuuid, customer_id timeuuid, name text, + type text, keys text, start_ts bigint, end_ts bigint, search_text text, additional_info text, - PRIMARY KEY (id, entity_id, tenant_id, customer_id) + PRIMARY KEY (id, entity_id, tenant_id, customer_id, type) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_name AS SELECT * - from thingsboard.entity_views + from thingsboard.entity_view WHERE tenant_id IS NOT NULL AND entity_id IS NOT NULL AND customer_id IS NOT NULL + AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY (tenant_id, name, id, customer_id, entity_id) + PRIMARY KEY (tenant_id, name, id, customer_id, entity_id, type) WITH CLUSTERING ORDER BY (name ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_search_text AS SELECT * - from thingsboard.entity_views + from thingsboard.entity_view WHERE tenant_id IS NOT NULL AND entity_id IS NOT NULL AND customer_id IS NOT NULL + AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id) + PRIMARY KEY (tenant_id, search_text, id, customer_id, entity_id, type) WITH CLUSTERING ORDER BY (search_text ASC, id DESC, customer_id DESC); +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_by_type_and_search_text AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND entity_id IS NOT NULL + AND customer_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, type, search_text, id, customer_id, entity_id) + WITH CLUSTERING ORDER BY (type ASC, search_text ASC, id DESC, customer_id DESC); + CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer AS SELECT * - from thingsboard.entity_views + from thingsboard.entity_view WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND entity_id IS NOT NULL + AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) + PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id, type) WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_customer_and_type AS + SELECT * + from thingsboard.entity_view + WHERE tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND entity_id IS NOT NULL + AND type IS NOT NULL + AND search_text IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, type, customer_id, search_text, id, entity_id) + WITH CLUSTERING ORDER BY (type ASC, customer_id DESC, search_text ASC, id DESC); + CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS SELECT * - from thingsboard.entity_views + from thingsboard.entity_view WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND entity_id IS NOT NULL + AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id) + PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index abd20abd40..0b9c8535fb 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -228,12 +228,13 @@ CREATE TABLE IF NOT EXISTS rule_node ( search_text varchar(255) ); -CREATE TABLE IF NOT EXISTS entity_views ( +CREATE TABLE IF NOT EXISTS entity_view ( id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, entity_id varchar(31), entity_type varchar(255), tenant_id varchar(31), customer_id varchar(31), + type varchar(255), name varchar(255), keys varchar(255), start_ts bigint, diff --git a/dao/src/test/resources/sql/drop-all-tables.sql b/dao/src/test/resources/sql/drop-all-tables.sql index b1fb72cc07..1bdc1a7ece 100644 --- a/dao/src/test/resources/sql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/drop-all-tables.sql @@ -19,4 +19,4 @@ DROP TABLE IF EXISTS widget_type; DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; -DROP TABLE IF EXISTS entity_views; +DROP TABLE IF EXISTS entity_view; diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index ce4dc42dcd..205abc9e4e 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -533,6 +533,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device } ); break; + case types.aliasFilterType.entityViewType.value: + getEntitiesByNameFilter(types.entityType.entityView, filter.entityViewNameFilter, maxItems, {ignoreLoading: true}, filter.entityViewType).then( + function success(entities) { + if (entities && entities.length || !failOnEmpty) { + result.entities = entitiesToEntitiesInfo(entities); + deferred.resolve(result); + } else { + deferred.reject(); + } + }, + function fail() { + deferred.reject(); + } + ); + break; case types.aliasFilterType.relationsQuery.value: result.stateEntity = filter.rootStateEntity; var rootEntityType; @@ -578,6 +593,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device break; case types.aliasFilterType.assetSearchQuery.value: case types.aliasFilterType.deviceSearchQuery.value: + case types.aliasFilterType.entityViewSearchQuery.value: result.stateEntity = filter.rootStateEntity; if (result.stateEntity && stateEntityId) { rootEntityType = stateEntityId.entityType; @@ -604,6 +620,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device } else if (filter.type == types.aliasFilterType.deviceSearchQuery.value) { searchQuery.deviceTypes = filter.deviceTypes; findByQueryPromise = deviceService.findByQuery(searchQuery, false, {ignoreLoading: true}); + } else if (filter.type == types.aliasFilterType.entityViewSearchQuery.value) { + searchQuery.entityViewTypes = filter.entityViewTypes; + findByQueryPromise = entityViewService.findByQuery(searchQuery, false, {ignoreLoading: true}); } findByQueryPromise.then( function success(entities) { @@ -646,6 +665,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; case types.aliasFilterType.deviceType.value: return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; + case types.aliasFilterType.entityViewType.value: + return entityTypes.indexOf(types.entityType.entityView) > -1 ? true : false; case types.aliasFilterType.relationsQuery.value: if (filter.filters && filter.filters.length) { var match = false; @@ -671,6 +692,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return entityTypes.indexOf(types.entityType.asset) > -1 ? true : false; case types.aliasFilterType.deviceSearchQuery.value: return entityTypes.indexOf(types.entityType.device) > -1 ? true : false; + case types.aliasFilterType.entityViewSearchQuery.value: + return entityTypes.indexOf(types.entityType.entityView) > -1 ? true : false; } } return false; @@ -690,12 +713,16 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return entityType === types.entityType.asset; case types.aliasFilterType.deviceType.value: return entityType === types.entityType.device; + case types.aliasFilterType.entityViewType.value: + return entityType === types.entityType.entityView; case types.aliasFilterType.relationsQuery.value: return true; case types.aliasFilterType.assetSearchQuery.value: return entityType === types.entityType.asset; case types.aliasFilterType.deviceSearchQuery.value: return entityType === types.entityType.device; + case types.aliasFilterType.entityViewSearchQuery.value: + return entityType === types.entityType.entityView; } return false; } @@ -1046,6 +1073,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return assetService.deleteAsset(entityId.id); } else if (entityId.entityType == types.entityType.device) { return deviceService.deleteDevice(entityId.id); + } else if (entityId.entityType == types.entityType.entityView) { + return entityViewService.deleteEntityView(entityId.id); } } @@ -1151,6 +1180,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return assetService.saveAsset(entity); } else if (entityType == types.entityType.device) { return deviceService.saveDevice(entity); + } else if (entityType == types.entityType.entityView) { + return entityViewService.saveEntityView(entity); } } @@ -1279,6 +1310,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device searchQuery.assetTypes = entitySubTypes; } else if (entityType == types.entityType.device) { searchQuery.deviceTypes = entitySubTypes; + } else if (entityType == types.entityType.entityView) { + searchQuery.entityViewTypes = entitySubTypes; } else { return null; //Not supported } diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index 15da584968..506ef1d9b6 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -253,6 +253,10 @@ export default angular.module('thingsboard.types', []) value: 'deviceType', name: 'alias.filter-type-device-type' }, + entityViewType: { + value: 'entityViewType', + name: 'alias.filter-type-entity-view-type' + }, relationsQuery: { value: 'relationsQuery', name: 'alias.filter-type-relations-query' @@ -264,6 +268,10 @@ export default angular.module('thingsboard.types', []) deviceSearchQuery: { value: 'deviceSearchQuery', name: 'alias.filter-type-device-search-query' + }, + entityViewSearchQuery: { + value: 'entityViewSearchQuery', + name: 'alias.filter-type-entity-view-search-query' } }, position: { diff --git a/ui/src/app/entity-view/entity-view-fieldset.tpl.html b/ui/src/app/entity-view/entity-view-fieldset.tpl.html index 3894eb9d63..66dc116cc8 100644 --- a/ui/src/app/entity-view/entity-view-fieldset.tpl.html +++ b/ui/src/app/entity-view/entity-view-fieldset.tpl.html @@ -52,12 +52,22 @@
entity-view.name-required
- - + + +
+ + + +
diff --git a/ui/src/app/entity/entity-filter-view.directive.js b/ui/src/app/entity/entity-filter-view.directive.js index 8bd34224a2..5b34107a35 100644 --- a/ui/src/app/entity/entity-filter-view.directive.js +++ b/ui/src/app/entity/entity-filter-view.directive.js @@ -77,6 +77,15 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, scope.filterDisplayValue = $translate.instant('alias.filter-type-device-type-description', {deviceType: deviceType}); } break; + case types.aliasFilterType.entityViewType.value: + var entityViewType = scope.filter.entityViewType; + prefix = scope.filter.entityViewNameFilter; + if (prefix && prefix.length) { + scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-type-and-name-description', {entityViewType: entityViewType, prefix: prefix}); + } else { + scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-type-description', {entityViewType: entityViewType}); + } + break; case types.aliasFilterType.relationsQuery.value: var rootEntityText; var directionText; @@ -134,6 +143,7 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, break; case types.aliasFilterType.assetSearchQuery.value: case types.aliasFilterType.deviceSearchQuery.value: + case types.aliasFilterType.entityViewSearchQuery.value: allEntitiesText = $translate.instant('alias.all-entities'); anyRelationText = $translate.instant('alias.any-relation'); if (scope.filter.rootStateEntity) { @@ -165,7 +175,7 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, scope.filterDisplayValue = $translate.instant('alias.filter-type-asset-search-query-description', translationValues ); - } else { + } else if (scope.filter.type == types.aliasFilterType.deviceSearchQuery.value) { var deviceTypesQuoted = []; scope.filter.deviceTypes.forEach(function(deviceType) { deviceTypesQuoted.push("'"+deviceType+"'"); @@ -175,6 +185,16 @@ export default function EntityFilterViewDirective($compile, $templateCache, $q, scope.filterDisplayValue = $translate.instant('alias.filter-type-device-search-query-description', translationValues ); + } else if (scope.filter.type == types.aliasFilterType.entityViewSearchQuery.value) { + var entityViewTypesQuoted = []; + scope.filter.entityViewTypes.forEach(function(entityViewType) { + entityViewTypesQuoted.push("'"+entityViewType+"'"); + }); + var entityViewTypesText = entityViewTypesQuoted.join(', '); + translationValues.entityViewTypes = entityViewTypesText; + scope.filterDisplayValue = $translate.instant('alias.filter-type-entity-view-search-query-description', + translationValues + ); } break; default: diff --git a/ui/src/app/entity/entity-filter.directive.js b/ui/src/app/entity/entity-filter.directive.js index 0c8f6468f0..38641fbc8b 100644 --- a/ui/src/app/entity/entity-filter.directive.js +++ b/ui/src/app/entity/entity-filter.directive.js @@ -69,9 +69,14 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc filter.deviceType = null; filter.deviceNameFilter = ''; break; + case types.aliasFilterType.entityViewType.value: + filter.entityViewType = null; + filter.entityViewNameFilter = ''; + break; case types.aliasFilterType.relationsQuery.value: case types.aliasFilterType.assetSearchQuery.value: case types.aliasFilterType.deviceSearchQuery.value: + case types.aliasFilterType.entityViewSearchQuery.value: filter.rootStateEntity = false; filter.stateEntityParamName = null; filter.defaultStateEntity = null; @@ -86,6 +91,9 @@ export default function EntityFilterDirective($compile, $templateCache, $q, $doc } else if (filter.type === types.aliasFilterType.deviceSearchQuery.value) { filter.relationType = null; filter.deviceTypes = []; + } else if (filter.type === types.aliasFilterType.entityViewSearchQuery.value) { + filter.relationType = null; + filter.entityViewTypes = []; } break; } diff --git a/ui/src/app/entity/entity-filter.tpl.html b/ui/src/app/entity/entity-filter.tpl.html index f9aac3cb78..95193f2a64 100644 --- a/ui/src/app/entity/entity-filter.tpl.html +++ b/ui/src/app/entity/entity-filter.tpl.html @@ -112,6 +112,20 @@ aria-label="{{ 'device.name-starts-with' | translate }}"> +
+ + + + + + +
@@ -311,4 +325,73 @@ ng-model="filter.deviceTypes">
+
+ +
+ + + +
+
+ + +
+
+ + + + +
+ + + +
+
+
+ + + + + {{ ('relation.search-direction.' + direction) | translate}} + + + + + + + +
+
relation.relation-type
+ + +
entity-view.entity-view-types
+ + +
diff --git a/ui/src/app/entity/entity-subtype-autocomplete.directive.js b/ui/src/app/entity/entity-subtype-autocomplete.directive.js index 5812715ee3..4c79f9a92d 100644 --- a/ui/src/app/entity/entity-subtype-autocomplete.directive.js +++ b/ui/src/app/entity/entity-subtype-autocomplete.directive.js @@ -22,7 +22,7 @@ import entitySubtypeAutocompleteTemplate from './entity-subtype-autocomplete.tpl /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, types) { +export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, $filter, assetService, deviceService, entityViewService, types) { var linker = function (scope, element, attrs, ngModelCtrl) { var template = $templateCache.get(entitySubtypeAutocompleteTemplate); @@ -96,6 +96,8 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); } else if (scope.entityType == types.entityType.device) { entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); + } else if (scope.entityType == types.entityType.entityView) { + entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true}); } if (entitySubtypesPromise) { entitySubtypesPromise.then( @@ -134,6 +136,13 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q, scope.$on('deviceSaved', function() { scope.entitySubtypes = null; }); + } else if (scope.entityType == types.entityType.entityView) { + scope.selectEntitySubtypeText = 'entity-view.select-entity-view-type'; + scope.entitySubtypeText = 'entity-view.entity-view-type'; + scope.entitySubtypeRequiredText = 'entity-view.entity-view-type-required'; + scope.$on('entityViewSaved', function() { + scope.entitySubtypes = null; + }); } } diff --git a/ui/src/app/entity/entity-subtype-list.directive.js b/ui/src/app/entity/entity-subtype-list.directive.js index d74a7b0523..2da948d435 100644 --- a/ui/src/app/entity/entity-subtype-list.directive.js +++ b/ui/src/app/entity/entity-subtype-list.directive.js @@ -22,7 +22,7 @@ import entitySubtypeListTemplate from './entity-subtype-list.tpl.html'; import './entity-subtype-list.scss'; /*@ngInject*/ -export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService) { +export default function EntitySubtypeListDirective($compile, $templateCache, $q, $mdUtil, $translate, $filter, types, assetService, deviceService, entityViewService) { var linker = function (scope, element, attrs, ngModelCtrl) { @@ -97,6 +97,8 @@ export default function EntitySubtypeListDirective($compile, $templateCache, $q, entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); } else if (scope.entityType == types.entityType.device) { entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); + } else if (scope.entityType == types.entityType.entityView) { + entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true}); } if (entitySubtypesPromise) { entitySubtypesPromise.then( diff --git a/ui/src/app/entity/entity-subtype-select.directive.js b/ui/src/app/entity/entity-subtype-select.directive.js index b86e944411..d6163daae6 100644 --- a/ui/src/app/entity/entity-subtype-select.directive.js +++ b/ui/src/app/entity/entity-subtype-select.directive.js @@ -22,7 +22,7 @@ import entitySubtypeSelectTemplate from './entity-subtype-select.tpl.html'; /* eslint-enable import/no-unresolved, import/default */ /*@ngInject*/ -export default function EntitySubtypeSelect($compile, $templateCache, $translate, assetService, deviceService, types) { +export default function EntitySubtypeSelect($compile, $templateCache, $translate, assetService, deviceService, entityViewService, types) { var linker = function (scope, element, attrs, ngModelCtrl) { var template = $templateCache.get(entitySubtypeSelectTemplate); @@ -75,6 +75,8 @@ export default function EntitySubtypeSelect($compile, $templateCache, $translate entitySubtypesPromise = assetService.getAssetTypes({ignoreLoading: true}); } else if (scope.entityType == types.entityType.device) { entitySubtypesPromise = deviceService.getDeviceTypes({ignoreLoading: true}); + } else if (scope.entityType == types.entityType.entityView) { + entitySubtypesPromise = entityViewService.getEntityViewTypes({ignoreLoading: true}); } if (entitySubtypesPromise) { entitySubtypesPromise.then( diff --git a/ui/src/app/help/help-links.constant.js b/ui/src/app/help/help-links.constant.js index 458c118883..9e6bb524d0 100644 --- a/ui/src/app/help/help-links.constant.js +++ b/ui/src/app/help/help-links.constant.js @@ -96,6 +96,7 @@ export default angular.module('thingsboard.help', []) customers: helpBaseUrl + "/docs/user-guide/ui/customers", assets: helpBaseUrl + "/docs/user-guide/ui/assets", devices: helpBaseUrl + "/docs/user-guide/ui/devices", + entityViews: helpBaseUrl + "/docs/user-guide/ui/entity-views", dashboards: helpBaseUrl + "/docs/user-guide/ui/dashboards", users: helpBaseUrl + "/docs/user-guide/ui/users", widgetsBundles: helpBaseUrl + "/docs/user-guide/ui/widget-library#bundles", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 481d677955..6c6ea438af 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -158,12 +158,17 @@ "filter-type-device-type": "Device type", "filter-type-device-type-description": "Devices of type '{{deviceType}}'", "filter-type-device-type-and-name-description": "Devices of type '{{deviceType}}' and with name starting with '{{prefix}}'", + "filter-type-entity-view-type": "Entity View type", + "filter-type-entity-view-type-description": "Entity Views of type '{{entityView}}'", + "filter-type-entity-view-type-and-name-description": "Entity Views of type '{{entityView}}' and with name starting with '{{prefix}}'", "filter-type-relations-query": "Relations query", "filter-type-relations-query-description": "{{entities}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-asset-search-query": "Asset search query", "filter-type-asset-search-query-description": "Assets with types {{assetTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-device-search-query": "Device search query", "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Entity view search query", + "filter-type-entity-view-search-query-description": "Entity views with types {{entityViewTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "entity-filter": "Entity filter", "resolve-multiple": "Resolve as multiple entities", "filter-type": "Filter type", @@ -839,7 +844,8 @@ "client-attributes": "Client attributes", "shared-attributes": "Shared attributes", "server-attributes": "Server attributes", - "latest-timeseries": "Latest timeseries" + "latest-timeseries": "Latest timeseries", + "related-entity": "Related entity" }, "event": { "event-type": "Event type",