diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql new file mode 100644 index 0000000000..d9ba517df7 --- /dev/null +++ b/application/src/main/data/upgrade/2.1.1/schema_update.cql @@ -0,0 +1,92 @@ +-- +-- 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_views_by_tenant_and_name; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_views_by_tenant_and_entity; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_views_by_tenant_and_customer; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_views_by_tenant_and_customer_and_entity; + +DROP TABLE IF EXISTS thingsboard.entity_views; + +CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( + id timeuuid, + entity_id timeuuid, + tenant_id timeuuid, + customer_id timeuuid, + name text, + keys text, + ts_begin bigint, + ts_end bigint, + search_text text, + additional_info text, + PRIMARY KEY (id, entity_id, tenant_id, customer_id) + ); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL + AND tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND keys IS NOT NULL + AND ts_begin IS NOT NULL + AND ts_end IS NOT NULL + AND name IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, name, id, entity_id, customer_id) + WITH CLUSTERING ORDER BY (name ASC, id DESC, entity_id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_entity AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL + AND tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND keys IS NOT NULL + AND ts_begin IS NOT NULL + AND ts_end IS NOT NULL + AND name IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, entity_id, id, customer_id, name) + WITH CLUSTERING ORDER BY (entity_id ASC, customer_id ASC, id DESC, name DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_customer AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL + AND tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND keys IS NOT NULL + AND ts_begin IS NOT NULL + AND ts_end IS NOT NULL + AND name IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, customer_id, id, entity_id, name) + WITH CLUSTERING ORDER BY (customer_id ASC, id DESC, entity_id DESC, name DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_customer_and_entity AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL + AND tenant_id IS NOT NULL + AND customer_id IS NOT NULL + AND keys IS NOT NULL + AND ts_begin IS NOT NULL + AND ts_end IS NOT NULL + AND name IS NOT NULL + AND id IS NOT NULL + PRIMARY KEY (tenant_id, customer_id, entity_id, id, name) + WITH CLUSTERING ORDER BY (customer_id ASC, entity_id DESC, id DESC, name DESC); diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.sql b/application/src/main/data/upgrade/2.1.1/schema_update.sql new file mode 100644 index 0000000000..3670aad035 --- /dev/null +++ b/application/src/main/data/upgrade/2.1.1/schema_update.sql @@ -0,0 +1,31 @@ +-- +-- 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_views ( + 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), + name varchar(255), + keys varchar(255), + ts_begin varchar(255), + ts_end varchar(255), + search_text varchar(255), + additional_info varchar +); diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index de73fe0796..af62e04cff 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; @@ -139,6 +140,9 @@ public abstract class BaseController { @Autowired protected DeviceStateService deviceStateService; + @Autowired + protected EntityViewService entityViewService; + @ExceptionHandler(ThingsboardException.class) public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { errorResponseHandler.handle(ex, response); @@ -313,6 +317,9 @@ public abstract class BaseController { case USER: checkUserId(new UserId(entityId.getId())); return; + case ENTITY_VIEW: + checkEntityView(entityViewService.findEntityViewById(new EntityViewId(entityId.getId()))); + return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); } @@ -340,6 +347,25 @@ public abstract class BaseController { } } + protected EntityView checkEntityViewId(EntityViewId entityViewId) throws ThingsboardException { + try { + validateId(entityViewId, "Incorrect entityViewId " + entityViewId); + EntityView entityView = entityViewService.findEntityViewById(entityViewId); + checkEntityView(entityView); + return entityView; + } catch (Exception e) { + throw handleException(e, false); + } + } + + protected void checkEntityView(EntityView entityView) throws ThingsboardException { + checkNotNull(entityView); + checkTenantId(entityView.getTenantId()); + if (entityView.getCustomerId() != null && !entityView.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { + checkCustomerId(entityView.getCustomerId()); + } + } + Asset checkAssetId(AssetId assetId) throws ThingsboardException { try { validateId(assetId, "Incorrect assetId " + assetId); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java new file mode 100644 index 0000000000..fd3ccf2fe1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -0,0 +1,98 @@ +/** + * 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.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.*; +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.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.service.security.model.SecurityUser; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Victor Basanets on 8/28/2017. + */ +@RestController +@RequestMapping("/api") +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) + @ResponseBody + public EntityView getEntityViewById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) + throws ThingsboardException { + + checkParameter(ENTITY_VIEW_ID, strEntityViewId); + try { + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); + return checkEntityViewId(entityViewId); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/entity-view", method = RequestMethod.POST) + @ResponseBody + public EntityView saveEntityView(@RequestBody EntityView entityView) throws ThingsboardException { + try { + entityView.setTenantId(getCurrentUser().getTenantId()); + EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); + logEntityAction(savedEntityView.getId(), savedEntityView, null, + entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + + return savedEntityView; + + } catch (Exception e) { + logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null, + entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); + + throw handleException(e); + } + } + + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/entity-view/{entityViewId}", method = RequestMethod.DELETE) + @ResponseStatus(value = HttpStatus.OK) + public void deleteEntityView(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { + checkParameter(ENTITY_VIEW_ID, strEntityViewId); + try { + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); + EntityView entityView = checkEntityViewId(entityViewId); + entityViewService.deleteEntityView(entityViewId); + + logEntityAction(entityViewId, entityView, entityView.getCustomerId(), + ActionType.DELETED,null, strEntityViewId); + } catch (Exception e) { + logEntityAction(emptyId(EntityType.ENTITY_VIEW), + null, + null, + ActionType.DELETED, e, strEntityViewId); + throw handleException(e); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index f863d0bb2a..347b05409e 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -88,6 +88,13 @@ public class ThingsboardInstallService { dataUpdateService.updateData("1.4.0"); + case "2.0.0": + log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ..."); + + databaseUpgradeService.upgradeDatabase("2.0.0"); + + dataUpdateService.updateData("2.0.0"); + log.info("Updating system data..."); systemDataLoaderService.deleteSystemWidgetBundle("charts"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java index 5daebcc3c5..0194a5dfd7 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultDataUpdateService.java @@ -49,6 +49,10 @@ public class DefaultDataUpdateService implements DataUpdateService { log.info("Updating data from version 1.4.0 to 2.0.0 ..."); tenantsDefaultRuleChainUpdater.updateEntities(null); break; + case "2.0.0": + log.info("Updating data from version 2.0.0 to 2.1.1 ..."); + tenantsDefaultRuleChainUpdater.updateEntities(null); + break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 3a4a837217..7d701b7a53 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -107,6 +107,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Schema updated."); } break; + case "2.0.0": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.1", SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + log.info("Schema updated."); + } + break; + default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 743a8607b5..be5149dcb8 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -359,7 +359,7 @@ spring: password: "${SPRING_DATASOURCE_PASSWORD:}" # PostgreSQL DAO Configuration -#spring: +# spring: # data: # sql: # repositories: diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java new file mode 100644 index 0000000000..c7ac98d413 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -0,0 +1,134 @@ +/** + * 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.controller; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.objects.AttributesEntityView; +import org.thingsboard.server.common.data.objects.TelemetryEntityView; +import org.thingsboard.server.common.data.security.Authority; + +import java.util.Arrays; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +public abstract class BaseEntityViewControllerTest extends AbstractControllerTest { + + private Tenant savedTenant; + private User tenantAdmin; + private Device testDevice; + private TelemetryEntityView obj; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + + Device device = new Device(); + device.setName("Test device"); + device.setType("default"); + testDevice = doPost("/api/device", device, Device.class); + + obj = new TelemetryEntityView( + Arrays.asList("109L", "209L"), + new AttributesEntityView( + Arrays.asList("caKey1", "caKey2", "caKey3"), + Arrays.asList("saKey1", "saKey2", "saKey3", "saKey4"), + Arrays.asList("shKey1", "shKey2", "shKey3", "shKey4", "shKey5"))); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testFindEntityViewById() throws Exception { + EntityView view = new EntityView(); + view.setName("Test entity view"); + view.setEntityId(testDevice.getId()); + view.setKeys(new TelemetryEntityView(obj)); + EntityView savedView = doPost("/api/entity-view", view, EntityView.class); + EntityView foundView = doGet("/api/entity-view/" + savedView.getId().getId().toString(), EntityView.class); + Assert.assertNotNull(foundView); + Assert.assertEquals(savedView, foundView); + } + + @Test + public void testSaveEntityViewWithIdOfDevice() throws Exception { + EntityView view = new EntityView(); + view.setEntityId(testDevice.getId()); + view.setName("Test entity view"); + view.setTenantId(savedTenant.getId()); + view.setKeys(new TelemetryEntityView(obj)); + EntityView savedView = doPost("/api/entity-view", view, EntityView.class); + + Assert.assertNotNull(savedView); + Assert.assertNotNull(savedView.getId()); + Assert.assertTrue(savedView.getCreatedTime() > 0); + Assert.assertEquals(savedTenant.getId(), savedView.getTenantId()); + Assert.assertNotNull(savedView.getCustomerId()); + Assert.assertEquals(NULL_UUID, savedView.getCustomerId().getId()); + Assert.assertEquals(savedView.getName(), savedView.getName()); + + savedView.setName("New test entity view"); + doPost("/api/entity-view", savedView, EntityView.class); + + EntityView foundEntityView = doGet("/api/entity-view/" + + savedView.getId().getId().toString(), EntityView.class); + + Assert.assertEquals(foundEntityView.getName(), savedView.getName()); + } + + @Test + public void testDeleteEntityView() throws Exception { + EntityView view = new EntityView(); + view.setName("Test entity view"); + view.setEntityId(testDevice.getId()); + view.setKeys(new TelemetryEntityView((TelemetryEntityView) obj)); + EntityView savedView = doPost("/api/entity-view", view, EntityView.class); + + doDelete("/api/entity-view/" + savedView.getId().getId().toString()) + .andExpect(status().isOk()); + + doGet("/api/entity-view/" + savedView.getId().getId().toString()) + .andExpect(status().isNotFound()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java index f316051212..a9e94e9184 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClasspathSuite.ClassnameFilters({ - "org.thingsboard.server.controller.sql.*SqlTest", + "org.thingsboard.server.controller.sql.EntityViewControllerSqlTest", }) public class ControllerSqlTestSuite { diff --git a/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java new file mode 100644 index 0000000000..ad066fc7ab --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java @@ -0,0 +1,24 @@ +/** + * 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.controller.nosql; + +import org.thingsboard.server.controller.BaseEntityViewControllerTest; + +/** + * Created by Victor Basanets on 8/27/2017. + */ +public class EntityViewControllerNoSqlTest extends BaseEntityViewControllerTest { +} diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntityViewControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntityViewControllerSqlTest.java new file mode 100644 index 0000000000..76d99250c1 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntityViewControllerSqlTest.java @@ -0,0 +1,31 @@ +/** + * 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.controller.sql; + +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.controller.BaseEntityViewControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.util.Arrays; + +/** + * Created by Victor Basanets on 8/27/2017. + */ +@DaoSqlTest +public class EntityViewControllerSqlTest extends BaseEntityViewControllerTest { +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index 21de402721..698a69ef6b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -20,4 +20,5 @@ public class CacheConstants { public static final String RELATIONS_CACHE = "relations"; public static final String DEVICE_CACHE = "devices"; public static final String ASSET_CACHE = "assets"; + public static final String ENTITY_VIEW_CACHE = "entityViews"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index fe9c018718..ef4994adda 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java new file mode 100644 index 0000000000..813a9acd08 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -0,0 +1,76 @@ +/** + * 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; + +import lombok.*; +import org.thingsboard.server.common.data.id.CustomerId; +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.objects.TelemetryEntityView; + +/** + * Created by Victor Basanets on 8/27/2017. + */ + +@Data +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class EntityView extends SearchTextBasedWithAdditionalInfo + implements HasName, HasTenantId, HasCustomerId { + + private static final long serialVersionUID = 5582010124562018986L; + + private EntityId entityId; + private TenantId tenantId; + private CustomerId customerId; + private String name; + private TelemetryEntityView keys; + private Long tsBegin; + private Long tsEnd; + + public EntityView() { + super(); + } + + public EntityView(EntityViewId id) { + super(id); + } + + public EntityView(EntityView entityView) { + super(entityView); + } + + @Override + public String getSearchText() { + return getName() /*What the ...*/; + } + + @Override + public CustomerId getCustomerId() { + return customerId; + } + + @Override + public String getName() { + return name; + } + + @Override + public TenantId getTenantId() { + return tenantId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index ed4cf2f784..4e35c0b2df 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -57,6 +57,8 @@ public class EntityIdFactory { return new RuleChainId(uuid); case RULE_NODE: return new RuleNodeId(uuid); + case ENTITY_VIEW: + return new EntityViewId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityViewId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityViewId.java new file mode 100644 index 0000000000..459dd990a4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityViewId.java @@ -0,0 +1,44 @@ +/** + * 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +/** + * Created by Victor Basanets on 8/27/2017. + */ +public class EntityViewId extends UUIDBased implements EntityId { + + private static final long serialVersionUID = 1L; + + @JsonCreator + public EntityViewId(@JsonProperty("id") UUID id) { + super(id); + } + + public static EntityViewId fromString(String entityViewID) { + return new EntityViewId(UUID.fromString(entityViewID)); + } + + @Override + public EntityType getEntityType() { + return EntityType.ENTITY_VIEW; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java new file mode 100644 index 0000000000..b1d270c934 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java @@ -0,0 +1,52 @@ +/** + * 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.objects; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Victor Basanets on 9/05/2017. + */ +@Data +@NoArgsConstructor +public class AttributesEntityView { + + private List cs = new ArrayList<>(); + private List ss = new ArrayList<>(); + private List sh = new ArrayList<>(); + + public AttributesEntityView(List cs, + List ss, + List sh) { + + this.cs = new ArrayList<>(cs); + this.ss = new ArrayList<>(ss); + this.sh = new ArrayList<>(sh); + } + + public AttributesEntityView(AttributesEntityView obj) { + this(obj.getCs(), obj.getSs(), obj.getSh()); + } + + @Override + public String toString() { + return "{cs=" + cs + ", ss=" + ss + ", sh=" + sh + '}'; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java new file mode 100644 index 0000000000..e7398e6500 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java @@ -0,0 +1,48 @@ +/** + * 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.objects; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Victor Basanets on 9/05/2017. + */ +@Data +@NoArgsConstructor +public class TelemetryEntityView { + + private List timeseries; + private AttributesEntityView attributes; + + public TelemetryEntityView(List timeseries, AttributesEntityView attributes) { + + this.timeseries = new ArrayList<>(timeseries); + this.attributes = attributes; + } + + public TelemetryEntityView(TelemetryEntityView obj) { + this(obj.getTimeseries(), obj.getAttributes()); + } + + @Override + public String toString() { + return "{timeseries=" + timeseries + ", attributes=" + attributes + '}'; + } +} 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 new file mode 100644 index 0000000000..25404db834 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java @@ -0,0 +1,88 @@ +/** + * 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.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; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +/** + * Created by Victor Basanets on 8/28/2017. + */ +public interface EntityViewDao extends Dao { + + /** + * Find entity views by tenantId and page link. + * + * @param tenantId the tenantId + * @param pageLink the page link + * @return the list of entity view objects + */ + List findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink); + + /** + * Find entity views by tenantId and entity view name. + * + * @param tenantId the tenantId + * @param name the entity view name + * @return the optional entity view object + */ + Optional findEntityViewByTenantIdAndName(UUID tenantId, String name); + + /** + * Find entity views by tenantId, entityId and page link. + * + * @param tenantId the tenantId + * @param entityId the entityId + * @param pageLink the page link + * @return the list of entity view objects + */ + List findEntityViewByTenantIdAndEntityId(UUID tenantId, + UUID entityId, + TextPageLink pageLink); + + /** + * Find entity views by tenantId, customerId and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param pageLink the page link + * @return the list of entity view objects + */ + List findEntityViewsByTenantIdAndCustomerId(UUID tenantId, + UUID customerId, + TextPageLink pageLink); + + /** + * Find entity views by tenantId, customerId, entityId and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param entityId the entityId + * @param pageLink the page link + * @return the list of entity view objects + */ + List findEntityViewsByTenantIdAndCustomerIdAndEntityId(UUID tenantId, + 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 new file mode 100644 index 0000000000..943d35e9b4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java @@ -0,0 +1,60 @@ +/** + * 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.dao.entityview; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.CustomerId; +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; + +/** + * Created by Victor Basanets on 8/27/2017. + */ +public interface EntityViewService { + + EntityView findEntityViewById(EntityViewId entityViewId); + + EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name); + + EntityView saveEntityView(EntityView entityView); + + EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId); + + EntityView unassignEntityViewFromCustomer(EntityViewId entityViewId); + + void deleteEntityView(EntityViewId entityViewId); + + TextPageData findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); + + TextPageData findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, + TextPageLink pageLink); + + void deleteEntityViewByTenantId(TenantId tenantId); + + TextPageData findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, + TextPageLink pageLink); + + TextPageData findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId, + CustomerId customerId, + EntityId entityId, + TextPageLink pageLink); + + void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId); +} 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 new file mode 100644 index 0000000000..6554083fe4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -0,0 +1,276 @@ +/** + * 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.dao.entityview; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.CustomerId; +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.dao.customer.CustomerDao; +import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.util.ArrayList; +import java.util.List; + +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. + */ +@Service +@Slf4j +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 "; + public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId "; + public static final String INCORRECT_ENTITY_VIEW_ID = "Incorrect entityViewId "; + + @Autowired + private EntityViewDao entityViewDao; + + @Autowired + private TenantDao tenantDao; + + @Autowired + private CustomerDao customerDao; + + @Override + public EntityView findEntityViewById(EntityViewId entityViewId) { + log.trace("Executing findEntityViewById [{}]", entityViewId); + validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); + return entityViewDao.findById(entityViewId.getId()); + } + + @Override + public EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name) { + log.trace("Executing findEntityViewByTenantIdAndName [{}][{}]", tenantId, name); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name) + .orElse(null); + } + + @Override + public EntityView saveEntityView(EntityView entityView) { + log.trace("Executing save entity view [{}]", entityView); + entityViewValidator.validate(entityView); + return entityViewDao.save(entityView); + } + + @Override + public EntityView assignEntityViewToCustomer(EntityViewId entityViewId, CustomerId customerId) { + EntityView entityView = findEntityViewById(entityViewId); + entityView.setCustomerId(customerId); + return saveEntityView(entityView); + } + + @Override + public EntityView unassignEntityViewFromCustomer(EntityViewId entityViewId) { + EntityView entityView = findEntityViewById(entityViewId); + entityView.setCustomerId(null); + return saveEntityView(entityView); + } + + @Override + public void deleteEntityView(EntityViewId entityViewId) { + log.trace("Executing deleteEntityView [{}]", entityViewId); + validateId(entityViewId, INCORRECT_ENTITY_VIEW_ID + entityViewId); + deleteEntityRelations(entityViewId); + EntityView entityView = entityViewDao.findById(entityViewId.getId()); + List list = new ArrayList<>(); + list.add(entityView.getTenantId()); + list.add(entityView.getName()); + entityViewDao.removeById(entityViewId.getId()); + } + + @Override + public TextPageData findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) { + log.trace("Executing findEntityViewByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + List entityViews = entityViewDao.findEntityViewByTenantId(tenantId.getId(), pageLink); + return new TextPageData<>(entityViews, pageLink); + } + + @Override + public TextPageData findEntityViewByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, + TextPageLink pageLink) { + + log.trace("Executing findEntityViewByTenantIdAndType, tenantId [{}], entityId [{}], pageLink [{}]", + tenantId, entityId, pageLink); + + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateString(entityId.toString(), "Incorrect entityId " + entityId.toString()); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + List entityViews = entityViewDao.findEntityViewByTenantIdAndEntityId(tenantId.getId(), + entityId.getId(), pageLink); + + return new TextPageData<>(entityViews, pageLink); + } + + @Override + public void deleteEntityViewByTenantId(TenantId tenantId) { + log.trace("Executing deleteEntityViewByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + tenantEntityViewRemover.removeEntities(tenantId); + } + + @Override + public TextPageData findEntityViewsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, + TextPageLink pageLink) { + + log.trace("Executing findEntityViewByTenantIdAndCustomerId, tenantId [{}], customerId [{}]," + + " pageLink [{}]", tenantId, customerId, pageLink); + + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + List entityViews = entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(), + customerId.getId(), pageLink); + + return new TextPageData<>(entityViews, pageLink); + } + + @Override + public TextPageData findEntityViewsByTenantIdAndCustomerIdAndEntityId(TenantId tenantId, + CustomerId customerId, + EntityId entityId, + TextPageLink pageLink) { + + log.trace("Executing findEntityViewsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}]," + + " entityId [{}], pageLink [{}]", tenantId, customerId, entityId, pageLink); + + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + validateString(entityId.toString(), "Incorrect entityId " + entityId.toString()); + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); + List entityViews = entityViewDao.findEntityViewsByTenantIdAndCustomerIdAndEntityId( + tenantId.getId(), customerId.getId(), entityId.getId(), pageLink); + + return new TextPageData<>(entityViews, pageLink); + } + + @Override + public void unassignCustomerEntityViews(TenantId tenantId, CustomerId customerId) { + log.trace("Executing unassignCustomerEntityViews, tenantId [{}], customerId [{}]", tenantId, customerId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId); + new CustomerEntityViewsUnAssigner(tenantId).removeEntities(customerId); + } + + private DataValidator entityViewValidator = + new DataValidator() { + + @Override + protected void validateCreate(EntityView entityView) { + entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) + .ifPresent( e -> { + throw new DataValidationException("Entity view with such name already exists!"); + }); + } + + @Override + protected void validateUpdate(EntityView entityView) { + entityViewDao.findEntityViewByTenantIdAndName(entityView.getTenantId().getId(), entityView.getName()) + .ifPresent( e -> { + if (!e.getUuidId().equals(entityView.getUuidId())) { + throw new DataValidationException("Entity view with such name already exists!"); + } + }); + } + + @Override + protected void validateDataImpl(EntityView entityView) { + if (StringUtils.isEmpty(entityView.getKeys().toString())) { + throw new DataValidationException("Entity view type should be specified!"); + } + if (StringUtils.isEmpty(entityView.getName())) { + throw new DataValidationException("Entity view name should be specified!"); + } + if (entityView.getTenantId() == null) { + throw new DataValidationException("Entity view should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(entityView.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Entity view is referencing to non-existent tenant!"); + } + } + if (entityView.getCustomerId() == null) { + entityView.setCustomerId(new CustomerId(NULL_UUID)); + } else if (!entityView.getCustomerId().getId().equals(NULL_UUID)) { + Customer customer = customerDao.findById(entityView.getCustomerId().getId()); + if (customer == null) { + throw new DataValidationException("Can't assign entity view to non-existent customer!"); + } + if (!customer.getTenantId().getId().equals(entityView.getTenantId().getId())) { + throw new DataValidationException("Can't assign entity view to customer from different tenant!"); + } + } + } + }; + + private PaginatedRemover tenantEntityViewRemover = + new PaginatedRemover() { + + @Override + protected List findEntities(TenantId id, TextPageLink pageLink) { + return entityViewDao.findEntityViewByTenantId(id.getId(), pageLink); + } + + @Override + protected void removeEntity(EntityView entity) { + deleteEntityView(new EntityViewId(entity.getUuidId())); + } + }; + + private class CustomerEntityViewsUnAssigner extends PaginatedRemover { + + private TenantId tenantId; + + CustomerEntityViewsUnAssigner(TenantId tenantId) { + this.tenantId = tenantId; + } + + @Override + protected List findEntities(CustomerId id, TextPageLink pageLink) { + return entityViewDao.findEntityViewsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); + } + + @Override + protected void removeEntity(EntityView entity) { + unassignEntityViewFromCustomer(new EntityViewId(entity.getUuidId())); + } + } +} 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 3a934eb5d2..d06ddbd8e5 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 @@ -45,6 +45,7 @@ public class ModelConstants { public static final String SEARCH_TEXT_PROPERTY = "search_text"; public static final String ADDITIONAL_INFO_PROPERTY = "additional_info"; public static final String ENTITY_TYPE_PROPERTY = "entity_type"; + /*public static final String ENTITY_VIEW_ID_PROPERTY = "entity_view_id";*/ public static final String ENTITY_TYPE_COLUMN = ENTITY_TYPE_PROPERTY; public static final String ENTITY_ID_COLUMN = "entity_id"; @@ -143,6 +144,20 @@ public class ModelConstants { public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name"; public static final String DEVICE_TYPES_BY_TENANT_VIEW_NAME = "device_types_by_tenant"; + /** + * Cassandra entityView constants. + */ + public static final String ENTITY_VIEW_TABLE_FAMILY_NAME = "entity_views"; + 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_TYPE_PROPERTY = "type_entity"; + public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; + public static final String ENTITY_VIEW_TS_BEGIN_PROPERTY = "ts_begin"; + public static final String ENTITY_VIEW_TS_END_PROPERTY = "ts_end"; + public static final String ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; + /** * Cassandra audit log constants. */ 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 new file mode 100644 index 0000000000..65914dd566 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/EntityViewEntity.java @@ -0,0 +1,146 @@ +/** + * 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.dao.model.nosql; + +import com.datastax.driver.core.utils.UUIDs; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.hibernate.annotations.Type; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +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 java.io.IOException; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; + +/** + * Created by Victor Basanets on 8/31/2017. + */ +@Data +@Table(name = ENTITY_VIEW_TABLE_FAMILY_NAME) +@EqualsAndHashCode +@ToString +public class EntityViewEntity implements SearchTextEntity { + + @PartitionKey(value = 0) + @Column(name = ID_PROPERTY) + private UUID id; + + @PartitionKey(value = 1) + @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) + private UUID entityId; + + @PartitionKey(value = 2) + @Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY) + private UUID tenantId; + + @PartitionKey(value = 3) + @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) + private UUID customerId; + + @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) + private String name; + + @Type(type = "json") + @Column(name = ModelConstants.ENTITY_VIEW_KEYS_PROPERTY) + private JsonNode keys; + + @Column(name = ModelConstants.ENTITY_VIEW_TS_BEGIN_PROPERTY) + private String tsBegin; + + @Column(name = ModelConstants.ENTITY_VIEW_TS_END_PROPERTY) + private String tsEnd; + + @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) + private String searchText; + + @Type(type = "json") + @Column(name = ModelConstants.ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + + public EntityViewEntity() { + super(); + } + + public EntityViewEntity(EntityView entityView) { + if (entityView.getId() != null) { + this.id = entityView.getId().getId(); + } + if (entityView.getEntityId() != null) { + this.entityId = entityView.getEntityId().getId(); + } + if (entityView.getTenantId() != null) { + this.tenantId = entityView.getTenantId().getId(); + } + if (entityView.getCustomerId() != null) { + this.customerId = entityView.getCustomerId().getId(); + } + this.name = entityView.getName(); +// try { +// this.keys = entityView.getKeys(); +// } catch (IOException e) { +// e.printStackTrace(); +// } + this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0"; + this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0"; + this.searchText = entityView.getSearchText(); + this.additionalInfo = entityView.getAdditionalInfo(); + } + + @Override + public String getSearchTextSource() { + return name; + } + + @Override + public EntityView toData() { + EntityView entityView = new EntityView(new EntityViewId(id)); + entityView.setCreatedTime(UUIDs.unixTimestamp(id)); + if (entityId != null) { + entityView.setEntityId(new DeviceId(entityId)); + } + if (tenantId != null) { + entityView.setTenantId(new TenantId(tenantId)); + } + if (customerId != null) { + entityView.setCustomerId(new CustomerId(customerId)); + } + entityView.setName(name); +// try { +// entityView.setKeys((TelemetryEntityView) entityView.getKeys().toObject(keys)); +// } catch (IOException e) { +// e.printStackTrace(); +// } + entityView.setTsBegin(Long.parseLong(tsBegin)); + entityView.setTsEnd(Long.parseLong(tsEnd)); + entityView.setAdditionalInfo(additionalInfo); + return entityView; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java new file mode 100644 index 0000000000..9136717fe3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityViewEntity.java @@ -0,0 +1,150 @@ +/** + * 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.dao.model.sql; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.*; +import org.thingsboard.server.common.data.objects.TelemetryEntityView; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.*; +import java.io.IOException; + +import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_ENTITY_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_PROPERTY; + +/** + * Created by Victor Basanets on 8/30/2017. + */ + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.ENTITY_VIEW_TABLE_FAMILY_NAME) +public class EntityViewEntity extends BaseSqlEntity implements SearchTextEntity { + + @Column(name = ModelConstants.ENTITY_VIEW_ENTITY_ID_PROPERTY) + private String entityId; + + @Enumerated(EnumType.STRING) + @Column(name = ENTITY_TYPE_PROPERTY) + private EntityType entityType; + + @Column(name = ModelConstants.ENTITY_VIEW_TENANT_ID_PROPERTY) + private String tenantId; + + @Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY) + private String customerId; + + @Column(name = ModelConstants.ENTITY_VIEW_NAME_PROPERTY) + private String name; + + @Column(name = ModelConstants.ENTITY_VIEW_KEYS_PROPERTY) + private String keys; + + @Column(name = ModelConstants.ENTITY_VIEW_TS_BEGIN_PROPERTY) + private String tsBegin; + + @Column(name = ModelConstants.ENTITY_VIEW_TS_END_PROPERTY) + private String tsEnd; + + @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) + private String searchText; + + @Type(type = "json") + @Column(name = ModelConstants.ENTITY_VIEW_ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + + private static final ObjectMapper mapper = new ObjectMapper(); + + public EntityViewEntity() { + super(); + } + + public EntityViewEntity(EntityView entityView) { + if (entityView.getId() != null) { + this.setId(entityView.getId().getId()); + } + if (entityView.getEntityId() != null) { + this.entityId = toString(entityView.getEntityId().getId()); + this.entityType = entityView.getEntityId().getEntityType(); + } + if (entityView.getTenantId() != null) { + this.tenantId = toString(entityView.getTenantId().getId()); + } + if (entityView.getCustomerId() != null) { + this.customerId = toString(entityView.getCustomerId().getId()); + } + this.name = entityView.getName(); + try { + this.keys = mapper.writeValueAsString(entityView.getKeys()); + } catch (IOException e) { + e.printStackTrace(); + } + this.tsBegin = entityView.getTsBegin() != null ? String.valueOf(entityView.getTsBegin()) : "0"; + this.tsEnd = entityView.getTsEnd() != null ? String.valueOf(entityView.getTsEnd()) : "0"; + this.searchText = entityView.getSearchText(); + this.additionalInfo = entityView.getAdditionalInfo(); + } + + @Override + public String getSearchTextSource() { + return name; + } + + @Override + public void setSearchText(String searchText) { + this.searchText = searchText; + } + + @Override + public EntityView toData() { + EntityView entityView = new EntityView(new EntityViewId(getId())); + entityView.setCreatedTime(UUIDs.unixTimestamp(getId())); + + if (entityId != null) { + entityView.setEntityId(EntityIdFactory.getByTypeAndId(entityType.name(), toUUID(entityId).toString())); + } + if (tenantId != null) { + entityView.setTenantId(new TenantId(toUUID(tenantId))); + } + if (customerId != null) { + entityView.setCustomerId(new CustomerId(toUUID(customerId))); + } + entityView.setName(name); + try { + entityView.setKeys(mapper.readValue(keys, TelemetryEntityView.class)); + } catch (IOException e) { + e.printStackTrace(); + } + entityView.setTsBegin(Long.parseLong(tsBegin)); + entityView.setTsEnd(Long.parseLong(tsEnd)); + entityView.setAdditionalInfo(additionalInfo); + return entityView; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java new file mode 100644 index 0000000000..27bd8395ac --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -0,0 +1,81 @@ +/** + * 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.dao.sql.entityview; + +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.id.EntityId; +import org.thingsboard.server.dao.model.sql.EntityViewEntity; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; + +/** + * Created by Victor Basanets on 8/31/2017. + */ +@SqlDao +public interface EntityViewRepository extends CrudRepository { + + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + + "AND e.id > :idOffset ORDER BY e.id") + List findByTenantId(@Param("tenantId") String tenantId, + @Param("textSearch") String textSearch, + @Param("idOffset") String idOffset, + Pageable pageable); + + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + + "AND e.entityId = :entityId " + + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + + "AND e.id > :idOffset ORDER BY e.id") + List findByTenantIdAndEntityId(@Param("tenantId") String tenantId, + @Param("entityId") String entityId, + @Param("textSearch") String textSearch, + @Param("idOffset") String idOffset, + Pageable pageable); + + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + + "AND e.customerId = :customerId " + + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + + "AND e.id > :idOffset ORDER BY e.id") + List findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, + @Param("customerId") String customerId, + @Param("searchText") String searchText, + @Param("idOffset") String idOffset, + Pageable pageable); + + @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + + "AND e.customerId = :customerId " + + "AND e.entityId = :entityId " + + "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + + "AND e.id > :idOffset ORDER BY e.id") + List findByTenantIdAndCustomerIdAndEntityId(@Param("tenantId") String tenantId, + @Param("customerId") String customerId, + @Param("entityId") String entityId, + @Param("textSearch") String textSearch, + @Param("idOffset") String idOffset, + Pageable pageable); + + EntityViewEntity findByTenantIdAndName(String tenantId, String name); + + List findAllByTenantIdAndCustomerIdAndIdIn(String tenantId, + String customerId, + List entityViewsIds); + + List findAllByTenantIdAndIdIn(String tenantId, List entityViewsIds); +} 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 new file mode 100644 index 0000000000..57b0afae8b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -0,0 +1,118 @@ +/** + * 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.dao.sql.entityview; + +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.EntityView; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.entityview.EntityViewDao; +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.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; + +/** + * Created by Victor Basanets on 8/31/2017. + */ +@Component +@SqlDao +public class JpaEntityViewDao extends JpaAbstractSearchTextDao + implements EntityViewDao { + + @Autowired + EntityViewRepository entityViewRepository; + + @Override + protected Class getEntityClass() { + return EntityViewEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return entityViewRepository; + } + + @Override + public List findEntityViewByTenantId(UUID tenantId, TextPageLink pageLink) { + return DaoUtil.convertDataList( + entityViewRepository.findByTenantId( + fromTimeUUID(tenantId), + Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()))); + } + + @Override + public Optional findEntityViewByTenantIdAndName(UUID tenantId, String name) { + return Optional.ofNullable( + DaoUtil.getData(entityViewRepository.findByTenantIdAndName(fromTimeUUID(tenantId), name))); + } + + @Override + public List findEntityViewByTenantIdAndEntityId(UUID tenantId, + UUID entityId, + TextPageLink pageLink) { + return DaoUtil.convertDataList( + entityViewRepository.findByTenantIdAndEntityId( + fromTimeUUID(tenantId), + fromTimeUUID(entityId), + Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()))); + } + + @Override + public List findEntityViewsByTenantIdAndCustomerId(UUID tenantId, + UUID customerId, + TextPageLink pageLink) { + return DaoUtil.convertDataList( + entityViewRepository.findByTenantIdAndCustomerId( + fromTimeUUID(tenantId), + fromTimeUUID(customerId), + Objects.toString(pageLink, ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()) + )); + } + + @Override + public List findEntityViewsByTenantIdAndCustomerIdAndEntityId(UUID tenantId, + UUID customerId, + UUID entityId, + TextPageLink pageLink) { + return DaoUtil.convertDataList( + entityViewRepository.findByTenantIdAndCustomerIdAndEntityId( + fromTimeUUID(tenantId), + fromTimeUUID(customerId), + fromTimeUUID(entityId), + Objects.toString(pageLink, ""), + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), + new PageRequest(0, pageLink.getLimit()) + )); + } +} diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql index f03122ab6b..bdd413d6ad 100644 --- a/dao/src/main/resources/cassandra/schema.cql +++ b/dao/src/main/resources/cassandra/schema.cql @@ -638,3 +638,45 @@ CREATE TABLE IF NOT EXISTS thingsboard.rule_node ( additional_info text, PRIMARY KEY (id) ); + +CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( + id timeuuid, + entity_id timeuuid, + tenant_id timeuuid, + customer_id timeuuid, + name text, + keys text, + ts_begin bigint, + ts_end bigint, + search_text text, + additional_info text, + PRIMARY KEY (id, entity_id, tenant_id, customer_id) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_name AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL AND tenant_id IS NOT NULL AND customer_id IS NOT NULL AND keys IS NOT NULL AND ts_begin IS NOT NULL AND ts_end IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY (tenant_id, name, id, entity_id, customer_id) + WITH CLUSTERING ORDER BY (name ASC, id DESC, entity_id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_entity AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL AND tenant_id IS NOT NULL AND customer_id IS NOT NULL AND keys IS NOT NULL AND ts_begin IS NOT NULL AND ts_end IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY (tenant_id, entity_id, id, customer_id, name) + WITH CLUSTERING ORDER BY (entity_id ASC, customer_id ASC, id DESC, name DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_customer AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL AND tenant_id IS NOT NULL AND customer_id IS NOT NULL AND keys IS NOT NULL AND ts_begin IS NOT NULL AND ts_end IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY (tenant_id, customer_id, id, entity_id, name) + WITH CLUSTERING ORDER BY (customer_id ASC, id DESC, entity_id DESC, name DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_views_by_tenant_and_customer_and_entity AS + SELECT * + from thingsboard.entity_views + WHERE entity_id IS NOT NULL AND tenant_id IS NOT NULL AND customer_id IS NOT NULL AND keys IS NOT NULL AND ts_begin IS NOT NULL AND ts_end IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY (tenant_id, customer_id, entity_id, id, name) + WITH CLUSTERING ORDER BY (customer_id ASC, entity_id DESC, id DESC, name DESC); diff --git a/dao/src/main/resources/sql/schema.sql b/dao/src/main/resources/sql/schema.sql index 91e77da503..ed3583cef3 100644 --- a/dao/src/main/resources/sql/schema.sql +++ b/dao/src/main/resources/sql/schema.sql @@ -251,3 +251,17 @@ CREATE TABLE IF NOT EXISTS rule_node ( debug_mode boolean, search_text varchar(255) ); + +CREATE TABLE IF NOT EXISTS entity_views ( + 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), + name varchar(255), + keys varchar(255), + ts_begin varchar(255), + ts_end varchar(255), + search_text varchar(255), + additional_info varchar +); diff --git a/dao/src/test/resources/sql/drop-all-tables.sql b/dao/src/test/resources/sql/drop-all-tables.sql index 23b6a56a47..ebc04b3938 100644 --- a/dao/src/test/resources/sql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/drop-all-tables.sql @@ -18,4 +18,5 @@ DROP TABLE IF EXISTS user_credentials; DROP TABLE IF EXISTS widget_type; DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; -DROP TABLE IF EXISTS rule_chain; \ No newline at end of file +DROP TABLE IF EXISTS rule_chain; +DROP TABLE IF EXISTS entity_views; \ No newline at end of file