thingsboard-memoizeit

Fixes for entity view

9/10/2018 5:11:07 AM

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
index fd3ccf2..227bed0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -19,17 +19,21 @@ 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.*;
+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;
+import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
 import org.thingsboard.server.common.data.exception.ThingsboardException;
-import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.EntityViewId;
 import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.TextPageData;
+import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.service.security.model.SecurityUser;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Created by Victor Basanets on 8/28/2017.
@@ -41,7 +45,7 @@ public class EntityViewController extends BaseController {
     public static final String ENTITY_VIEW_ID = "entityViewId";
 
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
-    @RequestMapping(value = "/entity-view/{entityViewId}", method = RequestMethod.GET)
+    @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET)
     @ResponseBody
     public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId)
             throws ThingsboardException {
@@ -56,7 +60,7 @@ public class EntityViewController extends BaseController {
     }
 
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
-    @RequestMapping(value = "/entity-view", method = RequestMethod.POST)
+    @RequestMapping(value = "/entityView", method = RequestMethod.POST)
     @ResponseBody
     public EntityView saveEntityView(@RequestBody EntityView entityView) throws ThingsboardException {
         try {
@@ -76,7 +80,7 @@ public class EntityViewController extends BaseController {
     }
 
     @PreAuthorize("hasAuthority('TENANT_ADMIN')")
-    @RequestMapping(value = "/entity-view/{entityViewId}", method = RequestMethod.DELETE)
+    @RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE)
     @ResponseStatus(value = HttpStatus.OK)
     public void deleteEntityView(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
         checkParameter(ENTITY_VIEW_ID, strEntityViewId);
@@ -95,4 +99,44 @@ public class EntityViewController extends BaseController {
             throw handleException(e);
         }
     }
+
+    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+    @RequestMapping(value = "/tenant/entityViews", params = {"limit"}, method = RequestMethod.GET)
+    @ResponseBody
+    public TextPageData<EntityView> getTenantEntityViews(
+            @RequestParam int limit,
+            @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));
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
+
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
+    @RequestMapping(value = "/entityViews", method = RequestMethod.POST)
+    @ResponseBody
+    public List<EntityView> findByQuery(@RequestBody EntityViewSearchQuery query) throws ThingsboardException {
+        checkNotNull(query);
+        checkNotNull(query.getParameters());
+        checkEntityId(query.getParameters().getEntityId());
+        try {
+            List<EntityView> entityViews = checkNotNull(entityViewService.findEntityViewsByQuery(query).get());
+            entityViews = entityViews.stream().filter(entityView -> {
+                try {
+                    checkEntityView(entityView);
+                    return true;
+                } catch (ThingsboardException e) {
+                    return false;
+                }
+            }).collect(Collectors.toList());
+            return entityViews;
+        } catch (Exception e) {
+            throw handleException(e);
+        }
+    }
 }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java
new file mode 100644
index 0000000..752eafc
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/entityview/EntityViewSearchQuery.java
@@ -0,0 +1,42 @@
+/**
+ * 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.
+ */
+package org.thingsboard.server.common.data.entityview;
+
+import lombok.Data;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
+import org.thingsboard.server.common.data.relation.EntityTypeFilter;
+import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
+
+import java.util.Collections;
+import java.util.List;
+
+@Data
+public class EntityViewSearchQuery {
+
+    private RelationsSearchParameters parameters;
+    private String relationType;
+
+    public EntityRelationsQuery toEntitySearchQuery() {
+        EntityRelationsQuery query = new EntityRelationsQuery();
+        query.setParameters(parameters);
+        query.setFilters(
+                Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType,
+                        Collections.singletonList(EntityType.ENTITY_VIEW))));
+        return query;
+    }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
index 5cbbef8..56a688a 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
@@ -16,7 +16,6 @@
 package org.thingsboard.server.dao.entityview;
 
 import org.thingsboard.server.common.data.EntityView;
-import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.Dao;
 
@@ -92,5 +91,4 @@ public interface EntityViewDao extends Dao<EntityView> {
                                                                        UUID customerId,
                                                                        UUID entityId,
                                                                        TextPageLink pageLink);
-
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
index 6de86be..a60376e 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
@@ -16,12 +16,18 @@
 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.EntityType;
 import org.thingsboard.server.common.data.EntityView;
+import org.thingsboard.server.common.data.device.DeviceSearchQuery;
+import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
 import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.page.TextPageData;
 import org.thingsboard.server.common.data.page.TextPageLink;
 
+import java.util.List;
+
 /**
  * Created by Victor Basanets on 8/27/2017.
  */
@@ -57,4 +63,6 @@ public interface EntityViewService {
     void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId);
 
     ListenableFuture<EntityView> findEntityViewByIdAsync(EntityViewId entityViewId);
+
+    ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
index 31d3b30..2551183 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
@@ -15,6 +15,8 @@
  */
 package org.thingsboard.server.dao.entityview;
 
+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.apache.commons.lang3.StringUtils;
@@ -25,14 +27,22 @@ import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import org.thingsboard.server.common.data.Customer;
+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;
+import org.thingsboard.server.common.data.device.DeviceSearchQuery;
+import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
 import org.thingsboard.server.common.data.id.CustomerId;
+import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.EntityViewId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageData;
 import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.relation.EntityRelation;
+import org.thingsboard.server.common.data.relation.EntitySearchDirection;
 import org.thingsboard.server.dao.customer.CustomerDao;
 import org.thingsboard.server.dao.entity.AbstractEntityService;
 import org.thingsboard.server.dao.exception.DataValidationException;
@@ -40,8 +50,12 @@ import org.thingsboard.server.dao.service.DataValidator;
 import org.thingsboard.server.dao.service.PaginatedRemover;
 import org.thingsboard.server.dao.tenant.TenantDao;
 
+import javax.annotation.Nullable;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CACHE;
 import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE;
@@ -55,8 +69,7 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
  */
 @Service
 @Slf4j
-public class EntityViewServiceImpl extends AbstractEntityService
-        implements EntityViewService {
+public class EntityViewServiceImpl extends AbstractEntityService implements EntityViewService {
 
     public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
     public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
@@ -75,7 +88,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
     @Autowired
     private CacheManager cacheManager;
 
-    @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
+//    @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
     @Override
     public EntityView findEntityViewById(EntityViewId entityViewId) {
         log.trace("Executing findEntityViewById [{}]", entityViewId);
@@ -91,7 +104,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
                 .orElse(null);
     }
 
-    @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")
+//    @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")
     @Override
     public EntityView saveEntityView(EntityView entityView) {
         log.trace("Executing save entity view [{}]", entityView);
@@ -136,7 +149,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
         return new TextPageData<>(entityViews, pageLink);
     }
 
-    @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #entityId, #pageLink}")
+//    @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #entityId, #pageLink}")
     @Override
     public TextPageData<EntityView> findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId,
                                                                     TextPageLink pageLink) {
@@ -176,7 +189,7 @@ public class EntityViewServiceImpl extends AbstractEntityService
         return new TextPageData<>(entityViews, pageLink);
     }
 
-    @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #customerId, #entityId, #pageLink}")
+//    @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #customerId, #entityId, #pageLink}")
     @Override
     public TextPageData<EntityView> findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId,
                                                                                       CustomerId customerId,
@@ -211,6 +224,24 @@ public class EntityViewServiceImpl extends AbstractEntityService
         return entityViewDao.findByIdAsync(entityViewId.getId());
     }
 
+    @Override
+    public ListenableFuture<List<EntityView>> findEntityViewsByQuery(EntityViewSearchQuery query) {
+        ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(query.toEntitySearchQuery());
+        ListenableFuture<List<EntityView>> entityViews = Futures.transformAsync(relations, r -> {
+            EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
+            List<ListenableFuture<EntityView>> futures = new ArrayList<>();
+            for (EntityRelation relation : r) {
+                EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
+                if (entityId.getEntityType() == EntityType.ENTITY_VIEW) {
+                    futures.add(findEntityViewByIdAsync(new EntityViewId(entityId.getId())));
+                }
+            }
+            return Futures.successfulAsList(futures);
+        });
+
+        return entityViews;
+    }
+
     private DataValidator<EntityView> entityViewValidator =
             new DataValidator<EntityView>() {
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index 6932bea..a96f258 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
@@ -151,6 +151,7 @@ public class ModelConstants {
     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_TYPE_PROPERTY = DEVICE_TYPE_PROPERTY;
     public static final String ENTITY_VIEW_TENANT_AND_NAME_VIEW_NAME = "entity_view_by_tenant_and_name";
     public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys";
     public static final String ENTITY_VIEW_TS_BEGIN_PROPERTY = "ts_begin";
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
index 990ff70..3f75713 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java
@@ -16,6 +16,7 @@
 package org.thingsboard.server.dao.model.nosql;
 
 import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.Column;
 import com.datastax.driver.mapping.annotations.PartitionKey;
 import com.datastax.driver.mapping.annotations.Table;
 import com.fasterxml.jackson.databind.JsonNode;
@@ -31,10 +32,8 @@ import org.thingsboard.server.common.data.objects.TelemetryEntityView;
 import org.thingsboard.server.dao.model.ModelConstants;
 import org.thingsboard.server.dao.model.SearchTextEntity;
 
-import javax.persistence.Column;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
-
 import java.io.IOException;
 import java.util.UUID;
 
@@ -55,22 +54,21 @@ public class EntityViewEntity implements SearchTextEntity<EntityView> {
     @Column(name = ID_PROPERTY)
     private UUID id;
 
-    @PartitionKey(value = 1)
-    @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY)
-    private UUID entityId;
-
     @Enumerated(EnumType.STRING)
     @Column(name = ENTITY_TYPE_PROPERTY)
     private EntityType entityType;
 
-    @PartitionKey(value = 2)
+    @PartitionKey(value = 1)
     @Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY)
     private UUID tenantId;
 
-    @PartitionKey(value = 3)
+    @PartitionKey(value = 2)
     @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY)
     private UUID customerId;
 
+    @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY)
+    private UUID entityId;
+
     @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY)
     private String name;
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
index 57b0afa..6ef0acb 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java
@@ -15,12 +15,16 @@
  */
 package org.thingsboard.server.dao.sql.entityview;
 
+import com.google.common.util.concurrent.ListenableFuture;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.stereotype.Component;
+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.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.DaoUtil;
 import org.thingsboard.server.dao.entityview.EntityViewDao;
@@ -28,6 +32,8 @@ import org.thingsboard.server.dao.model.sql.EntityViewEntity;
 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
 import org.thingsboard.server.dao.util.SqlDao;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql
index 68f196f..89df266 100644
--- a/dao/src/main/resources/cassandra/schema.cql
+++ b/dao/src/main/resources/cassandra/schema.cql
@@ -651,7 +651,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
     ts_end bigint,
     search_text text,
     additional_info text,
-    PRIMARY KEY (id, entity_id, tenant_id, customer_id)
+    PRIMARY KEY (id, tenant_id, customer_id)
 );
 
 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS
diff --git a/ui/src/app/api/entity-view.service.js b/ui/src/app/api/entity-view.service.js
index 9bd8f8d..f2d1908 100644
--- a/ui/src/app/api/entity-view.service.js
+++ b/ui/src/app/api/entity-view.service.js
@@ -155,6 +155,10 @@ function EntityViewService($http, $q, $window, userService, attributeService, cu
     function saveEntityView(entityView) {
         var deferred = $q.defer();
         var url = '/api/entityView';
+
+        entityView.keys = {};
+        entityView.keys.timeseries = ['a', 'b'];
+
         $http.post(url, entityView).then(function success(response) {
             deferred.resolve(response.data);
         }, function fail() {
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 1d1b717..060b557 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -328,7 +328,7 @@ export default angular.module('thingsboard.types', [])
                 alarm: "ALARM",
                 rulechain: "RULE_CHAIN",
                 rulenode: "RULE_NODE",
-                entityview: "ENTITY_VIEW"
+                entityView: "ENTITY_VIEW"
             },
             aliasEntityType: {
                 current_customer: "CURRENT_CUSTOMER"
diff --git a/ui/src/app/entity-view/entity-view.controller.js b/ui/src/app/entity-view/entity-view.controller.js
index fd3b5a8..2c8c8ea 100644
--- a/ui/src/app/entity-view/entity-view.controller.js
+++ b/ui/src/app/entity-view/entity-view.controller.js
@@ -194,7 +194,7 @@ export function EntityViewController($rootScope, userService, entityViewService,
             entityViewGroupActionsList.push(
                 {
                     onAction: function ($event, items) {
-                        assignEntiyViewsToCustomer($event, items);
+                        assignEntityViewsToCustomer($event, items);
                     },
                     name: function() { return $translate.instant('entity-view.assign-entity-views') },
                     details: function(selectedCount) {
@@ -221,10 +221,10 @@ export function EntityViewController($rootScope, userService, entityViewService,
             fetchEntityViewsFunction = function (pageLink, entityViewType) {
                 return entityViewService.getCustomerEntityViews(customerId, pageLink, true, null, entityViewType);
             };
-            deleteentityViewFunction = function (entityViewId) {
+            deleteEntityViewFunction = function (entityViewId) {
                 return entityViewService.unassignEntityViewFromCustomer(entityViewId);
             };
-            refreshentityViewsParamsFunction = function () {
+            refreshEntityViewsParamsFunction = function () {
                 return {"customerId": customerId, "topIndex": vm.topIndex};
             };
 
@@ -271,9 +271,9 @@ export function EntityViewController($rootScope, userService, entityViewService,
             }
         }
 
-        vm.entityViewGridConfig.refreshParamsFunc = refreshentityViewsParamsFunction;
-        vm.entityViewGridConfig.fetchItemsFunc = fetchentityViewsFunction;
-        vm.entityViewGridConfig.deleteItemFunc = deleteentityViewFunction;
+        vm.entityViewGridConfig.refreshParamsFunc = refreshEntityViewsParamsFunction;
+        vm.entityViewGridConfig.fetchItemsFunc = fetchEntityViewsFunction;
+        vm.entityViewGridConfig.deleteItemFunc = deleteEntityViewFunction;
 
     }
 
diff --git a/ui/src/app/entity-view/entity-view.directive.js b/ui/src/app/entity-view/entity-view.directive.js
index f980270..5a3d876 100644
--- a/ui/src/app/entity-view/entity-view.directive.js
+++ b/ui/src/app/entity-view/entity-view.directive.js
@@ -27,6 +27,7 @@ export default function EntityViewDirective($compile, $templateCache, toast, $tr
 
         scope.types = types;
         scope.isAssignedToCustomer = false;
+        scope.isPublic = false;
         scope.assignedCustomer = null;
 
         scope.$watch('entityView', function(newVal) {
@@ -36,10 +37,12 @@ export default function EntityViewDirective($compile, $templateCache, toast, $tr
                     customerService.getShortCustomerInfo(scope.entityView.customerId.id).then(
                         function success(customer) {
                             scope.assignedCustomer = customer;
+                            scope.isPublic = customer.isPublic;
                         }
                     );
                 } else {
                     scope.isAssignedToCustomer = false;
+                    scope.isPublic = false;
                     scope.assignedCustomer = null;
                 }
             }
@@ -60,6 +63,7 @@ export default function EntityViewDirective($compile, $templateCache, toast, $tr
             entityViewScope: '=',
             theForm: '=',
             onAssignToCustomer: '&',
+            onMakePublic: '&',
             onUnassignFromCustomer: '&',
             onDeleteEntityView: '&'
         }
diff --git a/ui/src/app/entity-view/entity-view.routes.js b/ui/src/app/entity-view/entity-view.routes.js
index 14f5079..481dfc8 100644
--- a/ui/src/app/entity-view/entity-view.routes.js
+++ b/ui/src/app/entity-view/entity-view.routes.js
@@ -35,14 +35,14 @@ export default function EntityViewRoutes($stateProvider, types) {
                 }
             },
             data: {
-                entityViewsTypes: 'tenant',
+                entityViewsType: 'tenant',
                 searchEnabled: true,
                 searchByEntitySubtype: true,
                 searchEntityType: types.entityType.entityview,
-                pageTitle: 'entity-views.entity-views'
+                pageTitle: 'entity-view.entity-views'
             },
             ncyBreadcrumb: {
-                label: '{"icon": "devices_other", "label": "entity-view.entity-views"}'
+                label: '{"icon": "view_stream", "label": "entity-view.entity-views"}'
             }
         })
         .state('home.customers.entityViews', {
@@ -58,14 +58,14 @@ export default function EntityViewRoutes($stateProvider, types) {
                 }
             },
             data: {
-                entityViewsTypes: 'customer',
+                entityViewsType: 'customer',
                 searchEnabled: true,
                 searchByEntitySubtype: true,
                 searchEntityType: types.entityType.entityview,
                 pageTitle: 'customer.entity-views'
             },
             ncyBreadcrumb: {
-                label: '{"icon": "devices_other", "label": "{{ vm.customerEntityViewsTitle }}", "translate": "false"}'
+                label: '{"icon": "view_stream", "label": "{{ vm.customerEntityViewsTitle }}", "translate": "false"}'
             }
         });
 
diff --git a/ui/src/app/entity-view/entity-view-fieldset.tpl.html b/ui/src/app/entity-view/entity-view-fieldset.tpl.html
index e0000c6..cb46f3d 100644
--- a/ui/src/app/entity-view/entity-view-fieldset.tpl.html
+++ b/ui/src/app/entity-view/entity-view-fieldset.tpl.html
@@ -15,6 +15,9 @@
     limitations under the License.
 
 -->
+<md-button ng-click="onMakePublic({event: $event})"
+           ng-show="!isEdit && entityViewScope === 'tenant' && !isAssignedToCustomer && !isPublic"
+           class="md-raised md-primary">{{ 'entity-view.make-public' | translate }}</md-button>
 <md-button ng-click="onAssignToCustomer({event: $event})"
            ng-show="!isEdit && entityViewScope === 'tenant' && !isAssignedToCustomer"
            class="md-raised md-primary">{{ 'entity-view.assign-to-customer' | translate }}</md-button>
@@ -49,13 +52,6 @@
 	      		<div translate ng-message="required">entity-view.name-required</div>
 	    	</div>				
 		</md-input-container>
-        <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>
         <md-input-container class="md-block">
             <label translate>entity-view.description</label>
             <textarea ng-model="entityView.additionalInfo.description" rows="2"></textarea>
diff --git a/ui/src/app/entity-view/entity-views.tpl.html b/ui/src/app/entity-view/entity-views.tpl.html
index 5398449..50a0adb 100644
--- a/ui/src/app/entity-view/entity-views.tpl.html
+++ b/ui/src/app/entity-view/entity-views.tpl.html
@@ -29,13 +29,12 @@
                        on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])"
                        on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)"
                        on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)"
-                       on-manage-credentials="vm.manageCredentials(event, vm.grid.detailsConfig.currentItem)"
                        on-delete-entity-view="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-entity-view>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}">
             <tb-attribute-table flex
                                 entity-id="vm.grid.operatingItem().id.id"
-                                entity-type="{{vm.types.entityType.entityview}}"
+                                entity-type="{{vm.types.entityType.entityView}}"
                                 entity-name="vm.grid.operatingItem().name"
                                 default-attribute-scope="{{vm.types.attributesScope.client.value}}">
             </tb-attribute-table>
@@ -43,19 +42,19 @@
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}">
             <tb-attribute-table flex
                                 entity-id="vm.grid.operatingItem().id.id"
-                                entity-type="{{vm.types.entityType.entityview}}"
+                                entity-type="{{vm.types.entityType.entityView}}"
                                 entity-name="vm.grid.operatingItem().name"
                                 default-attribute-scope="{{vm.types.latestTelemetry.value}}"
                                 disable-attribute-scope-selection="true">
             </tb-attribute-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}">
-            <tb-alarm-table flex entity-type="vm.types.entityType.entityview"
+            <tb-alarm-table flex entity-type="vm.types.entityType.entityView"
                             entity-id="vm.grid.operatingItem().id.id">
             </tb-alarm-table>
         </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'entity-view.events' | translate }}">
-            <tb-event-table flex entity-type="vm.types.entityType.entityview"
+            <tb-event-table flex entity-type="vm.types.entityType.entityView"
                             entity-id="vm.grid.operatingItem().id.id"
                             tenant-id="vm.grid.operatingItem().tenantId.id"
                             default-event-type="{{vm.types.eventType.error.value}}">
@@ -64,18 +63,11 @@
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}">
             <tb-relation-table flex
                                entity-id="vm.grid.operatingItem().id.id"
-                               entity-type="{{vm.types.entityType.entityview}}">
+                               entity-type="{{vm.types.entityType.entityView}}">
             </tb-relation-table>
         </md-tab>
-        <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.operatingItem().additionalInfo.gateway" md-on-select="vm.grid.triggerResize()" label="{{ 'extension.extensions' | translate }}">
-            <tb-extension-table flex
-                                entity-id="vm.grid.operatingItem().id.id"
-                                entity-name="vm.grid.operatingItem().name"
-                                entity-type="{{vm.types.entityType.entityview}}">
-            </tb-extension-table>
-        </md-tab>
         <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.grid.isTenantAdmin()" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}">
-            <tb-audit-log-table flex entity-type="vm.types.entityType.entityview"
+            <tb-audit-log-table flex entity-type="vm.types.entityType.entityView"
                             entity-id="vm.grid.operatingItem().id.id"
                             audit-log-mode="{{vm.types.auditLogMode.entity}}">
             </tb-audit-log-table>
diff --git a/ui/src/app/entity-view/index.js b/ui/src/app/entity-view/index.js
index ebdd3ea..69f669e 100644
--- a/ui/src/app/entity-view/index.js
+++ b/ui/src/app/entity-view/index.js
@@ -20,7 +20,7 @@ import thingsboardApiEntityView from '../api/entity-view.service';
 import thingsboardApiCustomer from '../api/customer.service';
 
 import EntityViewRoutes from './entity-view.routes';
-import EntityViewCardController from './entity-view.controller';
+import {EntityViewController, EntityViewCardController} from './entity-view.controller';
 import AssignEntityViewToCustomerController from './assign-to-customer.controller';
 import AddEntityViewsToCustomerController from './add-entity-views-to-customer.controller';
 import EntityViewDirective from './entity-view.directive';
@@ -33,7 +33,7 @@ export default angular.module('thingsboard.entityView', [
     thingsboardApiCustomer
 ])
     .config(EntityViewRoutes)
-    .controller('EntityViewController', EntityViewCardController)
+    .controller('EntityViewController', EntityViewController)
     .controller('EntityViewCardController', EntityViewCardController)
     .controller('AssignEntityViewToCustomerController', AssignEntityViewToCustomerController)
     .controller('AddEntityViewsToCustomerController', AddEntityViewsToCustomerController)
diff --git a/ui/src/app/layout/index.js b/ui/src/app/layout/index.js
index 8f2958d..192400a 100644
--- a/ui/src/app/layout/index.js
+++ b/ui/src/app/layout/index.js
@@ -48,6 +48,7 @@ import thingsboardAdmin from '../admin';
 import thingsboardProfile from '../profile';
 import thingsboardAsset from '../asset';
 import thingsboardDevice from '../device';
+import thingsboardEntityView from '../entity-view';
 import thingsboardWidgetLibrary from '../widget';
 import thingsboardDashboard from '../dashboard';
 import thingsboardRuleChain from '../rulechain';
@@ -79,6 +80,7 @@ export default angular.module('thingsboard.home', [
     thingsboardProfile,
     thingsboardAsset,
     thingsboardDevice,
+    thingsboardEntityView,
     thingsboardWidgetLibrary,
     thingsboardDashboard,
     thingsboardRuleChain,
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index 5e04a52..09769ed 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -821,7 +821,8 @@
         "assignedToCustomer": "Assigned to customer",
         "unable-entity-view-device-alias-title": "Unable to delete entity view alias",
         "unable-entity-view-device-alias-text": "Device alias '{{entityViewAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
-        "select-entity-view": "Select entity view"
+        "select-entity-view": "Select entity view",
+        "make-public": "Make entity view public"
     },
     "event": {
         "event-type": "Event type",
diff --git a/ui/src/app/services/menu.service.js b/ui/src/app/services/menu.service.js
index 1c33d1f..869a25d 100644
--- a/ui/src/app/services/menu.service.js
+++ b/ui/src/app/services/menu.service.js
@@ -168,6 +168,12 @@ function Menu(userService, $state, $rootScope) {
                             icon: 'devices_other'
                         },
                         {
+                            name: 'entity-view.entity-views',
+                            type: 'link',
+                            state: 'home.entityViews',
+                            icon: 'view_stream'
+                        },
+                        {
                             name: 'widget.widget-library',
                             type: 'link',
                             state: 'home.widgets-bundles',
@@ -228,6 +234,16 @@ function Menu(userService, $state, $rootScope) {
                                 ]
                             },
                             {
+                                name: 'entity-view.management',
+                                places: [
+                                    {
+                                        name: 'entity-view.entity-views',
+                                        icon: 'view_stream',
+                                        state: 'home.entityViews'
+                                    }
+                                ]
+                            },
+                            {
                                 name: 'dashboard.management',
                                 places: [
                                     {
@@ -274,6 +290,12 @@ function Menu(userService, $state, $rootScope) {
                             icon: 'devices_other'
                         },
                         {
+                            name: 'entity-view.entity-views',
+                            type: 'link',
+                            state: 'home.entityViews',
+                            icon: 'view_stream'
+                        },
+                        {
                             name: 'dashboard.dashboards',
                             type: 'link',
                             state: 'home.dashboards',
@@ -301,16 +323,26 @@ function Menu(userService, $state, $rootScope) {
                                 }
                             ]
                         },
-                            {
-                                name: 'dashboard.view-dashboards',
-                                places: [
-                                    {
-                                        name: 'dashboard.dashboards',
-                                        icon: 'dashboard',
-                                        state: 'home.dashboards'
-                                    }
-                                ]
-                            }];
+                        {
+                            name: 'entity-view.management',
+                            places: [
+                                {
+                                    name: 'entity-view.entity-views',
+                                    icon: 'view_stream',
+                                    state: 'home.entityViews'
+                                }
+                            ]
+                        },
+                        {
+                            name: 'dashboard.view-dashboards',
+                            places: [
+                                {
+                                    name: 'dashboard.dashboards',
+                                    icon: 'dashboard',
+                                    state: 'home.dashboards'
+                                }
+                            ]
+                        }];
                 }
             }
         }