diff --git a/application/src/main/data/upgrade/1.4.0/schema_update.cql b/application/src/main/data/upgrade/1.4.0/schema_update.cql index c5b656feef..7a63bb3fba 100644 --- a/application/src/main/data/upgrade/1.4.0/schema_update.cql +++ b/application/src/main/data/upgrade/1.4.0/schema_update.cql @@ -87,3 +87,26 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( PRIMARY KEY (( tenant_id ), partition) ) WITH CLUSTERING ORDER BY ( partition ASC ) AND compaction = { 'class' : 'LeveledCompactionStrategy' }; + +DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_tenant_and_search_text; +DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_customer_and_search_text; + +DROP TABLE IF EXISTS thingsboard.dashboard; + +CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( + id timeuuid, + tenant_id timeuuid, + title text, + search_text text, + assigned_customers text, + configuration text, + PRIMARY KEY (id, tenant_id) +); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS + SELECT * + from thingsboard.dashboard + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id ) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); + diff --git a/application/src/main/data/upgrade/1.4.0/schema_update.sql b/application/src/main/data/upgrade/1.4.0/schema_update.sql index b2e3a9720f..c635414e52 100644 --- a/application/src/main/data/upgrade/1.4.0/schema_update.sql +++ b/application/src/main/data/upgrade/1.4.0/schema_update.sql @@ -29,3 +29,13 @@ CREATE TABLE IF NOT EXISTS audit_log ( action_failure_details varchar(1000000) ); +DROP TABLE IF EXISTS dashboard; + +CREATE TABLE IF NOT EXISTS dashboard ( + id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, + configuration varchar(10000000), + assigned_customers varchar(1000000), + search_text varchar(255), + tenant_id varchar(31), + title varchar(255) +); 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 28489ca427..a72f2f384b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -423,7 +423,7 @@ public abstract class BaseController { try { validateId(dashboardId, "Incorrect dashboardId " + dashboardId); Dashboard dashboard = dashboardService.findDashboardById(dashboardId); - checkDashboard(dashboard, true); + checkDashboard(dashboard); return dashboard; } catch (Exception e) { throw handleException(e, false); @@ -435,27 +435,23 @@ public abstract class BaseController { validateId(dashboardId, "Incorrect dashboardId " + dashboardId); DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); SecurityUser authUser = getCurrentUser(); - checkDashboard(dashboardInfo, authUser.getAuthority() != Authority.SYS_ADMIN); + checkDashboard(dashboardInfo); return dashboardInfo; } catch (Exception e) { throw handleException(e, false); } } - private void checkDashboard(DashboardInfo dashboard, boolean checkCustomerId) throws ThingsboardException { + private void checkDashboard(DashboardInfo dashboard) throws ThingsboardException { checkNotNull(dashboard); checkTenantId(dashboard.getTenantId()); SecurityUser authUser = getCurrentUser(); if (authUser.getAuthority() == Authority.CUSTOMER_USER) { - if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { + if (dashboard.getAssignedCustomers() == null || !dashboard.getAssignedCustomers().containsKey(authUser.getCustomerId().toString())) { throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, ThingsboardErrorCode.PERMISSION_DENIED); } } - if (checkCustomerId && - dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { - checkCustomerId(dashboard.getCustomerId()); - } } ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 34320b12df..30d57f3f4d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.id.DashboardId; 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.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.exception.ThingsboardException; @@ -80,7 +82,7 @@ public class DashboardController extends BaseController { Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); logEntityAction(savedDashboard.getId(), savedDashboard, - savedDashboard.getCustomerId(), + null, dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); return savedDashboard; @@ -103,7 +105,7 @@ public class DashboardController extends BaseController { dashboardService.deleteDashboard(dashboardId); logEntityAction(dashboardId, dashboard, - dashboard.getCustomerId(), + null, ActionType.DELETED, null, strDashboardId); } catch (Exception e) { @@ -134,7 +136,7 @@ public class DashboardController extends BaseController { Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); logEntityAction(dashboardId, savedDashboard, - savedDashboard.getCustomerId(), + customerId, ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); @@ -150,23 +152,22 @@ public class DashboardController extends BaseController { } @PreAuthorize("hasAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/customer/dashboard/{dashboardId}", method = RequestMethod.DELETE) + @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) @ResponseBody - public Dashboard unassignDashboardFromCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { + public Dashboard unassignDashboardFromCustomer(@PathVariable("customerId") String strCustomerId, + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { + checkParameter("customerId", strCustomerId); checkParameter(DASHBOARD_ID, strDashboardId); try { + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); + Customer customer = checkCustomerId(customerId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId); - if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { - throw new IncorrectParameterException("Dashboard isn't assigned to any customer!"); - } - - Customer customer = checkCustomerId(dashboard.getCustomerId()); - Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId)); + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); logEntityAction(dashboardId, dashboard, - dashboard.getCustomerId(), + customerId, ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); return savedDashboard; @@ -192,7 +193,7 @@ public class DashboardController extends BaseController { Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); logEntityAction(dashboardId, savedDashboard, - savedDashboard.getCustomerId(), + publicCustomer.getId(), ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); return savedDashboard; @@ -206,6 +207,33 @@ public class DashboardController extends BaseController { } } + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE) + @ResponseBody + public Dashboard unassignDashboardFromPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { + checkParameter(DASHBOARD_ID, strDashboardId); + try { + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); + Dashboard dashboard = checkDashboardId(dashboardId); + Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); + + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, publicCustomer.getId())); + + logEntityAction(dashboardId, dashboard, + publicCustomer.getId(), + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); + + return savedDashboard; + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.DASHBOARD), null, + null, + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId); + + throw handleException(e); + } + } + @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET) @ResponseBody @@ -245,19 +273,20 @@ public class DashboardController extends BaseController { @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) @ResponseBody - public TextPageData getCustomerDashboards( + public TimePageData getCustomerDashboards( @PathVariable("customerId") String strCustomerId, @RequestParam int limit, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String idOffset, - @RequestParam(required = false) String textOffset) throws ThingsboardException { + @RequestParam(required = false) Long startTime, + @RequestParam(required = false) Long endTime, + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, + @RequestParam(required = false) String offset) throws ThingsboardException { checkParameter("customerId", strCustomerId); try { TenantId tenantId = getCurrentUser().getTenantId(); CustomerId customerId = new CustomerId(toUUID(strCustomerId)); checkCustomerId(customerId); - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); + return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get()); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index dacf453605..e3691535bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.thingsboard.server.dao.cassandra.CassandraCluster; import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; +import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.util.NoSqlDao; import org.thingsboard.server.service.install.cql.CQLStatementsParser; import org.thingsboard.server.service.install.cql.CassandraDbHelper; @@ -33,6 +34,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import static org.thingsboard.server.service.install.DatabaseHelper.*; + @Service @NoSqlDao @Profile("install") @@ -40,12 +43,6 @@ import java.util.List; public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; - public static final String DEVICE = "device"; - public static final String TENANT_ID = "tenant_id"; - public static final String CUSTOMER_ID = "customer_id"; - public static final String SEARCH_TEXT = "search_text"; - public static final String ADDITIONAL_INFO = "additional_info"; - public static final String ASSET = "asset"; @Value("${install.data_dir}") private String dataDir; @@ -56,6 +53,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { @Autowired private CassandraInstallCluster installCluster; + @Autowired + private DashboardService dashboardService; + @Override public void upgradeDatabase(String fromVersion) throws Exception { @@ -160,10 +160,32 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { case "1.3.0": break; case "1.3.1": + + cluster.getSession(); + + ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); + + log.info("Dumping dashboards ..."); + Path dashboardsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DASHBOARD, + new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, + new String[]{"", "", "", "", "", "", ""}, + "tb-dashboards"); + log.info("Dashboards dumped."); + + log.info("Updating schema ..."); schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); loadCql(schemaUpdateFile); log.info("Schema updated."); + + log.info("Restoring dashboards ..."); + if (dashboardsDump != null) { + CassandraDbHelper.loadCf(ks, cluster.getSession(), DASHBOARD, + new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump); + DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, false); + Files.deleteIfExists(dashboardsDump); + } + log.info("Dashboards restored."); break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java new file mode 100644 index 0000000000..44f42dd570 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java @@ -0,0 +1,93 @@ +/** + * Copyright © 2016-2017 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.service.install; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.lang3.StringUtils; +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.dao.dashboard.DashboardService; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * Created by igor on 2/27/18. + */ +@Slf4j +public class DatabaseHelper { + + public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); + + public static final String DEVICE = "device"; + public static final String TENANT_ID = "tenant_id"; + public static final String CUSTOMER_ID = "customer_id"; + public static final String SEARCH_TEXT = "search_text"; + public static final String ADDITIONAL_INFO = "additional_info"; + public static final String ASSET = "asset"; + public static final String DASHBOARD = "dashboard"; + public static final String ID = "id"; + public static final String TITLE = "title"; + public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; + public static final String CONFIGURATION = "configuration"; + + public static final ObjectMapper objectMapper = new ObjectMapper(); + + public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception { + String[] columns = new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}; + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withHeader(columns))) { + csvParser.forEach(record -> { + String customerIdString = record.get(CUSTOMER_ID); + String assignedCustomersString = record.get(ASSIGNED_CUSTOMERS); + DashboardId dashboardId = new DashboardId(toUUID(record.get(ID), sql)); + List customerIds = new ArrayList<>(); + if (!StringUtils.isEmpty(assignedCustomersString)) { + try { + JsonNode assignedCustomersJson = objectMapper.readTree(assignedCustomersString); + Map assignedCustomers = objectMapper.treeToValue(assignedCustomersJson, HashMap.class); + assignedCustomers.forEach((strCustomerId, title) -> { + customerIds.add(new CustomerId(UUID.fromString(strCustomerId))); + }); + } catch (IOException e) { + log.error("Unable to parse assigned customers field", e); + } + } + if (!StringUtils.isEmpty(customerIdString)) { + customerIds.add(new CustomerId(toUUID(customerIdString, sql))); + } + for (CustomerId customerId : customerIds) { + dashboardService.assignDashboardToCustomer(dashboardId, customerId); + } + }); + } + } + + private static UUID toUUID(String src, boolean sql) { + if (sql) { + return UUIDConverter.fromString(src); + } else { + return UUID.fromString(src); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 4e81f94419..a102dcbc18 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -339,8 +339,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { JsonNode dashboardJson = objectMapper.readTree(path.toFile()); Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); dashboard.setTenantId(tenantId); - dashboard.setCustomerId(customerId); - dashboardService.saveDashboard(dashboard); + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); + if (customerId != null && !customerId.isNullUid()) { + dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); + } } catch (Exception e) { log.error("Unable to load dashboard from json: [{}]", path.toString()); throw new RuntimeException("Unable to load dashboard from json", e); 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 098b4fe970..e1e1e5f945 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 @@ -17,18 +17,26 @@ package org.thingsboard.server.service.install; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; +import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.util.SqlDao; +import org.thingsboard.server.service.install.cql.CassandraDbHelper; +import org.thingsboard.server.service.install.sql.SqlDbHelper; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.DriverManager; +import static org.thingsboard.server.service.install.DatabaseHelper.*; +import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; + @Service @Profile("install") @Slf4j @@ -49,6 +57,9 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @Value("${spring.datasource.password}") private String dbPassword; + @Autowired + private DashboardService dashboardService; + @Override public void upgradeDatabase(String fromVersion) throws Exception { switch (fromVersion) { @@ -62,13 +73,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Schema updated."); break; case "1.3.1": - log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + + log.info("Dumping dashboards ..."); + Path dashboardsDump = SqlDbHelper.dumpTableIfExists(conn, DASHBOARD, + new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, + new String[]{"", "", "", "", "", "", ""}, + "tb-dashboards"); + log.info("Dashboards dumped."); + + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + log.info("Schema updated."); + + log.info("Restoring dashboards ..."); + if (dashboardsDump != null) { + SqlDbHelper.loadTable(conn, DASHBOARD, + new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump); + DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, true); + Files.deleteIfExists(dashboardsDump); + } + log.info("Dashboards restored."); } - log.info("Schema updated."); break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java index 0a411f65a9..ef4610eaf1 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java +++ b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.install.cql; import com.datastax.driver.core.*; -import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.CSVRecord; @@ -28,9 +27,9 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.*; -public class CassandraDbHelper { +import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; - private static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); +public class CassandraDbHelper { public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { diff --git a/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java new file mode 100644 index 0000000000..fa5175fe89 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/sql/SqlDbHelper.java @@ -0,0 +1,146 @@ +/** + * Copyright © 2016-2017 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.service.install.sql; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; + +/** + * Created by igor on 2/27/18. + */ +@Slf4j +public class SqlDbHelper { + + public static Path dumpTableIfExists(Connection conn, String tableName, + String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { + + DatabaseMetaData metaData = conn.getMetaData(); + ResultSet res = metaData.getTables(null, null, tableName, + new String[] {"TABLE"}); + if (res.next()) { + res.close(); + Path dumpFile = Files.createTempFile(dumpPrefix, null); + Files.deleteIfExists(dumpFile); + try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), CSV_DUMP_FORMAT)) { + try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName)) { + try (ResultSet tableRes = stmt.executeQuery()) { + ResultSetMetaData resMetaData = tableRes.getMetaData(); + Map columnIndexMap = new HashMap<>(); + for (int i = 0; i < resMetaData.getColumnCount(); i++) { + String columnName = resMetaData.getColumnName(i); + columnIndexMap.put(columnName, i); + } + while(tableRes.next()) { + dumpRow(tableRes, columnIndexMap, columns, defaultValues, csvPrinter); + } + } + } + } + return dumpFile; + } else { + return null; + } + } + + public static void loadTable(Connection conn, String tableName, String[] columns, Path sourceFile) throws Exception { + PreparedStatement prepared = conn.prepareStatement(createInsertStatement(tableName, columns)); + prepared.getParameterMetaData(); + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), CSV_DUMP_FORMAT.withHeader(columns))) { + csvParser.forEach(record -> { + try { + for (int i=0;i columnIndexMap, String[] columns, + String[] defaultValues, CSVPrinter csvPrinter) throws Exception { + List record = new ArrayList<>(); + for (int i=0;i columnIndexMap, ResultSet res) { + int index = columnIndexMap.containsKey(column) ? columnIndexMap.get(column) : -1; + if (index > -1) { + String str; + try { + Object obj = res.getObject(index); + if (obj == null) { + str = ""; + } else { + str = obj.toString(); + } + } catch (Exception e) { + str = ""; + } + return str; + } else { + return defaultValue; + } + } + + private static void setColumnValue(int index, String column, + CSVRecord record, PreparedStatement preparedStatement) throws SQLException { + String value = record.get(column); + int type = preparedStatement.getParameterMetaData().getParameterType(index + 1); + preparedStatement.setObject(index + 1, value, type); + } + + private static String createInsertStatement(String tableName, String[] columns) { + StringBuilder insertStatementBuilder = new StringBuilder(); + insertStatementBuilder.append("INSERT INTO ").append(tableName).append(" ("); + for (String column : columns) { + insertStatementBuilder.append(column).append(","); + } + insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); + insertStatementBuilder.append(") VALUES ("); + for (String column : columns) { + insertStatementBuilder.append("?").append(","); + } + insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); + insertStatementBuilder.append(")"); + return insertStatementBuilder.toString(); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index bb064116f1..434862bd9d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.containsString; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.sql.Time; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.model.ModelConstants; import org.junit.After; @@ -82,8 +85,6 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertNotNull(savedDashboard.getId()); Assert.assertTrue(savedDashboard.getCreatedTime() > 0); Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); - Assert.assertNotNull(savedDashboard.getCustomerId()); - Assert.assertEquals(NULL_UUID, savedDashboard.getCustomerId().getId()); Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); savedDashboard.setTitle("My new dashboard"); @@ -136,17 +137,20 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); - Assert.assertEquals(savedCustomer.getId(), assignedDashboard.getCustomerId()); - + + Assert.assertTrue(assignedDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); + Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); - Assert.assertEquals(savedCustomer.getId(), foundDashboard.getCustomerId()); + Assert.assertTrue(foundDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); Dashboard unassignedDashboard = - doDelete("/api/customer/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); - Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDashboard.getCustomerId().getId()); - + doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + + Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); + foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); - Assert.assertEquals(ModelConstants.NULL_UUID, foundDashboard.getCustomerId().getId()); + + Assert.assertTrue(foundDashboard.getAssignedCustomers() == null || foundDashboard.getAssignedCustomers().isEmpty()); } @Test @@ -320,11 +324,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest } List loadedDashboards = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(21); - TextPageData pageData = null; + TimePageLink pageLink = new TimePageLink(21); + TimePageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithTimePageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", + new TypeReference>(){}, pageLink); loadedDashboards.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageData.getNextPageLink(); @@ -336,93 +340,5 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertEquals(dashboards, loadedDashboards); } - - @Test - public void testFindCustomerDashboardsByTitle() throws Exception { - Customer customer = new Customer(); - customer.setTitle("Test customer"); - customer = doPost("/api/customer", customer, Customer.class); - CustomerId customerId = customer.getId(); - - String title1 = "Dashboard title 1"; - List dashboardsTitle1 = new ArrayList<>(); - for (int i=0;i<125;i++) { - Dashboard dashboard = new Dashboard(); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title1+suffix; - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); - dashboard.setTitle(title); - dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - dashboardsTitle1.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); - } - String title2 = "Dashboard title 2"; - List dashboardsTitle2 = new ArrayList<>(); - for (int i=0;i<143;i++) { - Dashboard dashboard = new Dashboard(); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title2+suffix; - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); - dashboard.setTitle(title); - dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - dashboardsTitle2.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); - } - - List loadedDashboardsTitle1 = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(18, title1); - TextPageData pageData = null; - do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); - loadedDashboardsTitle1.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - - Collections.sort(dashboardsTitle1, idComparator); - Collections.sort(loadedDashboardsTitle1, idComparator); - - Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); - - List loadedDashboardsTitle2 = new ArrayList<>(); - pageLink = new TextPageLink(7, title2); - do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); - loadedDashboardsTitle2.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - - Collections.sort(dashboardsTitle2, idComparator); - Collections.sort(loadedDashboardsTitle2, idComparator); - - Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); - - for (DashboardInfo dashboard : loadedDashboardsTitle1) { - doDelete("/api/customer/dashboard/" + dashboard.getId().getId().toString()) - .andExpect(status().isOk()); - } - - pageLink = new TextPageLink(5, title1); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); - Assert.assertFalse(pageData.hasNext()); - Assert.assertEquals(0, pageData.getData().size()); - - for (DashboardInfo dashboard : loadedDashboardsTitle2) { - doDelete("/api/customer/dashboard/" + dashboard.getId().getId().toString()) - .andExpect(status().isOk()); - } - - pageLink = new TextPageLink(9, title2); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); - Assert.assertFalse(pageData.hasNext()); - Assert.assertEquals(0, pageData.getData().size()); - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index 71f2b1308b..fa9f048e2a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -79,8 +79,6 @@ public class Dashboard extends DashboardInfo { StringBuilder builder = new StringBuilder(); builder.append("Dashboard [tenantId="); builder.append(getTenantId()); - builder.append(", customerId="); - builder.append(getCustomerId()); builder.append(", title="); builder.append(getTitle()); builder.append(", configuration="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 15898c6c63..7e18dc6b80 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -20,11 +20,16 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + public class DashboardInfo extends SearchTextBased implements HasName { private TenantId tenantId; - private CustomerId customerId; private String title; + private Map assignedCustomers; public DashboardInfo() { super(); @@ -37,8 +42,8 @@ public class DashboardInfo extends SearchTextBased implements HasNa public DashboardInfo(DashboardInfo dashboardInfo) { super(dashboardInfo); this.tenantId = dashboardInfo.getTenantId(); - this.customerId = dashboardInfo.getCustomerId(); this.title = dashboardInfo.getTitle(); + this.assignedCustomers = dashboardInfo.getAssignedCustomers(); } public TenantId getTenantId() { @@ -49,14 +54,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.tenantId = tenantId; } - public CustomerId getCustomerId() { - return customerId; - } - - public void setCustomerId(CustomerId customerId) { - this.customerId = customerId; - } - public String getTitle() { return title; } @@ -65,6 +62,44 @@ public class DashboardInfo extends SearchTextBased implements HasNa this.title = title; } + public Map getAssignedCustomers() { + return assignedCustomers; + } + + public void setAssignedCustomers(Map assignedCustomers) { + this.assignedCustomers = assignedCustomers; + } + + public boolean addAssignedCustomer(CustomerId customerId, String title) { + if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { + return false; + } else { + if (this.assignedCustomers == null) { + this.assignedCustomers = new HashMap<>(); + } + this.assignedCustomers.put(customerId.toString(), title); + return true; + } + } + + public boolean updateAssignedCustomer(CustomerId customerId, String title) { + if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { + this.assignedCustomers.put(customerId.toString(), title); + return true; + } else { + return false; + } + } + + public boolean removeAssignedCustomer(CustomerId customerId) { + if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { + this.assignedCustomers.remove(customerId.toString()); + return true; + } else { + return false; + } + } + @Override @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { @@ -80,7 +115,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); result = prime * result + ((title == null) ? 0 : title.hashCode()); return result; @@ -95,11 +129,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa if (getClass() != obj.getClass()) return false; DashboardInfo other = (DashboardInfo) obj; - if (customerId == null) { - if (other.customerId != null) - return false; - } else if (!customerId.equals(other.customerId)) - return false; if (tenantId == null) { if (other.tenantId != null) return false; @@ -118,8 +147,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa StringBuilder builder = new StringBuilder(); builder.append("DashboardInfo [tenantId="); builder.append(tenantId); - builder.append(", customerId="); - builder.append(customerId); builder.append(", title="); builder.append(title); builder.append("]"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java index 82798ab792..7c9c5e62fd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.relation; public enum RelationTypeGroup { COMMON, - ALARM + ALARM, + DASHBOARD } diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index f76d654bf9..f118aff7a2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -97,7 +97,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom public Customer saveCustomer(Customer customer) { log.trace("Executing saveCustomer [{}]", customer); customerValidator.validate(customer); - return customerDao.save(customer); + Customer savedCustomer = customerDao.save(customer); + dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId(), savedCustomer.getTitle()); + return savedCustomer; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java index d0651a47c3..5f35913088 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/CassandraDashboardInfoDao.java @@ -15,16 +15,26 @@ */ package org.thingsboard.server.dao.dashboard; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.nosql.DashboardInfoEntity; import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.util.NoSqlDao; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -37,6 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; @NoSqlDao public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao implements DashboardInfoDao { + @Autowired + private RelationDao relationDao; + @Override protected Class getColumnFamilyClass() { return DashboardInfoEntity.class; @@ -59,15 +72,18 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { + public ListenableFuture> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); - List dashboardEntities = findPageWithTextSearch(DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, - Arrays.asList(eq(DASHBOARD_CUSTOMER_ID_PROPERTY, customerId), - eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId)), - pageLink); - log.trace("Found dashboards [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", dashboardEntities, tenantId, customerId, pageLink); - return DaoUtil.convertDataList(dashboardEntities); + ListenableFuture> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); + + return Futures.transform(relations, (AsyncFunction, List>) input -> { + List> dashboardFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + dashboardFutures.add(findByIdAsync(relation.getTo().getId())); + } + return Futures.successfulAsList(dashboardFutures); + }); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java index a26bd1410f..baa020f95f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.dao.dashboard; +import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -44,6 +46,6 @@ public interface DashboardInfoDao extends Dao { * @param pageLink the page link * @return the list of dashboard objects */ - List findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink); + ListenableFuture> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index 74d85445f8..f4af29c88d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java @@ -23,6 +23,10 @@ import org.thingsboard.server.common.data.id.DashboardId; 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.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; + +import java.sql.Time; public interface DashboardService { @@ -38,7 +42,7 @@ public interface DashboardService { Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); - Dashboard unassignDashboardFromCustomer(DashboardId dashboardId); + Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId); void deleteDashboard(DashboardId dashboardId); @@ -46,8 +50,10 @@ public interface DashboardService { void deleteDashboardsByTenantId(TenantId tenantId); - TextPageData findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); + ListenableFuture> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink); void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId); + void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 53fdb731dc..b9fa3abdc9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -15,30 +15,42 @@ */ package org.thingsboard.server.dao.dashboard; +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; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.DashboardInfo; -import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; 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.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; 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.model.ModelConstants; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.service.TimePaginatedRemover; import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.tenant.TenantDao; +import javax.annotation.Nullable; +import java.sql.Time; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -59,7 +71,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private CustomerDao customerDao; - + @Override public Dashboard findDashboardById(DashboardId dashboardId) { log.trace("Executing findDashboardById [{}]", dashboardId); @@ -98,15 +110,59 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { Dashboard dashboard = findDashboardById(dashboardId); - dashboard.setCustomerId(customerId); - return saveDashboard(dashboard); + Customer customer = customerDao.findById(customerId.getId()); + if (customer == null) { + throw new DataValidationException("Can't assign dashboard to non-existent customer!"); + } + if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { + throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); + } + if (dashboard.addAssignedCustomer(customerId, customer.getTitle())) { + try { + createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to create dashboard relation. Customer Id: [{}]", dashboardId, customerId); + throw new RuntimeException(e); + } + return saveDashboard(dashboard); + } else { + return dashboard; + } } @Override - public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId) { + public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { Dashboard dashboard = findDashboardById(dashboardId); - dashboard.setCustomerId(null); - return saveDashboard(dashboard); + if (dashboard.removeAssignedCustomer(customerId)) { + try { + deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); + } catch (ExecutionException | InterruptedException e) { + log.warn("[{}] Failed to delete dashboard relation. Customer Id: [{}]", dashboardId, customerId); + throw new RuntimeException(e); + } + return saveDashboard(dashboard); + } else { + return dashboard; + } + } + + private Dashboard updateAssignedCustomerTitle(DashboardId dashboardId, CustomerId customerId, String customerTitle) { + Dashboard dashboard = findDashboardById(dashboardId); + if (dashboard.updateAssignedCustomer(customerId, customerTitle)) { + return saveDashboard(dashboard); + } else { + return dashboard; + } + } + + private void deleteRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { + log.debug("Deleting Dashboard relation: {}", dashboardRelation); + relationService.deleteRelationAsync(dashboardRelation).get(); + } + + private void createRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { + log.debug("Creating Dashboard relation: {}", dashboardRelation); + relationService.saveRelationAsync(dashboardRelation).get(); } @Override @@ -134,13 +190,20 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } @Override - public TextPageData findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) { + public ListenableFuture> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) { log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Validator.validateId(customerId, "Incorrect customerId " + customerId); Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); - List dashboards = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); - return new TextPageData<>(dashboards, pageLink); + ListenableFuture> dashboards = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); + + return Futures.transform(dashboards, new Function, TimePageData>() { + @Nullable + @Override + public TimePageData apply(@Nullable List dashboards) { + return new TimePageData<>(dashboards, pageLink); + } + }); } @Override @@ -148,9 +211,18 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Validator.validateId(customerId, "Incorrect customerId " + customerId); - new CustomerDashboardsUnassigner(tenantId).removeEntities(customerId); + new CustomerDashboardsUnassigner(tenantId, customerId).removeEntities(customerId); } - + + @Override + public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle) { + log.trace("Executing updateCustomerDashboards, tenantId [{}], customerId [{}], customerTitle [{}]", tenantId, customerId, customerTitle); + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(customerId, "Incorrect customerId " + customerId); + Validator.validateString(customerTitle, "Incorrect customerTitle " + customerTitle); + new CustomerDashboardsUpdater(tenantId, customerId, customerTitle).removeEntities(customerId); + } + private DataValidator dashboardValidator = new DataValidator() { @Override @@ -166,17 +238,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); } } - if (dashboard.getCustomerId() == null) { - dashboard.setCustomerId(new CustomerId(ModelConstants.NULL_UUID)); - } else if (!dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { - Customer customer = customerDao.findById(dashboard.getCustomerId().getId()); - if (customer == null) { - throw new DataValidationException("Can't assign dashboard to non-existent customer!"); - } - if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { - throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); - } - } } }; @@ -194,24 +255,60 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } }; - private class CustomerDashboardsUnassigner extends PaginatedRemover { + private class CustomerDashboardsUnassigner extends TimePaginatedRemover { private TenantId tenantId; + private CustomerId customerId; - CustomerDashboardsUnassigner(TenantId tenantId) { + CustomerDashboardsUnassigner(TenantId tenantId, CustomerId customerId) { this.tenantId = tenantId; + this.customerId = customerId; } @Override - protected List findEntities(CustomerId id, TextPageLink pageLink) { - return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); + protected List findEntities(CustomerId id, TimePageLink pageLink) { + try { + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); + } catch (InterruptedException | ExecutionException e) { + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); + throw new RuntimeException(e); + } } @Override protected void removeEntity(DashboardInfo entity) { - unassignDashboardFromCustomer(new DashboardId(entity.getUuidId())); + unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customerId); } } + private class CustomerDashboardsUpdater extends TimePaginatedRemover { + + private TenantId tenantId; + private CustomerId customerId; + private String customerTitle; + + CustomerDashboardsUpdater(TenantId tenantId, CustomerId customerId, String customerTitle) { + this.tenantId = tenantId; + this.customerId = customerId; + this.customerTitle = customerTitle; + } + + @Override + protected List findEntities(CustomerId id, TimePageLink pageLink) { + try { + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); + } catch (InterruptedException | ExecutionException e) { + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); + throw new RuntimeException(e); + } + } + + @Override + protected void removeEntity(DashboardInfo entity) { + updateAssignedCustomerTitle(new DashboardId(entity.getUuidId()), this.customerId, this.customerTitle); + } + + } + } 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 66e03b0629..c275ad0f6c 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 @@ -266,13 +266,11 @@ public class ModelConstants { */ public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; - public static final String DASHBOARD_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; + public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; - public static final String DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_customer_and_search_text"; - /** * Cassandra plugin metadata constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java index 047a8f269e..8590c2a36f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardEntity.java @@ -19,16 +19,19 @@ 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.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; import lombok.ToString; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.SearchTextEntity; import org.thingsboard.server.dao.model.type.JsonCodec; +import java.util.HashMap; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.*; @@ -36,8 +39,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) @EqualsAndHashCode @ToString +@Slf4j public final class DashboardEntity implements SearchTextEntity { - + + private static final ObjectMapper objectMapper = new ObjectMapper(); + @PartitionKey(value = 0) @Column(name = ID_PROPERTY) private UUID id; @@ -46,16 +52,15 @@ public final class DashboardEntity implements SearchTextEntity { @Column(name = DASHBOARD_TENANT_ID_PROPERTY) private UUID tenantId; - @PartitionKey(value = 2) - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) - private UUID customerId; - @Column(name = DASHBOARD_TITLE_PROPERTY) private String title; @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; - + + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) + private JsonNode assignedCustomers; + @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) private JsonNode configuration; @@ -70,10 +75,10 @@ public final class DashboardEntity implements SearchTextEntity { if (dashboard.getTenantId() != null) { this.tenantId = dashboard.getTenantId().getId(); } - if (dashboard.getCustomerId() != null) { - this.customerId = dashboard.getCustomerId().getId(); - } this.title = dashboard.getTitle(); + if (dashboard.getAssignedCustomers() != null) { + this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); + } this.configuration = dashboard.getConfiguration(); } @@ -93,14 +98,6 @@ public final class DashboardEntity implements SearchTextEntity { this.tenantId = tenantId; } - public UUID getCustomerId() { - return customerId; - } - - public void setCustomerId(UUID customerId) { - this.customerId = customerId; - } - public String getTitle() { return title; } @@ -109,6 +106,14 @@ public final class DashboardEntity implements SearchTextEntity { this.title = title; } + public JsonNode getAssignedCustomers() { + return assignedCustomers; + } + + public void setAssignedCustomers(JsonNode assignedCustomers) { + this.assignedCustomers = assignedCustomers; + } + public JsonNode getConfiguration() { return configuration; } @@ -138,10 +143,14 @@ public final class DashboardEntity implements SearchTextEntity { if (tenantId != null) { dashboard.setTenantId(new TenantId(tenantId)); } - if (customerId != null) { - dashboard.setCustomerId(new CustomerId(customerId)); - } dashboard.setTitle(title); + if (assignedCustomers != null) { + try { + dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); + } catch (JsonProcessingException e) { + log.warn("Unable to parse assigned customers!", e); + } + } dashboard.setConfiguration(configuration); return dashboard; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java index a2a5280a30..609f3bb598 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/DashboardInfoEntity.java @@ -19,14 +19,21 @@ 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.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.EqualsAndHashCode; import lombok.ToString; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.SearchTextEntity; +import org.thingsboard.server.dao.model.type.JsonCodec; +import java.util.HashMap; +import java.util.Set; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.*; @@ -34,8 +41,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) @EqualsAndHashCode @ToString +@Slf4j public class DashboardInfoEntity implements SearchTextEntity { + private static final ObjectMapper objectMapper = new ObjectMapper(); + @PartitionKey(value = 0) @Column(name = ID_PROPERTY) private UUID id; @@ -44,16 +54,15 @@ public class DashboardInfoEntity implements SearchTextEntity { @Column(name = DASHBOARD_TENANT_ID_PROPERTY) private UUID tenantId; - @PartitionKey(value = 2) - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) - private UUID customerId; - @Column(name = DASHBOARD_TITLE_PROPERTY) private String title; @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) + private JsonNode assignedCustomers; + public DashboardInfoEntity() { super(); } @@ -65,10 +74,10 @@ public class DashboardInfoEntity implements SearchTextEntity { if (dashboardInfo.getTenantId() != null) { this.tenantId = dashboardInfo.getTenantId().getId(); } - if (dashboardInfo.getCustomerId() != null) { - this.customerId = dashboardInfo.getCustomerId().getId(); - } this.title = dashboardInfo.getTitle(); + if (dashboardInfo.getAssignedCustomers() != null) { + this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); + } } public UUID getId() { @@ -87,14 +96,6 @@ public class DashboardInfoEntity implements SearchTextEntity { this.tenantId = tenantId; } - public UUID getCustomerId() { - return customerId; - } - - public void setCustomerId(UUID customerId) { - this.customerId = customerId; - } - public String getTitle() { return title; } @@ -103,6 +104,14 @@ public class DashboardInfoEntity implements SearchTextEntity { this.title = title; } + public JsonNode getAssignedCustomers() { + return assignedCustomers; + } + + public void setAssignedCustomers(JsonNode assignedCustomers) { + this.assignedCustomers = assignedCustomers; + } + @Override public String getSearchTextSource() { return getTitle(); @@ -124,10 +133,14 @@ public class DashboardInfoEntity implements SearchTextEntity { if (tenantId != null) { dashboardInfo.setTenantId(new TenantId(tenantId)); } - if (customerId != null) { - dashboardInfo.setCustomerId(new CustomerId(customerId)); - } dashboardInfo.setTitle(title); + if (assignedCustomers != null) { + try { + dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); + } catch (JsonProcessingException e) { + log.warn("Unable to parse assigned customers!", e); + } + } return dashboardInfo; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index bd0e0dc39f..6f7810bf55 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java @@ -16,9 +16,12 @@ package org.thingsboard.server.dao.model.sql; import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.Dashboard; @@ -33,26 +36,33 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import java.util.HashMap; +import java.util.List; +import java.util.Set; @Data +@Slf4j @EqualsAndHashCode(callSuper = true) @Entity @TypeDef(name = "json", typeClass = JsonStringType.class) @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) public final class DashboardEntity extends BaseSqlEntity implements SearchTextEntity { + private static final ObjectMapper objectMapper = new ObjectMapper(); + @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) private String tenantId; - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) - private String customerId; - @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) private String title; @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) private String searchText; + @Type(type = "json") + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) + private JsonNode assignedCustomers; + @Type(type = "json") @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) private JsonNode configuration; @@ -68,10 +78,10 @@ public final class DashboardEntity extends BaseSqlEntity implements S if (dashboard.getTenantId() != null) { this.tenantId = toString(dashboard.getTenantId().getId()); } - if (dashboard.getCustomerId() != null) { - this.customerId = toString(dashboard.getCustomerId().getId()); - } this.title = dashboard.getTitle(); + if (dashboard.getAssignedCustomers() != null) { + this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); + } this.configuration = dashboard.getConfiguration(); } @@ -92,10 +102,14 @@ public final class DashboardEntity extends BaseSqlEntity implements S if (tenantId != null) { dashboard.setTenantId(new TenantId(toUUID(tenantId))); } - if (customerId != null) { - dashboard.setCustomerId(new CustomerId(toUUID(customerId))); - } dashboard.setTitle(title); + if (assignedCustomers != null) { + try { + dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); + } catch (JsonProcessingException e) { + log.warn("Unable to parse assigned customers!", e); + } + } dashboard.setConfiguration(configuration); return dashboard; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index c87e97d2e3..d9b8efe8f4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java @@ -16,8 +16,13 @@ package org.thingsboard.server.dao.model.sql; import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.Type; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -29,25 +34,31 @@ import org.thingsboard.server.dao.model.SearchTextEntity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import java.util.HashMap; +import java.util.Set; @Data +@Slf4j @EqualsAndHashCode(callSuper = true) @Entity @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) public class DashboardInfoEntity extends BaseSqlEntity implements SearchTextEntity { + private static final ObjectMapper objectMapper = new ObjectMapper(); + @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) private String tenantId; - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) - private String customerId; - @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) private String title; @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) private String searchText; + @Type(type = "json") + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) + private JsonNode assignedCustomers; + public DashboardInfoEntity() { super(); } @@ -59,10 +70,10 @@ public class DashboardInfoEntity extends BaseSqlEntity implements if (dashboardInfo.getTenantId() != null) { this.tenantId = toString(dashboardInfo.getTenantId().getId()); } - if (dashboardInfo.getCustomerId() != null) { - this.customerId = toString(dashboardInfo.getCustomerId().getId()); - } this.title = dashboardInfo.getTitle(); + if (dashboardInfo.getAssignedCustomers() != null) { + this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); + } } @Override @@ -86,10 +97,14 @@ public class DashboardInfoEntity extends BaseSqlEntity implements if (tenantId != null) { dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); } - if (customerId != null) { - dashboardInfo.setCustomerId(new CustomerId(toUUID(customerId))); - } dashboardInfo.setTitle(title); + if (assignedCustomers != null) { + try { + dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); + } catch (JsonProcessingException e) { + log.warn("Unable to parse assigned customers!", e); + } + } return dashboardInfo; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java b/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java new file mode 100644 index 0000000000..b52f7475b2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2017 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.service; + +import org.thingsboard.server.common.data.id.IdBased; +import org.thingsboard.server.common.data.page.TimePageLink; + +import java.sql.Time; +import java.util.List; +import java.util.UUID; + +public abstract class TimePaginatedRemover> { + + private static final int DEFAULT_LIMIT = 100; + + public void removeEntities(I id) { + TimePageLink pageLink = new TimePageLink(DEFAULT_LIMIT); + boolean hasNext = true; + while (hasNext) { + List entities = findEntities(id, pageLink); + for (D entity : entities) { + removeEntity(entity); + } + hasNext = entities.size() == pageLink.getLimit(); + if (hasNext) { + int index = entities.size() - 1; + UUID idOffset = entities.get(index).getUuidId(); + pageLink.setIdOffset(idOffset); + } + } + } + + protected abstract List findEntities(I id, TimePageLink pageLink); + + protected abstract void removeEntity(D entity); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index d8a511d9c7..d962977d98 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.page.BasePageLink; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -116,7 +117,7 @@ public class Validator { * @param pageLink the page link * @param errorMessage the error message for exception */ - public static void validatePageLink(TextPageLink pageLink, String errorMessage) { + public static void validatePageLink(BasePageLink pageLink, String errorMessage) { if (pageLink == null || pageLink.getLimit() < 1 || (pageLink.getIdOffset() != null && pageLink.getIdOffset().version() != 1)) { throw new IncorrectParameterException(errorMessage); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java index 7f0ec7abf6..ef7d0a09b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java @@ -39,12 +39,4 @@ public interface DashboardInfoRepository extends CrudRepository :idOffset ORDER BY di.id") - List findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, - @Param("customerId") String customerId, - @Param("searchText") String searchText, - @Param("idOffset") String idOffset, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java index 204189af11..1e343a48f8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java @@ -15,19 +15,31 @@ */ package org.thingsboard.server.dao.sql.dashboard; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; 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.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.dashboard.DashboardInfoDao; import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; +import java.sql.Time; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -37,10 +49,14 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; /** * Created by Valerii Sosliuk on 5/6/2017. */ +@Slf4j @Component @SqlDao public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao implements DashboardInfoDao { + @Autowired + private RelationDao relationDao; + @Autowired private DashboardInfoRepository dashboardInfoRepository; @@ -65,13 +81,17 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { - return DaoUtil.convertDataList(dashboardInfoRepository - .findByTenantIdAndCustomerId( - UUIDConverter.fromTimeUUID(tenantId), - UUIDConverter.fromTimeUUID(customerId), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), - new PageRequest(0, pageLink.getLimit()))); + public ListenableFuture> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { + log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); + + ListenableFuture> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); + + return Futures.transform(relations, (AsyncFunction, List>) input -> { + List> dashboardFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + dashboardFutures.add(findByIdAsync(relation.getTo().getId())); + } + return Futures.successfulAsList(dashboardFutures); + }); } } diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql index bc7884dfbe..dc83045eb4 100644 --- a/dao/src/main/resources/cassandra/schema.cql +++ b/dao/src/main/resources/cassandra/schema.cql @@ -364,26 +364,19 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.widget_type_by_tenant_and_ali CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( id timeuuid, tenant_id timeuuid, - customer_id timeuuid, title text, search_text text, + assigned_customers text, configuration text, - PRIMARY KEY (id, tenant_id, customer_id) + PRIMARY KEY (id, tenant_id) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS SELECT * from thingsboard.dashboard - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id ) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC ); - -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_customer_and_search_text AS - SELECT * - from thingsboard.dashboard - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id ) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( entity_type text, // (DEVICE, CUSTOMER, TENANT) diff --git a/dao/src/main/resources/sql/schema.sql b/dao/src/main/resources/sql/schema.sql index 1f739f8ea3..53ec2235e2 100644 --- a/dao/src/main/resources/sql/schema.sql +++ b/dao/src/main/resources/sql/schema.sql @@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS customer ( CREATE TABLE IF NOT EXISTS dashboard ( id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, configuration varchar(10000000), - customer_id varchar(31), + assigned_customers varchar(1000000), search_text varchar(255), tenant_id varchar(31), title varchar(255) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index f0ed0b3474..0427fb44f7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java @@ -29,13 +29,17 @@ import org.thingsboard.server.common.data.id.CustomerId; 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.page.TimePageData; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import java.io.IOException; +import java.sql.Time; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutionException; public abstract class BaseDashboardServiceTest extends AbstractServiceTest { @@ -68,8 +72,6 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { Assert.assertNotNull(savedDashboard.getId()); Assert.assertTrue(savedDashboard.getCreatedTime() > 0); Assert.assertEquals(dashboard.getTenantId(), savedDashboard.getTenantId()); - Assert.assertNotNull(savedDashboard.getCustomerId()); - Assert.assertEquals(ModelConstants.NULL_UUID, savedDashboard.getCustomerId().getId()); Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); savedDashboard.setTitle("My new dashboard"); @@ -280,7 +282,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { } @Test - public void testFindDashboardsByTenantIdAndCustomerId() { + public void testFindDashboardsByTenantIdAndCustomerId() throws ExecutionException, InterruptedException { Tenant tenant = new Tenant(); tenant.setTitle("Test tenant"); tenant = tenantService.saveTenant(tenant); @@ -303,10 +305,10 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { } List loadedDashboards = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(23); - TextPageData pageData = null; + TimePageLink pageLink = new TimePageLink(23); + TimePageData pageData = null; do { - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); loadedDashboards.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageData.getNextPageLink(); @@ -320,96 +322,12 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { dashboardService.unassignCustomerDashboards(tenantId, customerId); - pageLink = new TextPageLink(42); - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + pageLink = new TimePageLink(42); + pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); tenantService.deleteTenant(tenantId); } - - @Test - public void testFindDashboardsByTenantIdCustomerIdAndTitle() { - - Customer customer = new Customer(); - customer.setTitle("Test customer"); - customer.setTenantId(tenantId); - customer = customerService.saveCustomer(customer); - CustomerId customerId = customer.getId(); - - String title1 = "Dashboard title 1"; - List dashboardsTitle1 = new ArrayList<>(); - for (int i=0;i<124;i++) { - Dashboard dashboard = new Dashboard(); - dashboard.setTenantId(tenantId); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title1+suffix; - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); - dashboard.setTitle(title); - dashboard = dashboardService.saveDashboard(dashboard); - dashboardsTitle1.add(new DashboardInfo(dashboardService.assignDashboardToCustomer(dashboard.getId(), customerId))); - } - String title2 = "Dashboard title 2"; - List dashboardsTitle2 = new ArrayList<>(); - for (int i=0;i<151;i++) { - Dashboard dashboard = new Dashboard(); - dashboard.setTenantId(tenantId); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title2+suffix; - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); - dashboard.setTitle(title); - dashboard = dashboardService.saveDashboard(dashboard); - dashboardsTitle2.add(new DashboardInfo(dashboardService.assignDashboardToCustomer(dashboard.getId(), customerId))); - } - - List loadedDashboardsTitle1 = new ArrayList<>(); - TextPageLink pageLink = new TextPageLink(24, title1); - TextPageData pageData = null; - do { - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); - loadedDashboardsTitle1.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - - Collections.sort(dashboardsTitle1, idComparator); - Collections.sort(loadedDashboardsTitle1, idComparator); - - Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); - - List loadedDashboardsTitle2 = new ArrayList<>(); - pageLink = new TextPageLink(4, title2); - do { - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); - loadedDashboardsTitle2.addAll(pageData.getData()); - if (pageData.hasNext()) { - pageLink = pageData.getNextPageLink(); - } - } while (pageData.hasNext()); - Collections.sort(dashboardsTitle2, idComparator); - Collections.sort(loadedDashboardsTitle2, idComparator); - - Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); - - for (DashboardInfo dashboard : loadedDashboardsTitle1) { - dashboardService.deleteDashboard(dashboard.getId()); - } - - pageLink = new TextPageLink(4, title1); - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); - Assert.assertFalse(pageData.hasNext()); - Assert.assertEquals(0, pageData.getData().size()); - - for (DashboardInfo dashboard : loadedDashboardsTitle2) { - dashboardService.deleteDashboard(dashboard.getId()); - } - - pageLink = new TextPageLink(4, title2); - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); - Assert.assertFalse(pageData.hasNext()); - Assert.assertEquals(0, pageData.getData().size()); - customerService.deleteCustomer(customerId); - } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java index 39bb084c33..3648cceefc 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDaoTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.sql.dashboard; import com.datastax.driver.core.utils.UUIDs; +import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.DashboardInfo; @@ -40,53 +41,26 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { @Test public void testFindDashboardsByTenantId() { UUID tenantId1 = UUIDs.timeBased(); - UUID customerId1 = UUIDs.timeBased(); UUID tenantId2 = UUIDs.timeBased(); - UUID customerId2 = UUIDs.timeBased(); for (int i = 0; i < 20; i++) { - createDashboard(tenantId1, customerId1, i); - createDashboard(tenantId2, customerId2, i * 2); + createDashboard(tenantId1, i); + createDashboard(tenantId2, i * 2); } TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); List dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink1); - assertEquals(15, dashboardInfos1.size()); + Assert.assertEquals(15, dashboardInfos1.size()); TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); List dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink2); - assertEquals(5, dashboardInfos2.size()); + Assert.assertEquals(5, dashboardInfos2.size()); } - @Test - public void testFindDashboardsByTenantAndCustomerId() { - UUID tenantId1 = UUIDs.timeBased(); - UUID customerId1 = UUIDs.timeBased(); - UUID tenantId2 = UUIDs.timeBased(); - UUID customerId2 = UUIDs.timeBased(); - - for (int i = 0; i < 20; i++) { - createDashboard(tenantId1, customerId1, i); - createDashboard(tenantId2, customerId2, i * 2); - } - - TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); - List dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId1, customerId1, pageLink1); - assertEquals(15, dashboardInfos1.size()); - - TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); - List dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId1, customerId1, pageLink2); - assertEquals(5, dashboardInfos2.size()); - } - - private void assertEquals(int i, int size) { - } - - private void createDashboard(UUID tenantId, UUID customerId, int index) { + private void createDashboard(UUID tenantId, int index) { DashboardInfo dashboardInfo = new DashboardInfo(); dashboardInfo.setId(new DashboardId(UUIDs.timeBased())); dashboardInfo.setTenantId(new TenantId(tenantId)); - dashboardInfo.setCustomerId(new CustomerId(customerId)); dashboardInfo.setTitle("DASHBOARD_" + index); dashboardInfoDao.save(dashboardInfo); }