Merge pull request #1132 from thingsboard/feature/entity-view-type
WIP: Feature/entity view type
This commit is contained in:
		
						commit
						17cb573ed3
					
				
							
								
								
									
										110
									
								
								application/src/main/data/upgrade/2.1.2/schema_update.cql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								application/src/main/data/upgrade/2.1.2/schema_update.cql
									
									
									
									
									
										Normal file
									
								
							@ -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);
 | 
			
		||||
							
								
								
									
										32
									
								
								application/src/main/data/upgrade/2.1.2/schema_update.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								application/src/main/data/upgrade/2.1.2/schema_update.sql
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
);
 | 
			
		||||
@ -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<EntityView> 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<EntityView> 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<EntityView> findByQuery(@RequestBody EntityViewSearchQuery query) throws ThingsboardException {
 | 
			
		||||
        checkNotNull(query);
 | 
			
		||||
        checkNotNull(query.getParameters());
 | 
			
		||||
        checkNotNull(query.getEntityViewTypes());
 | 
			
		||||
        checkEntityId(query.getParameters().getEntityId());
 | 
			
		||||
        try {
 | 
			
		||||
            List<EntityView> 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<EntitySubtype> getEntityViewTypes() throws ThingsboardException {
 | 
			
		||||
        try {
 | 
			
		||||
            SecurityUser user = getCurrentUser();
 | 
			
		||||
            TenantId tenantId = user.getTenantId();
 | 
			
		||||
            ListenableFuture<List<EntitySubtype>> entityViewTypes = entityViewService.findEntityViewTypesByTenantId(tenantId);
 | 
			
		||||
            return checkNotNull(entityViewTypes.get());
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw handleException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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");
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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()) {
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -40,6 +40,7 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    private CustomerId customerId;
 | 
			
		||||
    private String name;
 | 
			
		||||
    private String type;
 | 
			
		||||
    private TelemetryEntityView keys;
 | 
			
		||||
    private long startTimeMs;
 | 
			
		||||
    private long endTimeMs;
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ public class EntityViewSearchQuery {
 | 
			
		||||
 | 
			
		||||
    private RelationsSearchParameters parameters;
 | 
			
		||||
    private String relationType;
 | 
			
		||||
    private List<String> entityViewTypes;
 | 
			
		||||
 | 
			
		||||
    public EntityRelationsQuery toEntitySearchQuery() {
 | 
			
		||||
        EntityRelationsQuery query = new EntityRelationsQuery();
 | 
			
		||||
 | 
			
		||||
@ -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<Entit
 | 
			
		||||
    public List<EntityView> findEntityViewsByTenantId(UUID tenantId, TextPageLink pageLink) {
 | 
			
		||||
        log.debug("Try to find entity views by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
 | 
			
		||||
        List<EntityViewEntity> entityViewEntities =
 | 
			
		||||
                findPageWithTextSearch(ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
 | 
			
		||||
                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<EntityView> findEntityViewsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
 | 
			
		||||
        log.debug("Try to find entity views by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
 | 
			
		||||
        List<EntityViewEntity> 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<EntityView> 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<Entit
 | 
			
		||||
        return DaoUtil.convertDataList(entityViewEntities);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<EntityView> 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<EntityViewEntity> 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<List<EntityView>> 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<Entit
 | 
			
		||||
        query.and(eq(ENTITY_ID_COLUMN, entityId));
 | 
			
		||||
        return findListByStatementAsync(query);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ListenableFuture<List<EntitySubtype>> 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<ResultSet, List<EntitySubtype>>() {
 | 
			
		||||
            @Nullable
 | 
			
		||||
            @Override
 | 
			
		||||
            public List<EntitySubtype> apply(@Nullable ResultSet resultSet) {
 | 
			
		||||
                Result<EntitySubtypeEntity> result = cluster.getMapper(EntitySubtypeEntity.class).map(resultSet);
 | 
			
		||||
                if (result != null) {
 | 
			
		||||
                    List<EntitySubtype> entitySubtypes = new ArrayList<>();
 | 
			
		||||
                    result.all().forEach((entitySubtypeEntity) ->
 | 
			
		||||
                            entitySubtypes.add(entitySubtypeEntity.toEntitySubtype())
 | 
			
		||||
                    );
 | 
			
		||||
                    return entitySubtypes;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return Collections.emptyList();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<EntityView> {
 | 
			
		||||
     */
 | 
			
		||||
    List<EntityView> 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<EntityView> 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<EntityView> {
 | 
			
		||||
                                                            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<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(UUID tenantId,
 | 
			
		||||
                                                                   UUID customerId,
 | 
			
		||||
                                                                   String type,
 | 
			
		||||
                                                                   TextPageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find tenants entity view types.
 | 
			
		||||
     *
 | 
			
		||||
     * @return the list of tenant entity view type objects
 | 
			
		||||
     */
 | 
			
		||||
    ListenableFuture<List<EntitySubtype>> findTenantEntityViewTypesAsync(UUID tenantId);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type);
 | 
			
		||||
 | 
			
		||||
    TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
 | 
			
		||||
 | 
			
		||||
    TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, TextPageLink pageLink, String type);
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query);
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId);
 | 
			
		||||
@ -55,4 +60,6 @@ public interface EntityViewService {
 | 
			
		||||
    void deleteEntityView(EntityViewId entityViewId);
 | 
			
		||||
 | 
			
		||||
    void deleteEntityViewsByTenantId(TenantId tenantId);
 | 
			
		||||
 | 
			
		||||
    ListenableFuture<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<EntityView> 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<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndType(tenantId.getId(), type, pageLink);
 | 
			
		||||
        return new TextPageData<>(entityViews, pageLink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TextPageData<EntityView> 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<EntityView> 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<EntityView> entityViews = entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId.getId(),
 | 
			
		||||
                customerId.getId(), type, pageLink);
 | 
			
		||||
        return new TextPageData<>(entityViews, pageLink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) {
 | 
			
		||||
        ListenableFuture<List<EntityRelation>> 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<EntityView>, List<EntityView>>() {
 | 
			
		||||
            @Nullable
 | 
			
		||||
            @Override
 | 
			
		||||
            public List<EntityView> apply(@Nullable List<EntityView> 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<EntityView> 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<List<EntitySubtype>> findEntityViewTypesByTenantId(TenantId tenantId) {
 | 
			
		||||
        log.trace("Executing findEntityViewTypesByTenantId, tenantId [{}]", tenantId);
 | 
			
		||||
        validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
 | 
			
		||||
        ListenableFuture<List<EntitySubtype>> tenantEntityViewTypes = entityViewDao.findTenantEntityViewTypesAsync(tenantId.getId());
 | 
			
		||||
        return Futures.transform(tenantEntityViewTypes,
 | 
			
		||||
                entityViewTypes -> {
 | 
			
		||||
                    entityViewTypes.sort(Comparator.comparing(EntitySubtype::getType));
 | 
			
		||||
                    return entityViewTypes;
 | 
			
		||||
                });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ListenableFuture<List<Void>> copyAttributesFromEntityToEntityView(EntityView entityView, String scope, Collection<String> keys) {
 | 
			
		||||
        if (keys != null && !keys.isEmpty()) {
 | 
			
		||||
            ListenableFuture<List<AttributeKvEntry>> 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!");
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
@ -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";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -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<EntityView> {
 | 
			
		||||
    @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<EntityView> {
 | 
			
		||||
        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<EntityView> {
 | 
			
		||||
        if (customerId != null) {
 | 
			
		||||
            entityView.setCustomerId(new CustomerId(customerId));
 | 
			
		||||
        }
 | 
			
		||||
        entityView.setType(type);
 | 
			
		||||
        entityView.setName(name);
 | 
			
		||||
        try {
 | 
			
		||||
            entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class));
 | 
			
		||||
 | 
			
		||||
@ -69,6 +69,9 @@ public class EntityViewEntity extends BaseSqlEntity<EntityView> 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<EntityView> 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<EntityView> 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));
 | 
			
		||||
 | 
			
		||||
@ -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<EntityViewEntity, S
 | 
			
		||||
            "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " +
 | 
			
		||||
            "AND e.id > :idOffset ORDER BY e.id")
 | 
			
		||||
    List<EntityViewEntity> 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<EntityViewEntity> 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<EntityViewEntity> 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<EntityViewEntity> 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<EntityViewEntity> findAllByTenantIdAndEntityId(String tenantId, String entityId);
 | 
			
		||||
 | 
			
		||||
    @Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId")
 | 
			
		||||
    List<String> findTenantEntityViewTypes(@Param("tenantId") String tenantId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<EntityViewEntity,
 | 
			
		||||
                        new PageRequest(0, pageLink.getLimit())));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<EntityView> 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<EntityView> findEntityViewByTenantIdAndName(UUID tenantId, String name) {
 | 
			
		||||
        return Optional.ofNullable(
 | 
			
		||||
@ -95,9 +104,38 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity,
 | 
			
		||||
                ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<EntityView> 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<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) {
 | 
			
		||||
        return service.submit(() -> DaoUtil.convertDataList(
 | 
			
		||||
                entityViewRepository.findAllByTenantIdAndEntityId(UUIDConverter.fromTimeUUID(tenantId), UUIDConverter.fromTimeUUID(entityId))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ListenableFuture<List<EntitySubtype>> findTenantEntityViewTypesAsync(UUID tenantId) {
 | 
			
		||||
        return service.submit(() -> convertTenantEntityViewTypesToDto(tenantId, entityViewRepository.findTenantEntityViewTypes(fromTimeUUID(tenantId))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private List<EntitySubtype> convertTenantEntityViewTypesToDto(UUID tenantId, List<String> types) {
 | 
			
		||||
        List<EntitySubtype> 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
@ -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,
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -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: {
 | 
			
		||||
 | 
			
		||||
@ -52,12 +52,22 @@
 | 
			
		||||
	      		<div translate ng-message="required">entity-view.name-required</div>
 | 
			
		||||
	    	</div>				
 | 
			
		||||
		</md-input-container>
 | 
			
		||||
        <tb-entity-select flex ng-disabled="!isEdit"
 | 
			
		||||
                          the-form="theForm"
 | 
			
		||||
                          tb-required="true"
 | 
			
		||||
                          allowed-entity-types="allowedEntityTypes"
 | 
			
		||||
                          ng-model="entityView.entityId">
 | 
			
		||||
        </tb-entity-select>
 | 
			
		||||
        <tb-entity-subtype-autocomplete
 | 
			
		||||
                ng-disabled="$root.loading || !isEdit"
 | 
			
		||||
                tb-required="true"
 | 
			
		||||
                the-form="theForm"
 | 
			
		||||
                ng-model="entityView.type"
 | 
			
		||||
                entity-type="types.entityType.entityView">
 | 
			
		||||
        </tb-entity-subtype-autocomplete>
 | 
			
		||||
        <section layout="column">
 | 
			
		||||
            <label translate class="tb-title no-padding">entity-view.related-entity</label>
 | 
			
		||||
            <tb-entity-select flex ng-disabled="!isEdit"
 | 
			
		||||
                              the-form="theForm"
 | 
			
		||||
                              tb-required="true"
 | 
			
		||||
                              allowed-entity-types="allowedEntityTypes"
 | 
			
		||||
                              ng-model="entityView.entityId">
 | 
			
		||||
            </tb-entity-select>
 | 
			
		||||
        </section>
 | 
			
		||||
        <md-input-container class="md-block">
 | 
			
		||||
            <label translate>entity-view.description</label>
 | 
			
		||||
            <textarea ng-model="entityView.additionalInfo.description" rows="2"></textarea>
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -112,6 +112,20 @@
 | 
			
		||||
                   aria-label="{{ 'device.name-starts-with' | translate }}">
 | 
			
		||||
        </md-input-container>
 | 
			
		||||
    </section>
 | 
			
		||||
    <section layout="column" ng-if="filter.type == types.aliasFilterType.entityViewType.value" id="entityViewTypeFilter">
 | 
			
		||||
        <tb-entity-subtype-autocomplete
 | 
			
		||||
                tb-required="true"
 | 
			
		||||
                the-form="theForm"
 | 
			
		||||
                ng-model="filter.entityViewType"
 | 
			
		||||
                entity-type="types.entityType.entityView">
 | 
			
		||||
        </tb-entity-subtype-autocomplete>
 | 
			
		||||
        <md-input-container class="md-block">
 | 
			
		||||
            <label translate>entity-view.name-starts-with</label>
 | 
			
		||||
            <input name="entityViewNameFilter"
 | 
			
		||||
                   ng-model="filter.entityViewNameFilter"
 | 
			
		||||
                   aria-label="{{ 'entity-view.name-starts-with' | translate }}">
 | 
			
		||||
        </md-input-container>
 | 
			
		||||
    </section>
 | 
			
		||||
    <section layout="column" ng-if="filter.type == types.aliasFilterType.relationsQuery.value" id="relationsQueryFilter">
 | 
			
		||||
        <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
 | 
			
		||||
        <section class="tb-root-state-entity-switch" layout="row" layout-align="start center" style="padding-left: 0px;">
 | 
			
		||||
@ -311,4 +325,73 @@
 | 
			
		||||
                ng-model="filter.deviceTypes">
 | 
			
		||||
        </tb-entity-subtype-list>
 | 
			
		||||
    </section>
 | 
			
		||||
    <section layout="column" ng-if="filter.type == types.aliasFilterType.entityViewSearchQuery.value" id="entityViewSearchQueryFilter">
 | 
			
		||||
        <label class="tb-small">{{ 'alias.root-entity' | translate }}</label>
 | 
			
		||||
        <section class="tb-root-state-entity-switch" layout="row" layout-align="start center" style="padding-left: 0px;">
 | 
			
		||||
            <md-switch class="root-state-entity-switch" ng-model="filter.rootStateEntity"
 | 
			
		||||
                       aria-label="{{ 'alias.root-state-entity' | translate }}">
 | 
			
		||||
            </md-switch>
 | 
			
		||||
            <label class="tb-small root-state-entity-label" translate>alias.root-state-entity</label>
 | 
			
		||||
        </section>
 | 
			
		||||
        <div flex layout="row" ng-if="!filter.rootStateEntity">
 | 
			
		||||
            <tb-entity-select flex
 | 
			
		||||
                              the-form="theForm"
 | 
			
		||||
                              tb-required="!filter.rootStateEntity"
 | 
			
		||||
                              ng-disabled="filter.rootStateEntity"
 | 
			
		||||
                              use-alias-entity-types="true"
 | 
			
		||||
                              ng-model="filter.rootEntity">
 | 
			
		||||
            </tb-entity-select>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div flex layout="row" ng-if="filter.rootStateEntity">
 | 
			
		||||
            <md-input-container class="md-block" style="margin-top: 32px;">
 | 
			
		||||
                <label translate>alias.state-entity-parameter-name</label>
 | 
			
		||||
                <input name="stateEntityParamName"
 | 
			
		||||
                       placeholder="{{ 'alias.default-entity-parameter-name' | translate }}"
 | 
			
		||||
                       ng-model="filter.stateEntityParamName"
 | 
			
		||||
                       aria-label="{{ 'alias.state-entity-parameter-name' | translate }}">
 | 
			
		||||
            </md-input-container>
 | 
			
		||||
            <div flex layout="column">
 | 
			
		||||
                <label class="tb-small">{{ 'alias.default-state-entity' | translate }}</label>
 | 
			
		||||
                <tb-entity-select flex
 | 
			
		||||
                                  the-form="theForm"
 | 
			
		||||
                                  tb-required="false"
 | 
			
		||||
                                  use-alias-entity-types="true"
 | 
			
		||||
                                  ng-model="filter.defaultStateEntity">
 | 
			
		||||
                </tb-entity-select>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div flex layout="row">
 | 
			
		||||
            <md-input-container class="md-block" style="min-width: 100px;">
 | 
			
		||||
                <label translate>relation.direction</label>
 | 
			
		||||
                <md-select required ng-model="filter.direction">
 | 
			
		||||
                    <md-option ng-repeat="direction in types.entitySearchDirection" ng-value="direction">
 | 
			
		||||
                        {{ ('relation.search-direction.' + direction) | translate}}
 | 
			
		||||
                    </md-option>
 | 
			
		||||
                </md-select>
 | 
			
		||||
            </md-input-container>
 | 
			
		||||
            <md-input-container flex class="md-block">
 | 
			
		||||
                <label translate>alias.max-relation-level</label>
 | 
			
		||||
                <input name="maxRelationLevel"
 | 
			
		||||
                       type="number"
 | 
			
		||||
                       min="1"
 | 
			
		||||
                       step="1"
 | 
			
		||||
                       placeholder="{{ 'alias.unlimited-level' | translate }}"
 | 
			
		||||
                       ng-model="filter.maxLevel"
 | 
			
		||||
                       aria-label="{{ 'alias.max-relation-level' | translate }}">
 | 
			
		||||
            </md-input-container>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="md-caption" style="color: rgba(0,0,0,0.57);" translate>relation.relation-type</div>
 | 
			
		||||
        <tb-relation-type-autocomplete flex
 | 
			
		||||
                                       hide-label
 | 
			
		||||
                                       the-form="theForm"
 | 
			
		||||
                                       ng-model="filter.relationType"
 | 
			
		||||
                                       tb-required="false">
 | 
			
		||||
        </tb-relation-type-autocomplete>
 | 
			
		||||
        <div class="md-caption tb-required" style="color: rgba(0,0,0,0.57);" translate>entity-view.entity-view-types</div>
 | 
			
		||||
        <tb-entity-subtype-list
 | 
			
		||||
                tb-required="true"
 | 
			
		||||
                entity-type="types.entityType.entityView"
 | 
			
		||||
                ng-model="filter.entityViewTypes">
 | 
			
		||||
        </tb-entity-subtype-list>
 | 
			
		||||
    </section>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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(
 | 
			
		||||
 | 
			
		||||
@ -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(
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user