Commit 063a1093e0179ae7840d0f8e3bce3afdeeb08dad
1 parent
b90f0ea0
Ability to assign dashboards to multiple customers - DAO Layer.
Showing
32 changed files
with
830 additions
and
420 deletions
@@ -87,3 +87,26 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( | @@ -87,3 +87,26 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( | ||
87 | PRIMARY KEY (( tenant_id ), partition) | 87 | PRIMARY KEY (( tenant_id ), partition) |
88 | ) WITH CLUSTERING ORDER BY ( partition ASC ) | 88 | ) WITH CLUSTERING ORDER BY ( partition ASC ) |
89 | AND compaction = { 'class' : 'LeveledCompactionStrategy' }; | 89 | AND compaction = { 'class' : 'LeveledCompactionStrategy' }; |
90 | + | ||
91 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_tenant_and_search_text; | ||
92 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_customer_and_search_text; | ||
93 | + | ||
94 | +DROP TABLE IF EXISTS thingsboard.dashboard; | ||
95 | + | ||
96 | +CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( | ||
97 | + id timeuuid, | ||
98 | + tenant_id timeuuid, | ||
99 | + title text, | ||
100 | + search_text text, | ||
101 | + assigned_customers text, | ||
102 | + configuration text, | ||
103 | + PRIMARY KEY (id, tenant_id) | ||
104 | +); | ||
105 | + | ||
106 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS | ||
107 | + SELECT * | ||
108 | + from thingsboard.dashboard | ||
109 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
110 | + PRIMARY KEY ( tenant_id, search_text, id ) | ||
111 | + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); | ||
112 | + |
@@ -29,3 +29,13 @@ CREATE TABLE IF NOT EXISTS audit_log ( | @@ -29,3 +29,13 @@ CREATE TABLE IF NOT EXISTS audit_log ( | ||
29 | action_failure_details varchar(1000000) | 29 | action_failure_details varchar(1000000) |
30 | ); | 30 | ); |
31 | 31 | ||
32 | +DROP TABLE IF EXISTS dashboard; | ||
33 | + | ||
34 | +CREATE TABLE IF NOT EXISTS dashboard ( | ||
35 | + id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, | ||
36 | + configuration varchar(10000000), | ||
37 | + assigned_customers varchar(1000000), | ||
38 | + search_text varchar(255), | ||
39 | + tenant_id varchar(31), | ||
40 | + title varchar(255) | ||
41 | +); |
@@ -423,7 +423,7 @@ public abstract class BaseController { | @@ -423,7 +423,7 @@ public abstract class BaseController { | ||
423 | try { | 423 | try { |
424 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | 424 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
425 | Dashboard dashboard = dashboardService.findDashboardById(dashboardId); | 425 | Dashboard dashboard = dashboardService.findDashboardById(dashboardId); |
426 | - checkDashboard(dashboard, true); | 426 | + checkDashboard(dashboard); |
427 | return dashboard; | 427 | return dashboard; |
428 | } catch (Exception e) { | 428 | } catch (Exception e) { |
429 | throw handleException(e, false); | 429 | throw handleException(e, false); |
@@ -435,27 +435,23 @@ public abstract class BaseController { | @@ -435,27 +435,23 @@ public abstract class BaseController { | ||
435 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | 435 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
436 | DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); | 436 | DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); |
437 | SecurityUser authUser = getCurrentUser(); | 437 | SecurityUser authUser = getCurrentUser(); |
438 | - checkDashboard(dashboardInfo, authUser.getAuthority() != Authority.SYS_ADMIN); | 438 | + checkDashboard(dashboardInfo); |
439 | return dashboardInfo; | 439 | return dashboardInfo; |
440 | } catch (Exception e) { | 440 | } catch (Exception e) { |
441 | throw handleException(e, false); | 441 | throw handleException(e, false); |
442 | } | 442 | } |
443 | } | 443 | } |
444 | 444 | ||
445 | - private void checkDashboard(DashboardInfo dashboard, boolean checkCustomerId) throws ThingsboardException { | 445 | + private void checkDashboard(DashboardInfo dashboard) throws ThingsboardException { |
446 | checkNotNull(dashboard); | 446 | checkNotNull(dashboard); |
447 | checkTenantId(dashboard.getTenantId()); | 447 | checkTenantId(dashboard.getTenantId()); |
448 | SecurityUser authUser = getCurrentUser(); | 448 | SecurityUser authUser = getCurrentUser(); |
449 | if (authUser.getAuthority() == Authority.CUSTOMER_USER) { | 449 | if (authUser.getAuthority() == Authority.CUSTOMER_USER) { |
450 | - if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | 450 | + if (dashboard.getAssignedCustomers() == null || !dashboard.getAssignedCustomers().containsKey(authUser.getCustomerId().toString())) { |
451 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, | 451 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, |
452 | ThingsboardErrorCode.PERMISSION_DENIED); | 452 | ThingsboardErrorCode.PERMISSION_DENIED); |
453 | } | 453 | } |
454 | } | 454 | } |
455 | - if (checkCustomerId && | ||
456 | - dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
457 | - checkCustomerId(dashboard.getCustomerId()); | ||
458 | - } | ||
459 | } | 455 | } |
460 | 456 | ||
461 | ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { | 457 | ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { |
@@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.id.DashboardId; | @@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.id.DashboardId; | ||
28 | import org.thingsboard.server.common.data.id.TenantId; | 28 | import org.thingsboard.server.common.data.id.TenantId; |
29 | import org.thingsboard.server.common.data.page.TextPageData; | 29 | import org.thingsboard.server.common.data.page.TextPageData; |
30 | import org.thingsboard.server.common.data.page.TextPageLink; | 30 | import org.thingsboard.server.common.data.page.TextPageLink; |
31 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
32 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
31 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 33 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
32 | import org.thingsboard.server.dao.model.ModelConstants; | 34 | import org.thingsboard.server.dao.model.ModelConstants; |
33 | import org.thingsboard.server.exception.ThingsboardException; | 35 | import org.thingsboard.server.exception.ThingsboardException; |
@@ -80,7 +82,7 @@ public class DashboardController extends BaseController { | @@ -80,7 +82,7 @@ public class DashboardController extends BaseController { | ||
80 | Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); | 82 | Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); |
81 | 83 | ||
82 | logEntityAction(savedDashboard.getId(), savedDashboard, | 84 | logEntityAction(savedDashboard.getId(), savedDashboard, |
83 | - savedDashboard.getCustomerId(), | 85 | + null, |
84 | dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); | 86 | dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
85 | 87 | ||
86 | return savedDashboard; | 88 | return savedDashboard; |
@@ -103,7 +105,7 @@ public class DashboardController extends BaseController { | @@ -103,7 +105,7 @@ public class DashboardController extends BaseController { | ||
103 | dashboardService.deleteDashboard(dashboardId); | 105 | dashboardService.deleteDashboard(dashboardId); |
104 | 106 | ||
105 | logEntityAction(dashboardId, dashboard, | 107 | logEntityAction(dashboardId, dashboard, |
106 | - dashboard.getCustomerId(), | 108 | + null, |
107 | ActionType.DELETED, null, strDashboardId); | 109 | ActionType.DELETED, null, strDashboardId); |
108 | 110 | ||
109 | } catch (Exception e) { | 111 | } catch (Exception e) { |
@@ -134,7 +136,7 @@ public class DashboardController extends BaseController { | @@ -134,7 +136,7 @@ public class DashboardController extends BaseController { | ||
134 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | 136 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); |
135 | 137 | ||
136 | logEntityAction(dashboardId, savedDashboard, | 138 | logEntityAction(dashboardId, savedDashboard, |
137 | - savedDashboard.getCustomerId(), | 139 | + customerId, |
138 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); | 140 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); |
139 | 141 | ||
140 | 142 | ||
@@ -150,23 +152,22 @@ public class DashboardController extends BaseController { | @@ -150,23 +152,22 @@ public class DashboardController extends BaseController { | ||
150 | } | 152 | } |
151 | 153 | ||
152 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | 154 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
153 | - @RequestMapping(value = "/customer/dashboard/{dashboardId}", method = RequestMethod.DELETE) | 155 | + @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) |
154 | @ResponseBody | 156 | @ResponseBody |
155 | - public Dashboard unassignDashboardFromCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | 157 | + public Dashboard unassignDashboardFromCustomer(@PathVariable("customerId") String strCustomerId, |
158 | + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | ||
159 | + checkParameter("customerId", strCustomerId); | ||
156 | checkParameter(DASHBOARD_ID, strDashboardId); | 160 | checkParameter(DASHBOARD_ID, strDashboardId); |
157 | try { | 161 | try { |
162 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
163 | + Customer customer = checkCustomerId(customerId); | ||
158 | DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | 164 | DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); |
159 | Dashboard dashboard = checkDashboardId(dashboardId); | 165 | Dashboard dashboard = checkDashboardId(dashboardId); |
160 | - if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
161 | - throw new IncorrectParameterException("Dashboard isn't assigned to any customer!"); | ||
162 | - } | ||
163 | - | ||
164 | - Customer customer = checkCustomerId(dashboard.getCustomerId()); | ||
165 | 166 | ||
166 | - Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId)); | 167 | + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); |
167 | 168 | ||
168 | logEntityAction(dashboardId, dashboard, | 169 | logEntityAction(dashboardId, dashboard, |
169 | - dashboard.getCustomerId(), | 170 | + customerId, |
170 | ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); | 171 | ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); |
171 | 172 | ||
172 | return savedDashboard; | 173 | return savedDashboard; |
@@ -192,7 +193,7 @@ public class DashboardController extends BaseController { | @@ -192,7 +193,7 @@ public class DashboardController extends BaseController { | ||
192 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); | 193 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); |
193 | 194 | ||
194 | logEntityAction(dashboardId, savedDashboard, | 195 | logEntityAction(dashboardId, savedDashboard, |
195 | - savedDashboard.getCustomerId(), | 196 | + publicCustomer.getId(), |
196 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); | 197 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); |
197 | 198 | ||
198 | return savedDashboard; | 199 | return savedDashboard; |
@@ -206,6 +207,33 @@ public class DashboardController extends BaseController { | @@ -206,6 +207,33 @@ public class DashboardController extends BaseController { | ||
206 | } | 207 | } |
207 | } | 208 | } |
208 | 209 | ||
210 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
211 | + @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE) | ||
212 | + @ResponseBody | ||
213 | + public Dashboard unassignDashboardFromPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | ||
214 | + checkParameter(DASHBOARD_ID, strDashboardId); | ||
215 | + try { | ||
216 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | ||
217 | + Dashboard dashboard = checkDashboardId(dashboardId); | ||
218 | + Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); | ||
219 | + | ||
220 | + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, publicCustomer.getId())); | ||
221 | + | ||
222 | + logEntityAction(dashboardId, dashboard, | ||
223 | + publicCustomer.getId(), | ||
224 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); | ||
225 | + | ||
226 | + return savedDashboard; | ||
227 | + } catch (Exception e) { | ||
228 | + | ||
229 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | ||
230 | + null, | ||
231 | + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId); | ||
232 | + | ||
233 | + throw handleException(e); | ||
234 | + } | ||
235 | + } | ||
236 | + | ||
209 | @PreAuthorize("hasAuthority('SYS_ADMIN')") | 237 | @PreAuthorize("hasAuthority('SYS_ADMIN')") |
210 | @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET) | 238 | @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET) |
211 | @ResponseBody | 239 | @ResponseBody |
@@ -245,19 +273,20 @@ public class DashboardController extends BaseController { | @@ -245,19 +273,20 @@ public class DashboardController extends BaseController { | ||
245 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 273 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
246 | @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) | 274 | @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) |
247 | @ResponseBody | 275 | @ResponseBody |
248 | - public TextPageData<DashboardInfo> getCustomerDashboards( | 276 | + public TimePageData<DashboardInfo> getCustomerDashboards( |
249 | @PathVariable("customerId") String strCustomerId, | 277 | @PathVariable("customerId") String strCustomerId, |
250 | @RequestParam int limit, | 278 | @RequestParam int limit, |
251 | - @RequestParam(required = false) String textSearch, | ||
252 | - @RequestParam(required = false) String idOffset, | ||
253 | - @RequestParam(required = false) String textOffset) throws ThingsboardException { | 279 | + @RequestParam(required = false) Long startTime, |
280 | + @RequestParam(required = false) Long endTime, | ||
281 | + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, | ||
282 | + @RequestParam(required = false) String offset) throws ThingsboardException { | ||
254 | checkParameter("customerId", strCustomerId); | 283 | checkParameter("customerId", strCustomerId); |
255 | try { | 284 | try { |
256 | TenantId tenantId = getCurrentUser().getTenantId(); | 285 | TenantId tenantId = getCurrentUser().getTenantId(); |
257 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | 286 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
258 | checkCustomerId(customerId); | 287 | checkCustomerId(customerId); |
259 | - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
260 | - return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | 288 | + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); |
289 | + return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get()); | ||
261 | } catch (Exception e) { | 290 | } catch (Exception e) { |
262 | throw handleException(e); | 291 | throw handleException(e); |
263 | } | 292 | } |
@@ -24,6 +24,7 @@ import org.springframework.context.annotation.Profile; | @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Profile; | ||
24 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
25 | import org.thingsboard.server.dao.cassandra.CassandraCluster; | 25 | import org.thingsboard.server.dao.cassandra.CassandraCluster; |
26 | import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; | 26 | import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; |
27 | +import org.thingsboard.server.dao.dashboard.DashboardService; | ||
27 | import org.thingsboard.server.dao.util.NoSqlDao; | 28 | import org.thingsboard.server.dao.util.NoSqlDao; |
28 | import org.thingsboard.server.service.install.cql.CQLStatementsParser; | 29 | import org.thingsboard.server.service.install.cql.CQLStatementsParser; |
29 | import org.thingsboard.server.service.install.cql.CassandraDbHelper; | 30 | import org.thingsboard.server.service.install.cql.CassandraDbHelper; |
@@ -33,6 +34,8 @@ import java.nio.file.Path; | @@ -33,6 +34,8 @@ import java.nio.file.Path; | ||
33 | import java.nio.file.Paths; | 34 | import java.nio.file.Paths; |
34 | import java.util.List; | 35 | import java.util.List; |
35 | 36 | ||
37 | +import static org.thingsboard.server.service.install.DatabaseHelper.*; | ||
38 | + | ||
36 | @Service | 39 | @Service |
37 | @NoSqlDao | 40 | @NoSqlDao |
38 | @Profile("install") | 41 | @Profile("install") |
@@ -40,12 +43,6 @@ import java.util.List; | @@ -40,12 +43,6 @@ import java.util.List; | ||
40 | public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | 43 | public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
41 | 44 | ||
42 | private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; | 45 | private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; |
43 | - public static final String DEVICE = "device"; | ||
44 | - public static final String TENANT_ID = "tenant_id"; | ||
45 | - public static final String CUSTOMER_ID = "customer_id"; | ||
46 | - public static final String SEARCH_TEXT = "search_text"; | ||
47 | - public static final String ADDITIONAL_INFO = "additional_info"; | ||
48 | - public static final String ASSET = "asset"; | ||
49 | 46 | ||
50 | @Value("${install.data_dir}") | 47 | @Value("${install.data_dir}") |
51 | private String dataDir; | 48 | private String dataDir; |
@@ -56,6 +53,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -56,6 +53,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
56 | @Autowired | 53 | @Autowired |
57 | private CassandraInstallCluster installCluster; | 54 | private CassandraInstallCluster installCluster; |
58 | 55 | ||
56 | + @Autowired | ||
57 | + private DashboardService dashboardService; | ||
58 | + | ||
59 | @Override | 59 | @Override |
60 | public void upgradeDatabase(String fromVersion) throws Exception { | 60 | public void upgradeDatabase(String fromVersion) throws Exception { |
61 | 61 | ||
@@ -160,10 +160,32 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -160,10 +160,32 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
160 | case "1.3.0": | 160 | case "1.3.0": |
161 | break; | 161 | break; |
162 | case "1.3.1": | 162 | case "1.3.1": |
163 | + | ||
164 | + cluster.getSession(); | ||
165 | + | ||
166 | + ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); | ||
167 | + | ||
168 | + log.info("Dumping dashboards ..."); | ||
169 | + Path dashboardsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DASHBOARD, | ||
170 | + new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, | ||
171 | + new String[]{"", "", "", "", "", "", ""}, | ||
172 | + "tb-dashboards"); | ||
173 | + log.info("Dashboards dumped."); | ||
174 | + | ||
175 | + | ||
163 | log.info("Updating schema ..."); | 176 | log.info("Updating schema ..."); |
164 | schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); | 177 | schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); |
165 | loadCql(schemaUpdateFile); | 178 | loadCql(schemaUpdateFile); |
166 | log.info("Schema updated."); | 179 | log.info("Schema updated."); |
180 | + | ||
181 | + log.info("Restoring dashboards ..."); | ||
182 | + if (dashboardsDump != null) { | ||
183 | + CassandraDbHelper.loadCf(ks, cluster.getSession(), DASHBOARD, | ||
184 | + new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump); | ||
185 | + DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, false); | ||
186 | + Files.deleteIfExists(dashboardsDump); | ||
187 | + } | ||
188 | + log.info("Dashboards restored."); | ||
167 | break; | 189 | break; |
168 | default: | 190 | default: |
169 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | 191 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.install; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.apache.commons.csv.CSVFormat; | ||
21 | +import org.apache.commons.csv.CSVParser; | ||
22 | +import org.apache.commons.lang3.StringUtils; | ||
23 | +import com.fasterxml.jackson.databind.JsonNode; | ||
24 | +import org.thingsboard.server.common.data.UUIDConverter; | ||
25 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
26 | +import org.thingsboard.server.common.data.id.DashboardId; | ||
27 | +import org.thingsboard.server.dao.dashboard.DashboardService; | ||
28 | + | ||
29 | +import java.io.IOException; | ||
30 | +import java.nio.file.Files; | ||
31 | +import java.nio.file.Path; | ||
32 | +import java.util.*; | ||
33 | + | ||
34 | +/** | ||
35 | + * Created by igor on 2/27/18. | ||
36 | + */ | ||
37 | +@Slf4j | ||
38 | +public class DatabaseHelper { | ||
39 | + | ||
40 | + public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); | ||
41 | + | ||
42 | + public static final String DEVICE = "device"; | ||
43 | + public static final String TENANT_ID = "tenant_id"; | ||
44 | + public static final String CUSTOMER_ID = "customer_id"; | ||
45 | + public static final String SEARCH_TEXT = "search_text"; | ||
46 | + public static final String ADDITIONAL_INFO = "additional_info"; | ||
47 | + public static final String ASSET = "asset"; | ||
48 | + public static final String DASHBOARD = "dashboard"; | ||
49 | + public static final String ID = "id"; | ||
50 | + public static final String TITLE = "title"; | ||
51 | + public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; | ||
52 | + public static final String CONFIGURATION = "configuration"; | ||
53 | + | ||
54 | + public static final ObjectMapper objectMapper = new ObjectMapper(); | ||
55 | + | ||
56 | + public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception { | ||
57 | + String[] columns = new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}; | ||
58 | + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withHeader(columns))) { | ||
59 | + csvParser.forEach(record -> { | ||
60 | + String customerIdString = record.get(CUSTOMER_ID); | ||
61 | + String assignedCustomersString = record.get(ASSIGNED_CUSTOMERS); | ||
62 | + DashboardId dashboardId = new DashboardId(toUUID(record.get(ID), sql)); | ||
63 | + List<CustomerId> customerIds = new ArrayList<>(); | ||
64 | + if (!StringUtils.isEmpty(assignedCustomersString)) { | ||
65 | + try { | ||
66 | + JsonNode assignedCustomersJson = objectMapper.readTree(assignedCustomersString); | ||
67 | + Map<String,String> assignedCustomers = objectMapper.treeToValue(assignedCustomersJson, HashMap.class); | ||
68 | + assignedCustomers.forEach((strCustomerId, title) -> { | ||
69 | + customerIds.add(new CustomerId(UUID.fromString(strCustomerId))); | ||
70 | + }); | ||
71 | + } catch (IOException e) { | ||
72 | + log.error("Unable to parse assigned customers field", e); | ||
73 | + } | ||
74 | + } | ||
75 | + if (!StringUtils.isEmpty(customerIdString)) { | ||
76 | + customerIds.add(new CustomerId(toUUID(customerIdString, sql))); | ||
77 | + } | ||
78 | + for (CustomerId customerId : customerIds) { | ||
79 | + dashboardService.assignDashboardToCustomer(dashboardId, customerId); | ||
80 | + } | ||
81 | + }); | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + private static UUID toUUID(String src, boolean sql) { | ||
86 | + if (sql) { | ||
87 | + return UUIDConverter.fromString(src); | ||
88 | + } else { | ||
89 | + return UUID.fromString(src); | ||
90 | + } | ||
91 | + } | ||
92 | + | ||
93 | +} |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -339,8 +339,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -339,8 +339,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
339 | JsonNode dashboardJson = objectMapper.readTree(path.toFile()); | 339 | JsonNode dashboardJson = objectMapper.readTree(path.toFile()); |
340 | Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); | 340 | Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); |
341 | dashboard.setTenantId(tenantId); | 341 | dashboard.setTenantId(tenantId); |
342 | - dashboard.setCustomerId(customerId); | ||
343 | - dashboardService.saveDashboard(dashboard); | 342 | + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); |
343 | + if (customerId != null && !customerId.isNullUid()) { | ||
344 | + dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); | ||
345 | + } | ||
344 | } catch (Exception e) { | 346 | } catch (Exception e) { |
345 | log.error("Unable to load dashboard from json: [{}]", path.toString()); | 347 | log.error("Unable to load dashboard from json: [{}]", path.toString()); |
346 | throw new RuntimeException("Unable to load dashboard from json", e); | 348 | throw new RuntimeException("Unable to load dashboard from json", e); |
@@ -17,18 +17,26 @@ | @@ -17,18 +17,26 @@ | ||
17 | package org.thingsboard.server.service.install; | 17 | package org.thingsboard.server.service.install; |
18 | 18 | ||
19 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
20 | import org.springframework.beans.factory.annotation.Value; | 21 | import org.springframework.beans.factory.annotation.Value; |
21 | import org.springframework.context.annotation.Profile; | 22 | import org.springframework.context.annotation.Profile; |
22 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
24 | +import org.thingsboard.server.dao.dashboard.DashboardService; | ||
23 | import org.thingsboard.server.dao.util.SqlDao; | 25 | import org.thingsboard.server.dao.util.SqlDao; |
26 | +import org.thingsboard.server.service.install.cql.CassandraDbHelper; | ||
27 | +import org.thingsboard.server.service.install.sql.SqlDbHelper; | ||
24 | 28 | ||
25 | import java.nio.charset.Charset; | 29 | import java.nio.charset.Charset; |
26 | import java.nio.file.Files; | 30 | import java.nio.file.Files; |
27 | import java.nio.file.Path; | 31 | import java.nio.file.Path; |
28 | import java.nio.file.Paths; | 32 | import java.nio.file.Paths; |
29 | import java.sql.Connection; | 33 | import java.sql.Connection; |
34 | +import java.sql.DatabaseMetaData; | ||
30 | import java.sql.DriverManager; | 35 | import java.sql.DriverManager; |
31 | 36 | ||
37 | +import static org.thingsboard.server.service.install.DatabaseHelper.*; | ||
38 | +import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; | ||
39 | + | ||
32 | @Service | 40 | @Service |
33 | @Profile("install") | 41 | @Profile("install") |
34 | @Slf4j | 42 | @Slf4j |
@@ -49,6 +57,9 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -49,6 +57,9 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
49 | @Value("${spring.datasource.password}") | 57 | @Value("${spring.datasource.password}") |
50 | private String dbPassword; | 58 | private String dbPassword; |
51 | 59 | ||
60 | + @Autowired | ||
61 | + private DashboardService dashboardService; | ||
62 | + | ||
52 | @Override | 63 | @Override |
53 | public void upgradeDatabase(String fromVersion) throws Exception { | 64 | public void upgradeDatabase(String fromVersion) throws Exception { |
54 | switch (fromVersion) { | 65 | switch (fromVersion) { |
@@ -62,13 +73,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -62,13 +73,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
62 | log.info("Schema updated."); | 73 | log.info("Schema updated."); |
63 | break; | 74 | break; |
64 | case "1.3.1": | 75 | case "1.3.1": |
65 | - log.info("Updating schema ..."); | ||
66 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); | ||
67 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 76 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
77 | + | ||
78 | + log.info("Dumping dashboards ..."); | ||
79 | + Path dashboardsDump = SqlDbHelper.dumpTableIfExists(conn, DASHBOARD, | ||
80 | + new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, | ||
81 | + new String[]{"", "", "", "", "", "", ""}, | ||
82 | + "tb-dashboards"); | ||
83 | + log.info("Dashboards dumped."); | ||
84 | + | ||
85 | + log.info("Updating schema ..."); | ||
86 | + schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); | ||
68 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); | 87 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); |
69 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | 88 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script |
89 | + log.info("Schema updated."); | ||
90 | + | ||
91 | + log.info("Restoring dashboards ..."); | ||
92 | + if (dashboardsDump != null) { | ||
93 | + SqlDbHelper.loadTable(conn, DASHBOARD, | ||
94 | + new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump); | ||
95 | + DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, true); | ||
96 | + Files.deleteIfExists(dashboardsDump); | ||
97 | + } | ||
98 | + log.info("Dashboards restored."); | ||
70 | } | 99 | } |
71 | - log.info("Schema updated."); | ||
72 | break; | 100 | break; |
73 | default: | 101 | default: |
74 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 102 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
@@ -17,7 +17,6 @@ | @@ -17,7 +17,6 @@ | ||
17 | package org.thingsboard.server.service.install.cql; | 17 | package org.thingsboard.server.service.install.cql; |
18 | 18 | ||
19 | import com.datastax.driver.core.*; | 19 | import com.datastax.driver.core.*; |
20 | -import org.apache.commons.csv.CSVFormat; | ||
21 | import org.apache.commons.csv.CSVParser; | 20 | import org.apache.commons.csv.CSVParser; |
22 | import org.apache.commons.csv.CSVPrinter; | 21 | import org.apache.commons.csv.CSVPrinter; |
23 | import org.apache.commons.csv.CSVRecord; | 22 | import org.apache.commons.csv.CSVRecord; |
@@ -28,9 +27,9 @@ import java.nio.file.Path; | @@ -28,9 +27,9 @@ import java.nio.file.Path; | ||
28 | import java.nio.file.StandardCopyOption; | 27 | import java.nio.file.StandardCopyOption; |
29 | import java.util.*; | 28 | import java.util.*; |
30 | 29 | ||
31 | -public class CassandraDbHelper { | 30 | +import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; |
32 | 31 | ||
33 | - private static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); | 32 | +public class CassandraDbHelper { |
34 | 33 | ||
35 | public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, | 34 | public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, |
36 | String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { | 35 | String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.install.sql; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.apache.commons.csv.CSVParser; | ||
20 | +import org.apache.commons.csv.CSVPrinter; | ||
21 | +import org.apache.commons.csv.CSVRecord; | ||
22 | + | ||
23 | +import java.nio.file.Files; | ||
24 | +import java.nio.file.Path; | ||
25 | +import java.sql.*; | ||
26 | +import java.util.ArrayList; | ||
27 | +import java.util.HashMap; | ||
28 | +import java.util.List; | ||
29 | +import java.util.Map; | ||
30 | + | ||
31 | +import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; | ||
32 | + | ||
33 | +/** | ||
34 | + * Created by igor on 2/27/18. | ||
35 | + */ | ||
36 | +@Slf4j | ||
37 | +public class SqlDbHelper { | ||
38 | + | ||
39 | + public static Path dumpTableIfExists(Connection conn, String tableName, | ||
40 | + String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { | ||
41 | + | ||
42 | + DatabaseMetaData metaData = conn.getMetaData(); | ||
43 | + ResultSet res = metaData.getTables(null, null, tableName, | ||
44 | + new String[] {"TABLE"}); | ||
45 | + if (res.next()) { | ||
46 | + res.close(); | ||
47 | + Path dumpFile = Files.createTempFile(dumpPrefix, null); | ||
48 | + Files.deleteIfExists(dumpFile); | ||
49 | + try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), CSV_DUMP_FORMAT)) { | ||
50 | + try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName)) { | ||
51 | + try (ResultSet tableRes = stmt.executeQuery()) { | ||
52 | + ResultSetMetaData resMetaData = tableRes.getMetaData(); | ||
53 | + Map<String, Integer> columnIndexMap = new HashMap<>(); | ||
54 | + for (int i = 0; i < resMetaData.getColumnCount(); i++) { | ||
55 | + String columnName = resMetaData.getColumnName(i); | ||
56 | + columnIndexMap.put(columnName, i); | ||
57 | + } | ||
58 | + while(tableRes.next()) { | ||
59 | + dumpRow(tableRes, columnIndexMap, columns, defaultValues, csvPrinter); | ||
60 | + } | ||
61 | + } | ||
62 | + } | ||
63 | + } | ||
64 | + return dumpFile; | ||
65 | + } else { | ||
66 | + return null; | ||
67 | + } | ||
68 | + } | ||
69 | + | ||
70 | + public static void loadTable(Connection conn, String tableName, String[] columns, Path sourceFile) throws Exception { | ||
71 | + PreparedStatement prepared = conn.prepareStatement(createInsertStatement(tableName, columns)); | ||
72 | + prepared.getParameterMetaData(); | ||
73 | + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), CSV_DUMP_FORMAT.withHeader(columns))) { | ||
74 | + csvParser.forEach(record -> { | ||
75 | + try { | ||
76 | + for (int i=0;i<columns.length;i++) { | ||
77 | + setColumnValue(i, columns[i], record, prepared); | ||
78 | + } | ||
79 | + prepared.execute(); | ||
80 | + } catch (SQLException e) { | ||
81 | + log.error("Unable to load table record!", e); | ||
82 | + } | ||
83 | + }); | ||
84 | + } | ||
85 | + } | ||
86 | + | ||
87 | + private static void dumpRow(ResultSet res, Map<String, Integer> columnIndexMap, String[] columns, | ||
88 | + String[] defaultValues, CSVPrinter csvPrinter) throws Exception { | ||
89 | + List<String> record = new ArrayList<>(); | ||
90 | + for (int i=0;i<columns.length;i++) { | ||
91 | + String column = columns[i]; | ||
92 | + String defaultValue; | ||
93 | + if (defaultValues != null && i < defaultValues.length) { | ||
94 | + defaultValue = defaultValues[i]; | ||
95 | + } else { | ||
96 | + defaultValue = ""; | ||
97 | + } | ||
98 | + record.add(getColumnValue(column, defaultValue, columnIndexMap, res)); | ||
99 | + } | ||
100 | + csvPrinter.printRecord(record); | ||
101 | + } | ||
102 | + | ||
103 | + private static String getColumnValue(String column, String defaultValue, Map<String, Integer> columnIndexMap, ResultSet res) { | ||
104 | + int index = columnIndexMap.containsKey(column) ? columnIndexMap.get(column) : -1; | ||
105 | + if (index > -1) { | ||
106 | + String str; | ||
107 | + try { | ||
108 | + Object obj = res.getObject(index); | ||
109 | + if (obj == null) { | ||
110 | + str = ""; | ||
111 | + } else { | ||
112 | + str = obj.toString(); | ||
113 | + } | ||
114 | + } catch (Exception e) { | ||
115 | + str = ""; | ||
116 | + } | ||
117 | + return str; | ||
118 | + } else { | ||
119 | + return defaultValue; | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + private static void setColumnValue(int index, String column, | ||
124 | + CSVRecord record, PreparedStatement preparedStatement) throws SQLException { | ||
125 | + String value = record.get(column); | ||
126 | + int type = preparedStatement.getParameterMetaData().getParameterType(index + 1); | ||
127 | + preparedStatement.setObject(index + 1, value, type); | ||
128 | + } | ||
129 | + | ||
130 | + private static String createInsertStatement(String tableName, String[] columns) { | ||
131 | + StringBuilder insertStatementBuilder = new StringBuilder(); | ||
132 | + insertStatementBuilder.append("INSERT INTO ").append(tableName).append(" ("); | ||
133 | + for (String column : columns) { | ||
134 | + insertStatementBuilder.append(column).append(","); | ||
135 | + } | ||
136 | + insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); | ||
137 | + insertStatementBuilder.append(") VALUES ("); | ||
138 | + for (String column : columns) { | ||
139 | + insertStatementBuilder.append("?").append(","); | ||
140 | + } | ||
141 | + insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); | ||
142 | + insertStatementBuilder.append(")"); | ||
143 | + return insertStatementBuilder.toString(); | ||
144 | + } | ||
145 | + | ||
146 | +} |
@@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.containsString; | @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.containsString; | ||
19 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; | 19 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
21 | 21 | ||
22 | +import java.sql.Time; | ||
22 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
23 | import java.util.Collections; | 24 | import java.util.Collections; |
24 | import java.util.List; | 25 | import java.util.List; |
@@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.*; | @@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.*; | ||
29 | import org.thingsboard.server.common.data.id.CustomerId; | 30 | import org.thingsboard.server.common.data.id.CustomerId; |
30 | import org.thingsboard.server.common.data.page.TextPageData; | 31 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | import org.thingsboard.server.common.data.page.TextPageLink; | 32 | import org.thingsboard.server.common.data.page.TextPageLink; |
33 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
34 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
32 | import org.thingsboard.server.common.data.security.Authority; | 35 | import org.thingsboard.server.common.data.security.Authority; |
33 | import org.thingsboard.server.dao.model.ModelConstants; | 36 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | import org.junit.After; | 37 | import org.junit.After; |
@@ -82,8 +85,6 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -82,8 +85,6 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
82 | Assert.assertNotNull(savedDashboard.getId()); | 85 | Assert.assertNotNull(savedDashboard.getId()); |
83 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); | 86 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); |
84 | Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); | 87 | Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); |
85 | - Assert.assertNotNull(savedDashboard.getCustomerId()); | ||
86 | - Assert.assertEquals(NULL_UUID, savedDashboard.getCustomerId().getId()); | ||
87 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); | 88 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); |
88 | 89 | ||
89 | savedDashboard.setTitle("My new dashboard"); | 90 | savedDashboard.setTitle("My new dashboard"); |
@@ -136,17 +137,20 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -136,17 +137,20 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
136 | 137 | ||
137 | Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() | 138 | Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() |
138 | + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | 139 | + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
139 | - Assert.assertEquals(savedCustomer.getId(), assignedDashboard.getCustomerId()); | ||
140 | - | 140 | + |
141 | + Assert.assertTrue(assignedDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); | ||
142 | + | ||
141 | Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | 143 | Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
142 | - Assert.assertEquals(savedCustomer.getId(), foundDashboard.getCustomerId()); | 144 | + Assert.assertTrue(foundDashboard.getAssignedCustomers().containsKey(savedCustomer.getId().toString())); |
143 | 145 | ||
144 | Dashboard unassignedDashboard = | 146 | Dashboard unassignedDashboard = |
145 | - doDelete("/api/customer/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | ||
146 | - Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDashboard.getCustomerId().getId()); | ||
147 | - | 147 | + doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
148 | + | ||
149 | + Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); | ||
150 | + | ||
148 | foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | 151 | foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
149 | - Assert.assertEquals(ModelConstants.NULL_UUID, foundDashboard.getCustomerId().getId()); | 152 | + |
153 | + Assert.assertTrue(foundDashboard.getAssignedCustomers() == null || foundDashboard.getAssignedCustomers().isEmpty()); | ||
150 | } | 154 | } |
151 | 155 | ||
152 | @Test | 156 | @Test |
@@ -320,11 +324,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -320,11 +324,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
320 | } | 324 | } |
321 | 325 | ||
322 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); | 326 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); |
323 | - TextPageLink pageLink = new TextPageLink(21); | ||
324 | - TextPageData<DashboardInfo> pageData = null; | 327 | + TimePageLink pageLink = new TimePageLink(21); |
328 | + TimePageData<DashboardInfo> pageData = null; | ||
325 | do { | 329 | do { |
326 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
327 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | 330 | + pageData = doGetTypedWithTimePageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", |
331 | + new TypeReference<TimePageData<DashboardInfo>>(){}, pageLink); | ||
328 | loadedDashboards.addAll(pageData.getData()); | 332 | loadedDashboards.addAll(pageData.getData()); |
329 | if (pageData.hasNext()) { | 333 | if (pageData.hasNext()) { |
330 | pageLink = pageData.getNextPageLink(); | 334 | pageLink = pageData.getNextPageLink(); |
@@ -336,93 +340,5 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -336,93 +340,5 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
336 | 340 | ||
337 | Assert.assertEquals(dashboards, loadedDashboards); | 341 | Assert.assertEquals(dashboards, loadedDashboards); |
338 | } | 342 | } |
339 | - | ||
340 | - @Test | ||
341 | - public void testFindCustomerDashboardsByTitle() throws Exception { | ||
342 | - Customer customer = new Customer(); | ||
343 | - customer.setTitle("Test customer"); | ||
344 | - customer = doPost("/api/customer", customer, Customer.class); | ||
345 | - CustomerId customerId = customer.getId(); | ||
346 | - | ||
347 | - String title1 = "Dashboard title 1"; | ||
348 | - List<DashboardInfo> dashboardsTitle1 = new ArrayList<>(); | ||
349 | - for (int i=0;i<125;i++) { | ||
350 | - Dashboard dashboard = new Dashboard(); | ||
351 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
352 | - String title = title1+suffix; | ||
353 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
354 | - dashboard.setTitle(title); | ||
355 | - dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); | ||
356 | - dashboardsTitle1.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() | ||
357 | - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); | ||
358 | - } | ||
359 | - String title2 = "Dashboard title 2"; | ||
360 | - List<DashboardInfo> dashboardsTitle2 = new ArrayList<>(); | ||
361 | - for (int i=0;i<143;i++) { | ||
362 | - Dashboard dashboard = new Dashboard(); | ||
363 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
364 | - String title = title2+suffix; | ||
365 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
366 | - dashboard.setTitle(title); | ||
367 | - dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); | ||
368 | - dashboardsTitle2.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() | ||
369 | - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); | ||
370 | - } | ||
371 | - | ||
372 | - List<DashboardInfo> loadedDashboardsTitle1 = new ArrayList<>(); | ||
373 | - TextPageLink pageLink = new TextPageLink(18, title1); | ||
374 | - TextPageData<DashboardInfo> pageData = null; | ||
375 | - do { | ||
376 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
377 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
378 | - loadedDashboardsTitle1.addAll(pageData.getData()); | ||
379 | - if (pageData.hasNext()) { | ||
380 | - pageLink = pageData.getNextPageLink(); | ||
381 | - } | ||
382 | - } while (pageData.hasNext()); | ||
383 | - | ||
384 | - Collections.sort(dashboardsTitle1, idComparator); | ||
385 | - Collections.sort(loadedDashboardsTitle1, idComparator); | ||
386 | - | ||
387 | - Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); | ||
388 | - | ||
389 | - List<DashboardInfo> loadedDashboardsTitle2 = new ArrayList<>(); | ||
390 | - pageLink = new TextPageLink(7, title2); | ||
391 | - do { | ||
392 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
393 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
394 | - loadedDashboardsTitle2.addAll(pageData.getData()); | ||
395 | - if (pageData.hasNext()) { | ||
396 | - pageLink = pageData.getNextPageLink(); | ||
397 | - } | ||
398 | - } while (pageData.hasNext()); | ||
399 | - | ||
400 | - Collections.sort(dashboardsTitle2, idComparator); | ||
401 | - Collections.sort(loadedDashboardsTitle2, idComparator); | ||
402 | - | ||
403 | - Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); | ||
404 | - | ||
405 | - for (DashboardInfo dashboard : loadedDashboardsTitle1) { | ||
406 | - doDelete("/api/customer/dashboard/" + dashboard.getId().getId().toString()) | ||
407 | - .andExpect(status().isOk()); | ||
408 | - } | ||
409 | - | ||
410 | - pageLink = new TextPageLink(5, title1); | ||
411 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
412 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
413 | - Assert.assertFalse(pageData.hasNext()); | ||
414 | - Assert.assertEquals(0, pageData.getData().size()); | ||
415 | - | ||
416 | - for (DashboardInfo dashboard : loadedDashboardsTitle2) { | ||
417 | - doDelete("/api/customer/dashboard/" + dashboard.getId().getId().toString()) | ||
418 | - .andExpect(status().isOk()); | ||
419 | - } | ||
420 | - | ||
421 | - pageLink = new TextPageLink(9, title2); | ||
422 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
423 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
424 | - Assert.assertFalse(pageData.hasNext()); | ||
425 | - Assert.assertEquals(0, pageData.getData().size()); | ||
426 | - } | ||
427 | 343 | ||
428 | } | 344 | } |
@@ -79,8 +79,6 @@ public class Dashboard extends DashboardInfo { | @@ -79,8 +79,6 @@ public class Dashboard extends DashboardInfo { | ||
79 | StringBuilder builder = new StringBuilder(); | 79 | StringBuilder builder = new StringBuilder(); |
80 | builder.append("Dashboard [tenantId="); | 80 | builder.append("Dashboard [tenantId="); |
81 | builder.append(getTenantId()); | 81 | builder.append(getTenantId()); |
82 | - builder.append(", customerId="); | ||
83 | - builder.append(getCustomerId()); | ||
84 | builder.append(", title="); | 82 | builder.append(", title="); |
85 | builder.append(getTitle()); | 83 | builder.append(getTitle()); |
86 | builder.append(", configuration="); | 84 | builder.append(", configuration="); |
@@ -20,11 +20,16 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -20,11 +20,16 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
20 | import org.thingsboard.server.common.data.id.DashboardId; | 20 | import org.thingsboard.server.common.data.id.DashboardId; |
21 | import org.thingsboard.server.common.data.id.TenantId; | 21 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 22 | ||
23 | +import java.util.HashMap; | ||
24 | +import java.util.List; | ||
25 | +import java.util.Map; | ||
26 | +import java.util.Set; | ||
27 | + | ||
23 | public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { | 28 | public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { |
24 | 29 | ||
25 | private TenantId tenantId; | 30 | private TenantId tenantId; |
26 | - private CustomerId customerId; | ||
27 | private String title; | 31 | private String title; |
32 | + private Map<String, String> assignedCustomers; | ||
28 | 33 | ||
29 | public DashboardInfo() { | 34 | public DashboardInfo() { |
30 | super(); | 35 | super(); |
@@ -37,8 +42,8 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -37,8 +42,8 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
37 | public DashboardInfo(DashboardInfo dashboardInfo) { | 42 | public DashboardInfo(DashboardInfo dashboardInfo) { |
38 | super(dashboardInfo); | 43 | super(dashboardInfo); |
39 | this.tenantId = dashboardInfo.getTenantId(); | 44 | this.tenantId = dashboardInfo.getTenantId(); |
40 | - this.customerId = dashboardInfo.getCustomerId(); | ||
41 | this.title = dashboardInfo.getTitle(); | 45 | this.title = dashboardInfo.getTitle(); |
46 | + this.assignedCustomers = dashboardInfo.getAssignedCustomers(); | ||
42 | } | 47 | } |
43 | 48 | ||
44 | public TenantId getTenantId() { | 49 | public TenantId getTenantId() { |
@@ -49,14 +54,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -49,14 +54,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
49 | this.tenantId = tenantId; | 54 | this.tenantId = tenantId; |
50 | } | 55 | } |
51 | 56 | ||
52 | - public CustomerId getCustomerId() { | ||
53 | - return customerId; | ||
54 | - } | ||
55 | - | ||
56 | - public void setCustomerId(CustomerId customerId) { | ||
57 | - this.customerId = customerId; | ||
58 | - } | ||
59 | - | ||
60 | public String getTitle() { | 57 | public String getTitle() { |
61 | return title; | 58 | return title; |
62 | } | 59 | } |
@@ -65,6 +62,44 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -65,6 +62,44 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
65 | this.title = title; | 62 | this.title = title; |
66 | } | 63 | } |
67 | 64 | ||
65 | + public Map<String, String> getAssignedCustomers() { | ||
66 | + return assignedCustomers; | ||
67 | + } | ||
68 | + | ||
69 | + public void setAssignedCustomers(Map<String, String> assignedCustomers) { | ||
70 | + this.assignedCustomers = assignedCustomers; | ||
71 | + } | ||
72 | + | ||
73 | + public boolean addAssignedCustomer(CustomerId customerId, String title) { | ||
74 | + if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { | ||
75 | + return false; | ||
76 | + } else { | ||
77 | + if (this.assignedCustomers == null) { | ||
78 | + this.assignedCustomers = new HashMap<>(); | ||
79 | + } | ||
80 | + this.assignedCustomers.put(customerId.toString(), title); | ||
81 | + return true; | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + public boolean updateAssignedCustomer(CustomerId customerId, String title) { | ||
86 | + if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { | ||
87 | + this.assignedCustomers.put(customerId.toString(), title); | ||
88 | + return true; | ||
89 | + } else { | ||
90 | + return false; | ||
91 | + } | ||
92 | + } | ||
93 | + | ||
94 | + public boolean removeAssignedCustomer(CustomerId customerId) { | ||
95 | + if (this.assignedCustomers != null && this.assignedCustomers.containsKey(customerId.toString())) { | ||
96 | + this.assignedCustomers.remove(customerId.toString()); | ||
97 | + return true; | ||
98 | + } else { | ||
99 | + return false; | ||
100 | + } | ||
101 | + } | ||
102 | + | ||
68 | @Override | 103 | @Override |
69 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) | 104 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) |
70 | public String getName() { | 105 | public String getName() { |
@@ -80,7 +115,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -80,7 +115,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
80 | public int hashCode() { | 115 | public int hashCode() { |
81 | final int prime = 31; | 116 | final int prime = 31; |
82 | int result = super.hashCode(); | 117 | int result = super.hashCode(); |
83 | - result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); | ||
84 | result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); | 118 | result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); |
85 | result = prime * result + ((title == null) ? 0 : title.hashCode()); | 119 | result = prime * result + ((title == null) ? 0 : title.hashCode()); |
86 | return result; | 120 | return result; |
@@ -95,11 +129,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -95,11 +129,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
95 | if (getClass() != obj.getClass()) | 129 | if (getClass() != obj.getClass()) |
96 | return false; | 130 | return false; |
97 | DashboardInfo other = (DashboardInfo) obj; | 131 | DashboardInfo other = (DashboardInfo) obj; |
98 | - if (customerId == null) { | ||
99 | - if (other.customerId != null) | ||
100 | - return false; | ||
101 | - } else if (!customerId.equals(other.customerId)) | ||
102 | - return false; | ||
103 | if (tenantId == null) { | 132 | if (tenantId == null) { |
104 | if (other.tenantId != null) | 133 | if (other.tenantId != null) |
105 | return false; | 134 | return false; |
@@ -118,8 +147,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -118,8 +147,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
118 | StringBuilder builder = new StringBuilder(); | 147 | StringBuilder builder = new StringBuilder(); |
119 | builder.append("DashboardInfo [tenantId="); | 148 | builder.append("DashboardInfo [tenantId="); |
120 | builder.append(tenantId); | 149 | builder.append(tenantId); |
121 | - builder.append(", customerId="); | ||
122 | - builder.append(customerId); | ||
123 | builder.append(", title="); | 150 | builder.append(", title="); |
124 | builder.append(title); | 151 | builder.append(title); |
125 | builder.append("]"); | 152 | builder.append("]"); |
@@ -97,7 +97,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom | @@ -97,7 +97,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom | ||
97 | public Customer saveCustomer(Customer customer) { | 97 | public Customer saveCustomer(Customer customer) { |
98 | log.trace("Executing saveCustomer [{}]", customer); | 98 | log.trace("Executing saveCustomer [{}]", customer); |
99 | customerValidator.validate(customer); | 99 | customerValidator.validate(customer); |
100 | - return customerDao.save(customer); | 100 | + Customer savedCustomer = customerDao.save(customer); |
101 | + dashboardService.updateCustomerDashboards(savedCustomer.getTenantId(), savedCustomer.getId(), savedCustomer.getTitle()); | ||
102 | + return savedCustomer; | ||
101 | } | 103 | } |
102 | 104 | ||
103 | @Override | 105 | @Override |
@@ -15,16 +15,26 @@ | @@ -15,16 +15,26 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.dashboard; | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.AsyncFunction; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
20 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
22 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | import org.springframework.stereotype.Component; | 23 | import org.springframework.stereotype.Component; |
20 | import org.thingsboard.server.common.data.DashboardInfo; | 24 | import org.thingsboard.server.common.data.DashboardInfo; |
25 | +import org.thingsboard.server.common.data.EntityType; | ||
26 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
21 | import org.thingsboard.server.common.data.page.TextPageLink; | 27 | import org.thingsboard.server.common.data.page.TextPageLink; |
28 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
29 | +import org.thingsboard.server.common.data.relation.EntityRelation; | ||
30 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | ||
22 | import org.thingsboard.server.dao.DaoUtil; | 31 | import org.thingsboard.server.dao.DaoUtil; |
23 | import org.thingsboard.server.dao.model.nosql.DashboardInfoEntity; | 32 | import org.thingsboard.server.dao.model.nosql.DashboardInfoEntity; |
24 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; | 33 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; |
34 | +import org.thingsboard.server.dao.relation.RelationDao; | ||
25 | import org.thingsboard.server.dao.util.NoSqlDao; | 35 | import org.thingsboard.server.dao.util.NoSqlDao; |
26 | 36 | ||
27 | -import java.util.Arrays; | 37 | +import java.util.ArrayList; |
28 | import java.util.Collections; | 38 | import java.util.Collections; |
29 | import java.util.List; | 39 | import java.util.List; |
30 | import java.util.UUID; | 40 | import java.util.UUID; |
@@ -37,6 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | @@ -37,6 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | ||
37 | @NoSqlDao | 47 | @NoSqlDao |
38 | public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { | 48 | public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { |
39 | 49 | ||
50 | + @Autowired | ||
51 | + private RelationDao relationDao; | ||
52 | + | ||
40 | @Override | 53 | @Override |
41 | protected Class<DashboardInfoEntity> getColumnFamilyClass() { | 54 | protected Class<DashboardInfoEntity> getColumnFamilyClass() { |
42 | return DashboardInfoEntity.class; | 55 | return DashboardInfoEntity.class; |
@@ -59,15 +72,18 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da | @@ -59,15 +72,18 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da | ||
59 | } | 72 | } |
60 | 73 | ||
61 | @Override | 74 | @Override |
62 | - public List<DashboardInfo> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { | 75 | + public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { |
63 | log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); | 76 | log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); |
64 | - List<DashboardInfoEntity> dashboardEntities = findPageWithTextSearch(DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | ||
65 | - Arrays.asList(eq(DASHBOARD_CUSTOMER_ID_PROPERTY, customerId), | ||
66 | - eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId)), | ||
67 | - pageLink); | ||
68 | 77 | ||
69 | - log.trace("Found dashboards [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", dashboardEntities, tenantId, customerId, pageLink); | ||
70 | - return DaoUtil.convertDataList(dashboardEntities); | 78 | + ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); |
79 | + | ||
80 | + return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<DashboardInfo>>) input -> { | ||
81 | + List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); | ||
82 | + for (EntityRelation relation : input) { | ||
83 | + dashboardFutures.add(findByIdAsync(relation.getTo().getId())); | ||
84 | + } | ||
85 | + return Futures.successfulAsList(dashboardFutures); | ||
86 | + }); | ||
71 | } | 87 | } |
72 | 88 | ||
73 | } | 89 | } |
@@ -15,8 +15,10 @@ | @@ -15,8 +15,10 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.dashboard; | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import org.thingsboard.server.common.data.DashboardInfo; | 19 | import org.thingsboard.server.common.data.DashboardInfo; |
19 | import org.thingsboard.server.common.data.page.TextPageLink; | 20 | import org.thingsboard.server.common.data.page.TextPageLink; |
21 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
20 | import org.thingsboard.server.dao.Dao; | 22 | import org.thingsboard.server.dao.Dao; |
21 | 23 | ||
22 | import java.util.List; | 24 | import java.util.List; |
@@ -44,6 +46,6 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> { | @@ -44,6 +46,6 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> { | ||
44 | * @param pageLink the page link | 46 | * @param pageLink the page link |
45 | * @return the list of dashboard objects | 47 | * @return the list of dashboard objects |
46 | */ | 48 | */ |
47 | - List<DashboardInfo> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink); | 49 | + ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink); |
48 | 50 | ||
49 | } | 51 | } |
@@ -23,6 +23,10 @@ import org.thingsboard.server.common.data.id.DashboardId; | @@ -23,6 +23,10 @@ import org.thingsboard.server.common.data.id.DashboardId; | ||
23 | import org.thingsboard.server.common.data.id.TenantId; | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | import org.thingsboard.server.common.data.page.TextPageData; | 24 | import org.thingsboard.server.common.data.page.TextPageData; |
25 | import org.thingsboard.server.common.data.page.TextPageLink; | 25 | import org.thingsboard.server.common.data.page.TextPageLink; |
26 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
27 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
28 | + | ||
29 | +import java.sql.Time; | ||
26 | 30 | ||
27 | public interface DashboardService { | 31 | public interface DashboardService { |
28 | 32 | ||
@@ -38,7 +42,7 @@ public interface DashboardService { | @@ -38,7 +42,7 @@ public interface DashboardService { | ||
38 | 42 | ||
39 | Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); | 43 | Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); |
40 | 44 | ||
41 | - Dashboard unassignDashboardFromCustomer(DashboardId dashboardId); | 45 | + Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId); |
42 | 46 | ||
43 | void deleteDashboard(DashboardId dashboardId); | 47 | void deleteDashboard(DashboardId dashboardId); |
44 | 48 | ||
@@ -46,8 +50,10 @@ public interface DashboardService { | @@ -46,8 +50,10 @@ public interface DashboardService { | ||
46 | 50 | ||
47 | void deleteDashboardsByTenantId(TenantId tenantId); | 51 | void deleteDashboardsByTenantId(TenantId tenantId); |
48 | 52 | ||
49 | - TextPageData<DashboardInfo> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); | 53 | + ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink); |
50 | 54 | ||
51 | void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId); | 55 | void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId); |
52 | 56 | ||
57 | + void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle); | ||
58 | + | ||
53 | } | 59 | } |
@@ -15,30 +15,42 @@ | @@ -15,30 +15,42 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.dashboard; | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | ||
18 | +import com.google.common.base.Function; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 20 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
20 | import org.apache.commons.lang3.StringUtils; | 22 | import org.apache.commons.lang3.StringUtils; |
21 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
22 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
23 | -import org.thingsboard.server.common.data.Customer; | ||
24 | -import org.thingsboard.server.common.data.Dashboard; | ||
25 | -import org.thingsboard.server.common.data.DashboardInfo; | ||
26 | -import org.thingsboard.server.common.data.Tenant; | 25 | +import org.thingsboard.server.common.data.*; |
26 | +import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
27 | import org.thingsboard.server.common.data.id.CustomerId; | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
28 | import org.thingsboard.server.common.data.id.DashboardId; | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
29 | import org.thingsboard.server.common.data.id.TenantId; | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | import org.thingsboard.server.common.data.page.TextPageData; | 30 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | import org.thingsboard.server.common.data.page.TextPageLink; | 31 | import org.thingsboard.server.common.data.page.TextPageLink; |
32 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
33 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
34 | +import org.thingsboard.server.common.data.relation.EntityRelation; | ||
35 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | ||
32 | import org.thingsboard.server.dao.customer.CustomerDao; | 36 | import org.thingsboard.server.dao.customer.CustomerDao; |
33 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 37 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
34 | import org.thingsboard.server.dao.exception.DataValidationException; | 38 | import org.thingsboard.server.dao.exception.DataValidationException; |
35 | import org.thingsboard.server.dao.model.ModelConstants; | 39 | import org.thingsboard.server.dao.model.ModelConstants; |
40 | +import org.thingsboard.server.dao.relation.RelationDao; | ||
36 | import org.thingsboard.server.dao.service.DataValidator; | 41 | import org.thingsboard.server.dao.service.DataValidator; |
37 | import org.thingsboard.server.dao.service.PaginatedRemover; | 42 | import org.thingsboard.server.dao.service.PaginatedRemover; |
43 | +import org.thingsboard.server.dao.service.TimePaginatedRemover; | ||
38 | import org.thingsboard.server.dao.service.Validator; | 44 | import org.thingsboard.server.dao.service.Validator; |
39 | import org.thingsboard.server.dao.tenant.TenantDao; | 45 | import org.thingsboard.server.dao.tenant.TenantDao; |
40 | 46 | ||
47 | +import javax.annotation.Nullable; | ||
48 | +import java.sql.Time; | ||
49 | +import java.util.ArrayList; | ||
50 | +import java.util.HashSet; | ||
41 | import java.util.List; | 51 | import java.util.List; |
52 | +import java.util.Set; | ||
53 | +import java.util.concurrent.ExecutionException; | ||
42 | 54 | ||
43 | import static org.thingsboard.server.dao.service.Validator.validateId; | 55 | import static org.thingsboard.server.dao.service.Validator.validateId; |
44 | 56 | ||
@@ -59,7 +71,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -59,7 +71,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
59 | 71 | ||
60 | @Autowired | 72 | @Autowired |
61 | private CustomerDao customerDao; | 73 | private CustomerDao customerDao; |
62 | - | 74 | + |
63 | @Override | 75 | @Override |
64 | public Dashboard findDashboardById(DashboardId dashboardId) { | 76 | public Dashboard findDashboardById(DashboardId dashboardId) { |
65 | log.trace("Executing findDashboardById [{}]", dashboardId); | 77 | log.trace("Executing findDashboardById [{}]", dashboardId); |
@@ -98,15 +110,59 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -98,15 +110,59 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
98 | @Override | 110 | @Override |
99 | public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { | 111 | public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { |
100 | Dashboard dashboard = findDashboardById(dashboardId); | 112 | Dashboard dashboard = findDashboardById(dashboardId); |
101 | - dashboard.setCustomerId(customerId); | ||
102 | - return saveDashboard(dashboard); | 113 | + Customer customer = customerDao.findById(customerId.getId()); |
114 | + if (customer == null) { | ||
115 | + throw new DataValidationException("Can't assign dashboard to non-existent customer!"); | ||
116 | + } | ||
117 | + if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { | ||
118 | + throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); | ||
119 | + } | ||
120 | + if (dashboard.addAssignedCustomer(customerId, customer.getTitle())) { | ||
121 | + try { | ||
122 | + createRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); | ||
123 | + } catch (ExecutionException | InterruptedException e) { | ||
124 | + log.warn("[{}] Failed to create dashboard relation. Customer Id: [{}]", dashboardId, customerId); | ||
125 | + throw new RuntimeException(e); | ||
126 | + } | ||
127 | + return saveDashboard(dashboard); | ||
128 | + } else { | ||
129 | + return dashboard; | ||
130 | + } | ||
103 | } | 131 | } |
104 | 132 | ||
105 | @Override | 133 | @Override |
106 | - public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId) { | 134 | + public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { |
107 | Dashboard dashboard = findDashboardById(dashboardId); | 135 | Dashboard dashboard = findDashboardById(dashboardId); |
108 | - dashboard.setCustomerId(null); | ||
109 | - return saveDashboard(dashboard); | 136 | + if (dashboard.removeAssignedCustomer(customerId)) { |
137 | + try { | ||
138 | + deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); | ||
139 | + } catch (ExecutionException | InterruptedException e) { | ||
140 | + log.warn("[{}] Failed to delete dashboard relation. Customer Id: [{}]", dashboardId, customerId); | ||
141 | + throw new RuntimeException(e); | ||
142 | + } | ||
143 | + return saveDashboard(dashboard); | ||
144 | + } else { | ||
145 | + return dashboard; | ||
146 | + } | ||
147 | + } | ||
148 | + | ||
149 | + private Dashboard updateAssignedCustomerTitle(DashboardId dashboardId, CustomerId customerId, String customerTitle) { | ||
150 | + Dashboard dashboard = findDashboardById(dashboardId); | ||
151 | + if (dashboard.updateAssignedCustomer(customerId, customerTitle)) { | ||
152 | + return saveDashboard(dashboard); | ||
153 | + } else { | ||
154 | + return dashboard; | ||
155 | + } | ||
156 | + } | ||
157 | + | ||
158 | + private void deleteRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { | ||
159 | + log.debug("Deleting Dashboard relation: {}", dashboardRelation); | ||
160 | + relationService.deleteRelationAsync(dashboardRelation).get(); | ||
161 | + } | ||
162 | + | ||
163 | + private void createRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { | ||
164 | + log.debug("Creating Dashboard relation: {}", dashboardRelation); | ||
165 | + relationService.saveRelationAsync(dashboardRelation).get(); | ||
110 | } | 166 | } |
111 | 167 | ||
112 | @Override | 168 | @Override |
@@ -134,13 +190,20 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -134,13 +190,20 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
134 | } | 190 | } |
135 | 191 | ||
136 | @Override | 192 | @Override |
137 | - public TextPageData<DashboardInfo> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) { | 193 | + public ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) { |
138 | log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); | 194 | log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); |
139 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | 195 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
140 | Validator.validateId(customerId, "Incorrect customerId " + customerId); | 196 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
141 | Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); | 197 | Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); |
142 | - List<DashboardInfo> dashboards = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); | ||
143 | - return new TextPageData<>(dashboards, pageLink); | 198 | + ListenableFuture<List<DashboardInfo>> dashboards = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); |
199 | + | ||
200 | + return Futures.transform(dashboards, new Function<List<DashboardInfo>, TimePageData<DashboardInfo>>() { | ||
201 | + @Nullable | ||
202 | + @Override | ||
203 | + public TimePageData<DashboardInfo> apply(@Nullable List<DashboardInfo> dashboards) { | ||
204 | + return new TimePageData<>(dashboards, pageLink); | ||
205 | + } | ||
206 | + }); | ||
144 | } | 207 | } |
145 | 208 | ||
146 | @Override | 209 | @Override |
@@ -148,9 +211,18 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -148,9 +211,18 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
148 | log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId); | 211 | log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId); |
149 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | 212 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
150 | Validator.validateId(customerId, "Incorrect customerId " + customerId); | 213 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
151 | - new CustomerDashboardsUnassigner(tenantId).removeEntities(customerId); | 214 | + new CustomerDashboardsUnassigner(tenantId, customerId).removeEntities(customerId); |
152 | } | 215 | } |
153 | - | 216 | + |
217 | + @Override | ||
218 | + public void updateCustomerDashboards(TenantId tenantId, CustomerId customerId, String customerTitle) { | ||
219 | + log.trace("Executing updateCustomerDashboards, tenantId [{}], customerId [{}], customerTitle [{}]", tenantId, customerId, customerTitle); | ||
220 | + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
221 | + Validator.validateId(customerId, "Incorrect customerId " + customerId); | ||
222 | + Validator.validateString(customerTitle, "Incorrect customerTitle " + customerTitle); | ||
223 | + new CustomerDashboardsUpdater(tenantId, customerId, customerTitle).removeEntities(customerId); | ||
224 | + } | ||
225 | + | ||
154 | private DataValidator<Dashboard> dashboardValidator = | 226 | private DataValidator<Dashboard> dashboardValidator = |
155 | new DataValidator<Dashboard>() { | 227 | new DataValidator<Dashboard>() { |
156 | @Override | 228 | @Override |
@@ -166,17 +238,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -166,17 +238,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
166 | throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); | 238 | throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); |
167 | } | 239 | } |
168 | } | 240 | } |
169 | - if (dashboard.getCustomerId() == null) { | ||
170 | - dashboard.setCustomerId(new CustomerId(ModelConstants.NULL_UUID)); | ||
171 | - } else if (!dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
172 | - Customer customer = customerDao.findById(dashboard.getCustomerId().getId()); | ||
173 | - if (customer == null) { | ||
174 | - throw new DataValidationException("Can't assign dashboard to non-existent customer!"); | ||
175 | - } | ||
176 | - if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { | ||
177 | - throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); | ||
178 | - } | ||
179 | - } | ||
180 | } | 241 | } |
181 | }; | 242 | }; |
182 | 243 | ||
@@ -194,24 +255,60 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -194,24 +255,60 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
194 | } | 255 | } |
195 | }; | 256 | }; |
196 | 257 | ||
197 | - private class CustomerDashboardsUnassigner extends PaginatedRemover<CustomerId, DashboardInfo> { | 258 | + private class CustomerDashboardsUnassigner extends TimePaginatedRemover<CustomerId, DashboardInfo> { |
198 | 259 | ||
199 | private TenantId tenantId; | 260 | private TenantId tenantId; |
261 | + private CustomerId customerId; | ||
200 | 262 | ||
201 | - CustomerDashboardsUnassigner(TenantId tenantId) { | 263 | + CustomerDashboardsUnassigner(TenantId tenantId, CustomerId customerId) { |
202 | this.tenantId = tenantId; | 264 | this.tenantId = tenantId; |
265 | + this.customerId = customerId; | ||
203 | } | 266 | } |
204 | 267 | ||
205 | @Override | 268 | @Override |
206 | - protected List<DashboardInfo> findEntities(CustomerId id, TextPageLink pageLink) { | ||
207 | - return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); | 269 | + protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) { |
270 | + try { | ||
271 | + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); | ||
272 | + } catch (InterruptedException | ExecutionException e) { | ||
273 | + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); | ||
274 | + throw new RuntimeException(e); | ||
275 | + } | ||
208 | } | 276 | } |
209 | 277 | ||
210 | @Override | 278 | @Override |
211 | protected void removeEntity(DashboardInfo entity) { | 279 | protected void removeEntity(DashboardInfo entity) { |
212 | - unassignDashboardFromCustomer(new DashboardId(entity.getUuidId())); | 280 | + unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customerId); |
213 | } | 281 | } |
214 | 282 | ||
215 | } | 283 | } |
216 | 284 | ||
285 | + private class CustomerDashboardsUpdater extends TimePaginatedRemover<CustomerId, DashboardInfo> { | ||
286 | + | ||
287 | + private TenantId tenantId; | ||
288 | + private CustomerId customerId; | ||
289 | + private String customerTitle; | ||
290 | + | ||
291 | + CustomerDashboardsUpdater(TenantId tenantId, CustomerId customerId, String customerTitle) { | ||
292 | + this.tenantId = tenantId; | ||
293 | + this.customerId = customerId; | ||
294 | + this.customerTitle = customerTitle; | ||
295 | + } | ||
296 | + | ||
297 | + @Override | ||
298 | + protected List<DashboardInfo> findEntities(CustomerId id, TimePageLink pageLink) { | ||
299 | + try { | ||
300 | + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink).get(); | ||
301 | + } catch (InterruptedException | ExecutionException e) { | ||
302 | + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", tenantId, id); | ||
303 | + throw new RuntimeException(e); | ||
304 | + } | ||
305 | + } | ||
306 | + | ||
307 | + @Override | ||
308 | + protected void removeEntity(DashboardInfo entity) { | ||
309 | + updateAssignedCustomerTitle(new DashboardId(entity.getUuidId()), this.customerId, this.customerTitle); | ||
310 | + } | ||
311 | + | ||
312 | + } | ||
313 | + | ||
217 | } | 314 | } |
@@ -266,13 +266,11 @@ public class ModelConstants { | @@ -266,13 +266,11 @@ public class ModelConstants { | ||
266 | */ | 266 | */ |
267 | public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; | 267 | public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; |
268 | public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; | 268 | public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; |
269 | - public static final String DASHBOARD_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; | ||
270 | public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; | 269 | public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; |
271 | public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; | 270 | public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; |
271 | + public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; | ||
272 | 272 | ||
273 | public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; | 273 | public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; |
274 | - public static final String DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_customer_and_search_text"; | ||
275 | - | ||
276 | 274 | ||
277 | /** | 275 | /** |
278 | * Cassandra plugin metadata constants. | 276 | * Cassandra plugin metadata constants. |
@@ -19,16 +19,19 @@ import com.datastax.driver.core.utils.UUIDs; | @@ -19,16 +19,19 @@ import com.datastax.driver.core.utils.UUIDs; | ||
19 | import com.datastax.driver.mapping.annotations.Column; | 19 | import com.datastax.driver.mapping.annotations.Column; |
20 | import com.datastax.driver.mapping.annotations.PartitionKey; | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | import com.datastax.driver.mapping.annotations.Table; | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
22 | import com.fasterxml.jackson.databind.JsonNode; | 23 | import com.fasterxml.jackson.databind.JsonNode; |
24 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
23 | import lombok.EqualsAndHashCode; | 25 | import lombok.EqualsAndHashCode; |
24 | import lombok.ToString; | 26 | import lombok.ToString; |
27 | +import lombok.extern.slf4j.Slf4j; | ||
25 | import org.thingsboard.server.common.data.Dashboard; | 28 | import org.thingsboard.server.common.data.Dashboard; |
26 | -import org.thingsboard.server.common.data.id.CustomerId; | ||
27 | import org.thingsboard.server.common.data.id.DashboardId; | 29 | import org.thingsboard.server.common.data.id.DashboardId; |
28 | import org.thingsboard.server.common.data.id.TenantId; | 30 | import org.thingsboard.server.common.data.id.TenantId; |
29 | import org.thingsboard.server.dao.model.SearchTextEntity; | 31 | import org.thingsboard.server.dao.model.SearchTextEntity; |
30 | import org.thingsboard.server.dao.model.type.JsonCodec; | 32 | import org.thingsboard.server.dao.model.type.JsonCodec; |
31 | 33 | ||
34 | +import java.util.HashMap; | ||
32 | import java.util.UUID; | 35 | import java.util.UUID; |
33 | 36 | ||
34 | import static org.thingsboard.server.dao.model.ModelConstants.*; | 37 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
@@ -36,8 +39,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | @@ -36,8 +39,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | ||
36 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) | 39 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) |
37 | @EqualsAndHashCode | 40 | @EqualsAndHashCode |
38 | @ToString | 41 | @ToString |
42 | +@Slf4j | ||
39 | public final class DashboardEntity implements SearchTextEntity<Dashboard> { | 43 | public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
40 | - | 44 | + |
45 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
46 | + | ||
41 | @PartitionKey(value = 0) | 47 | @PartitionKey(value = 0) |
42 | @Column(name = ID_PROPERTY) | 48 | @Column(name = ID_PROPERTY) |
43 | private UUID id; | 49 | private UUID id; |
@@ -46,16 +52,15 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -46,16 +52,15 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
46 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) | 52 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) |
47 | private UUID tenantId; | 53 | private UUID tenantId; |
48 | 54 | ||
49 | - @PartitionKey(value = 2) | ||
50 | - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
51 | - private UUID customerId; | ||
52 | - | ||
53 | @Column(name = DASHBOARD_TITLE_PROPERTY) | 55 | @Column(name = DASHBOARD_TITLE_PROPERTY) |
54 | private String title; | 56 | private String title; |
55 | 57 | ||
56 | @Column(name = SEARCH_TEXT_PROPERTY) | 58 | @Column(name = SEARCH_TEXT_PROPERTY) |
57 | private String searchText; | 59 | private String searchText; |
58 | - | 60 | + |
61 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) | ||
62 | + private JsonNode assignedCustomers; | ||
63 | + | ||
59 | @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) | 64 | @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) |
60 | private JsonNode configuration; | 65 | private JsonNode configuration; |
61 | 66 | ||
@@ -70,10 +75,10 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -70,10 +75,10 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
70 | if (dashboard.getTenantId() != null) { | 75 | if (dashboard.getTenantId() != null) { |
71 | this.tenantId = dashboard.getTenantId().getId(); | 76 | this.tenantId = dashboard.getTenantId().getId(); |
72 | } | 77 | } |
73 | - if (dashboard.getCustomerId() != null) { | ||
74 | - this.customerId = dashboard.getCustomerId().getId(); | ||
75 | - } | ||
76 | this.title = dashboard.getTitle(); | 78 | this.title = dashboard.getTitle(); |
79 | + if (dashboard.getAssignedCustomers() != null) { | ||
80 | + this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); | ||
81 | + } | ||
77 | this.configuration = dashboard.getConfiguration(); | 82 | this.configuration = dashboard.getConfiguration(); |
78 | } | 83 | } |
79 | 84 | ||
@@ -93,14 +98,6 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -93,14 +98,6 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
93 | this.tenantId = tenantId; | 98 | this.tenantId = tenantId; |
94 | } | 99 | } |
95 | 100 | ||
96 | - public UUID getCustomerId() { | ||
97 | - return customerId; | ||
98 | - } | ||
99 | - | ||
100 | - public void setCustomerId(UUID customerId) { | ||
101 | - this.customerId = customerId; | ||
102 | - } | ||
103 | - | ||
104 | public String getTitle() { | 101 | public String getTitle() { |
105 | return title; | 102 | return title; |
106 | } | 103 | } |
@@ -109,6 +106,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -109,6 +106,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
109 | this.title = title; | 106 | this.title = title; |
110 | } | 107 | } |
111 | 108 | ||
109 | + public JsonNode getAssignedCustomers() { | ||
110 | + return assignedCustomers; | ||
111 | + } | ||
112 | + | ||
113 | + public void setAssignedCustomers(JsonNode assignedCustomers) { | ||
114 | + this.assignedCustomers = assignedCustomers; | ||
115 | + } | ||
116 | + | ||
112 | public JsonNode getConfiguration() { | 117 | public JsonNode getConfiguration() { |
113 | return configuration; | 118 | return configuration; |
114 | } | 119 | } |
@@ -138,10 +143,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -138,10 +143,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
138 | if (tenantId != null) { | 143 | if (tenantId != null) { |
139 | dashboard.setTenantId(new TenantId(tenantId)); | 144 | dashboard.setTenantId(new TenantId(tenantId)); |
140 | } | 145 | } |
141 | - if (customerId != null) { | ||
142 | - dashboard.setCustomerId(new CustomerId(customerId)); | ||
143 | - } | ||
144 | dashboard.setTitle(title); | 146 | dashboard.setTitle(title); |
147 | + if (assignedCustomers != null) { | ||
148 | + try { | ||
149 | + dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | ||
150 | + } catch (JsonProcessingException e) { | ||
151 | + log.warn("Unable to parse assigned customers!", e); | ||
152 | + } | ||
153 | + } | ||
145 | dashboard.setConfiguration(configuration); | 154 | dashboard.setConfiguration(configuration); |
146 | return dashboard; | 155 | return dashboard; |
147 | } | 156 | } |
@@ -19,14 +19,21 @@ import com.datastax.driver.core.utils.UUIDs; | @@ -19,14 +19,21 @@ import com.datastax.driver.core.utils.UUIDs; | ||
19 | import com.datastax.driver.mapping.annotations.Column; | 19 | import com.datastax.driver.mapping.annotations.Column; |
20 | import com.datastax.driver.mapping.annotations.PartitionKey; | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | import com.datastax.driver.mapping.annotations.Table; | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
23 | +import com.fasterxml.jackson.databind.JsonNode; | ||
24 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
22 | import lombok.EqualsAndHashCode; | 25 | import lombok.EqualsAndHashCode; |
23 | import lombok.ToString; | 26 | import lombok.ToString; |
27 | +import lombok.extern.slf4j.Slf4j; | ||
24 | import org.thingsboard.server.common.data.DashboardInfo; | 28 | import org.thingsboard.server.common.data.DashboardInfo; |
25 | import org.thingsboard.server.common.data.id.CustomerId; | 29 | import org.thingsboard.server.common.data.id.CustomerId; |
26 | import org.thingsboard.server.common.data.id.DashboardId; | 30 | import org.thingsboard.server.common.data.id.DashboardId; |
27 | import org.thingsboard.server.common.data.id.TenantId; | 31 | import org.thingsboard.server.common.data.id.TenantId; |
28 | import org.thingsboard.server.dao.model.SearchTextEntity; | 32 | import org.thingsboard.server.dao.model.SearchTextEntity; |
33 | +import org.thingsboard.server.dao.model.type.JsonCodec; | ||
29 | 34 | ||
35 | +import java.util.HashMap; | ||
36 | +import java.util.Set; | ||
30 | import java.util.UUID; | 37 | import java.util.UUID; |
31 | 38 | ||
32 | import static org.thingsboard.server.dao.model.ModelConstants.*; | 39 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
@@ -34,8 +41,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | @@ -34,8 +41,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | ||
34 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) | 41 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) |
35 | @EqualsAndHashCode | 42 | @EqualsAndHashCode |
36 | @ToString | 43 | @ToString |
44 | +@Slf4j | ||
37 | public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | 45 | public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
38 | 46 | ||
47 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
48 | + | ||
39 | @PartitionKey(value = 0) | 49 | @PartitionKey(value = 0) |
40 | @Column(name = ID_PROPERTY) | 50 | @Column(name = ID_PROPERTY) |
41 | private UUID id; | 51 | private UUID id; |
@@ -44,16 +54,15 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -44,16 +54,15 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
44 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) | 54 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) |
45 | private UUID tenantId; | 55 | private UUID tenantId; |
46 | 56 | ||
47 | - @PartitionKey(value = 2) | ||
48 | - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
49 | - private UUID customerId; | ||
50 | - | ||
51 | @Column(name = DASHBOARD_TITLE_PROPERTY) | 57 | @Column(name = DASHBOARD_TITLE_PROPERTY) |
52 | private String title; | 58 | private String title; |
53 | 59 | ||
54 | @Column(name = SEARCH_TEXT_PROPERTY) | 60 | @Column(name = SEARCH_TEXT_PROPERTY) |
55 | private String searchText; | 61 | private String searchText; |
56 | 62 | ||
63 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY, codec = JsonCodec.class) | ||
64 | + private JsonNode assignedCustomers; | ||
65 | + | ||
57 | public DashboardInfoEntity() { | 66 | public DashboardInfoEntity() { |
58 | super(); | 67 | super(); |
59 | } | 68 | } |
@@ -65,10 +74,10 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -65,10 +74,10 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
65 | if (dashboardInfo.getTenantId() != null) { | 74 | if (dashboardInfo.getTenantId() != null) { |
66 | this.tenantId = dashboardInfo.getTenantId().getId(); | 75 | this.tenantId = dashboardInfo.getTenantId().getId(); |
67 | } | 76 | } |
68 | - if (dashboardInfo.getCustomerId() != null) { | ||
69 | - this.customerId = dashboardInfo.getCustomerId().getId(); | ||
70 | - } | ||
71 | this.title = dashboardInfo.getTitle(); | 77 | this.title = dashboardInfo.getTitle(); |
78 | + if (dashboardInfo.getAssignedCustomers() != null) { | ||
79 | + this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); | ||
80 | + } | ||
72 | } | 81 | } |
73 | 82 | ||
74 | public UUID getId() { | 83 | public UUID getId() { |
@@ -87,14 +96,6 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -87,14 +96,6 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
87 | this.tenantId = tenantId; | 96 | this.tenantId = tenantId; |
88 | } | 97 | } |
89 | 98 | ||
90 | - public UUID getCustomerId() { | ||
91 | - return customerId; | ||
92 | - } | ||
93 | - | ||
94 | - public void setCustomerId(UUID customerId) { | ||
95 | - this.customerId = customerId; | ||
96 | - } | ||
97 | - | ||
98 | public String getTitle() { | 99 | public String getTitle() { |
99 | return title; | 100 | return title; |
100 | } | 101 | } |
@@ -103,6 +104,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -103,6 +104,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
103 | this.title = title; | 104 | this.title = title; |
104 | } | 105 | } |
105 | 106 | ||
107 | + public JsonNode getAssignedCustomers() { | ||
108 | + return assignedCustomers; | ||
109 | + } | ||
110 | + | ||
111 | + public void setAssignedCustomers(JsonNode assignedCustomers) { | ||
112 | + this.assignedCustomers = assignedCustomers; | ||
113 | + } | ||
114 | + | ||
106 | @Override | 115 | @Override |
107 | public String getSearchTextSource() { | 116 | public String getSearchTextSource() { |
108 | return getTitle(); | 117 | return getTitle(); |
@@ -124,10 +133,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -124,10 +133,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
124 | if (tenantId != null) { | 133 | if (tenantId != null) { |
125 | dashboardInfo.setTenantId(new TenantId(tenantId)); | 134 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
126 | } | 135 | } |
127 | - if (customerId != null) { | ||
128 | - dashboardInfo.setCustomerId(new CustomerId(customerId)); | ||
129 | - } | ||
130 | dashboardInfo.setTitle(title); | 136 | dashboardInfo.setTitle(title); |
137 | + if (assignedCustomers != null) { | ||
138 | + try { | ||
139 | + dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | ||
140 | + } catch (JsonProcessingException e) { | ||
141 | + log.warn("Unable to parse assigned customers!", e); | ||
142 | + } | ||
143 | + } | ||
131 | return dashboardInfo; | 144 | return dashboardInfo; |
132 | } | 145 | } |
133 | 146 |
@@ -16,9 +16,12 @@ | @@ -16,9 +16,12 @@ | ||
16 | package org.thingsboard.server.dao.model.sql; | 16 | package org.thingsboard.server.dao.model.sql; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
19 | import com.fasterxml.jackson.databind.JsonNode; | 20 | import com.fasterxml.jackson.databind.JsonNode; |
21 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
20 | import lombok.Data; | 22 | import lombok.Data; |
21 | import lombok.EqualsAndHashCode; | 23 | import lombok.EqualsAndHashCode; |
24 | +import lombok.extern.slf4j.Slf4j; | ||
22 | import org.hibernate.annotations.Type; | 25 | import org.hibernate.annotations.Type; |
23 | import org.hibernate.annotations.TypeDef; | 26 | import org.hibernate.annotations.TypeDef; |
24 | import org.thingsboard.server.common.data.Dashboard; | 27 | import org.thingsboard.server.common.data.Dashboard; |
@@ -33,20 +36,23 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; | @@ -33,20 +36,23 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; | ||
33 | import javax.persistence.Column; | 36 | import javax.persistence.Column; |
34 | import javax.persistence.Entity; | 37 | import javax.persistence.Entity; |
35 | import javax.persistence.Table; | 38 | import javax.persistence.Table; |
39 | +import java.util.HashMap; | ||
40 | +import java.util.List; | ||
41 | +import java.util.Set; | ||
36 | 42 | ||
37 | @Data | 43 | @Data |
44 | +@Slf4j | ||
38 | @EqualsAndHashCode(callSuper = true) | 45 | @EqualsAndHashCode(callSuper = true) |
39 | @Entity | 46 | @Entity |
40 | @TypeDef(name = "json", typeClass = JsonStringType.class) | 47 | @TypeDef(name = "json", typeClass = JsonStringType.class) |
41 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) | 48 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) |
42 | public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> { | 49 | public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> { |
43 | 50 | ||
51 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
52 | + | ||
44 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) | 53 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
45 | private String tenantId; | 54 | private String tenantId; |
46 | 55 | ||
47 | - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
48 | - private String customerId; | ||
49 | - | ||
50 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) | 56 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) |
51 | private String title; | 57 | private String title; |
52 | 58 | ||
@@ -54,6 +60,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | @@ -54,6 +60,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | ||
54 | private String searchText; | 60 | private String searchText; |
55 | 61 | ||
56 | @Type(type = "json") | 62 | @Type(type = "json") |
63 | + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | ||
64 | + private JsonNode assignedCustomers; | ||
65 | + | ||
66 | + @Type(type = "json") | ||
57 | @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) | 67 | @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) |
58 | private JsonNode configuration; | 68 | private JsonNode configuration; |
59 | 69 | ||
@@ -68,10 +78,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | @@ -68,10 +78,10 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | ||
68 | if (dashboard.getTenantId() != null) { | 78 | if (dashboard.getTenantId() != null) { |
69 | this.tenantId = toString(dashboard.getTenantId().getId()); | 79 | this.tenantId = toString(dashboard.getTenantId().getId()); |
70 | } | 80 | } |
71 | - if (dashboard.getCustomerId() != null) { | ||
72 | - this.customerId = toString(dashboard.getCustomerId().getId()); | ||
73 | - } | ||
74 | this.title = dashboard.getTitle(); | 81 | this.title = dashboard.getTitle(); |
82 | + if (dashboard.getAssignedCustomers() != null) { | ||
83 | + this.assignedCustomers = objectMapper.valueToTree(dashboard.getAssignedCustomers()); | ||
84 | + } | ||
75 | this.configuration = dashboard.getConfiguration(); | 85 | this.configuration = dashboard.getConfiguration(); |
76 | } | 86 | } |
77 | 87 | ||
@@ -92,10 +102,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | @@ -92,10 +102,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | ||
92 | if (tenantId != null) { | 102 | if (tenantId != null) { |
93 | dashboard.setTenantId(new TenantId(toUUID(tenantId))); | 103 | dashboard.setTenantId(new TenantId(toUUID(tenantId))); |
94 | } | 104 | } |
95 | - if (customerId != null) { | ||
96 | - dashboard.setCustomerId(new CustomerId(toUUID(customerId))); | ||
97 | - } | ||
98 | dashboard.setTitle(title); | 105 | dashboard.setTitle(title); |
106 | + if (assignedCustomers != null) { | ||
107 | + try { | ||
108 | + dashboard.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | ||
109 | + } catch (JsonProcessingException e) { | ||
110 | + log.warn("Unable to parse assigned customers!", e); | ||
111 | + } | ||
112 | + } | ||
99 | dashboard.setConfiguration(configuration); | 113 | dashboard.setConfiguration(configuration); |
100 | return dashboard; | 114 | return dashboard; |
101 | } | 115 | } |
@@ -16,8 +16,13 @@ | @@ -16,8 +16,13 @@ | ||
16 | package org.thingsboard.server.dao.model.sql; | 16 | package org.thingsboard.server.dao.model.sql; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
20 | +import com.fasterxml.jackson.databind.JsonNode; | ||
21 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | import lombok.Data; | 22 | import lombok.Data; |
20 | import lombok.EqualsAndHashCode; | 23 | import lombok.EqualsAndHashCode; |
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.hibernate.annotations.Type; | ||
21 | import org.thingsboard.server.common.data.DashboardInfo; | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
22 | import org.thingsboard.server.common.data.id.CustomerId; | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
23 | import org.thingsboard.server.common.data.id.DashboardId; | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
@@ -29,25 +34,31 @@ import org.thingsboard.server.dao.model.SearchTextEntity; | @@ -29,25 +34,31 @@ import org.thingsboard.server.dao.model.SearchTextEntity; | ||
29 | import javax.persistence.Column; | 34 | import javax.persistence.Column; |
30 | import javax.persistence.Entity; | 35 | import javax.persistence.Entity; |
31 | import javax.persistence.Table; | 36 | import javax.persistence.Table; |
37 | +import java.util.HashMap; | ||
38 | +import java.util.Set; | ||
32 | 39 | ||
33 | @Data | 40 | @Data |
41 | +@Slf4j | ||
34 | @EqualsAndHashCode(callSuper = true) | 42 | @EqualsAndHashCode(callSuper = true) |
35 | @Entity | 43 | @Entity |
36 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) | 44 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) |
37 | public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { | 45 | public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { |
38 | 46 | ||
47 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
48 | + | ||
39 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) | 49 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
40 | private String tenantId; | 50 | private String tenantId; |
41 | 51 | ||
42 | - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
43 | - private String customerId; | ||
44 | - | ||
45 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) | 52 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) |
46 | private String title; | 53 | private String title; |
47 | 54 | ||
48 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) | 55 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
49 | private String searchText; | 56 | private String searchText; |
50 | 57 | ||
58 | + @Type(type = "json") | ||
59 | + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | ||
60 | + private JsonNode assignedCustomers; | ||
61 | + | ||
51 | public DashboardInfoEntity() { | 62 | public DashboardInfoEntity() { |
52 | super(); | 63 | super(); |
53 | } | 64 | } |
@@ -59,10 +70,10 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | @@ -59,10 +70,10 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | ||
59 | if (dashboardInfo.getTenantId() != null) { | 70 | if (dashboardInfo.getTenantId() != null) { |
60 | this.tenantId = toString(dashboardInfo.getTenantId().getId()); | 71 | this.tenantId = toString(dashboardInfo.getTenantId().getId()); |
61 | } | 72 | } |
62 | - if (dashboardInfo.getCustomerId() != null) { | ||
63 | - this.customerId = toString(dashboardInfo.getCustomerId().getId()); | ||
64 | - } | ||
65 | this.title = dashboardInfo.getTitle(); | 73 | this.title = dashboardInfo.getTitle(); |
74 | + if (dashboardInfo.getAssignedCustomers() != null) { | ||
75 | + this.assignedCustomers = objectMapper.valueToTree(dashboardInfo.getAssignedCustomers()); | ||
76 | + } | ||
66 | } | 77 | } |
67 | 78 | ||
68 | @Override | 79 | @Override |
@@ -86,10 +97,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | @@ -86,10 +97,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | ||
86 | if (tenantId != null) { | 97 | if (tenantId != null) { |
87 | dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); | 98 | dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); |
88 | } | 99 | } |
89 | - if (customerId != null) { | ||
90 | - dashboardInfo.setCustomerId(new CustomerId(toUUID(customerId))); | ||
91 | - } | ||
92 | dashboardInfo.setTitle(title); | 100 | dashboardInfo.setTitle(title); |
101 | + if (assignedCustomers != null) { | ||
102 | + try { | ||
103 | + dashboardInfo.setAssignedCustomers(objectMapper.treeToValue(assignedCustomers, HashMap.class)); | ||
104 | + } catch (JsonProcessingException e) { | ||
105 | + log.warn("Unable to parse assigned customers!", e); | ||
106 | + } | ||
107 | + } | ||
93 | return dashboardInfo; | 108 | return dashboardInfo; |
94 | } | 109 | } |
95 | 110 |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.service; | ||
17 | + | ||
18 | +import org.thingsboard.server.common.data.id.IdBased; | ||
19 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
20 | + | ||
21 | +import java.sql.Time; | ||
22 | +import java.util.List; | ||
23 | +import java.util.UUID; | ||
24 | + | ||
25 | +public abstract class TimePaginatedRemover<I, D extends IdBased<?>> { | ||
26 | + | ||
27 | + private static final int DEFAULT_LIMIT = 100; | ||
28 | + | ||
29 | + public void removeEntities(I id) { | ||
30 | + TimePageLink pageLink = new TimePageLink(DEFAULT_LIMIT); | ||
31 | + boolean hasNext = true; | ||
32 | + while (hasNext) { | ||
33 | + List<D> entities = findEntities(id, pageLink); | ||
34 | + for (D entity : entities) { | ||
35 | + removeEntity(entity); | ||
36 | + } | ||
37 | + hasNext = entities.size() == pageLink.getLimit(); | ||
38 | + if (hasNext) { | ||
39 | + int index = entities.size() - 1; | ||
40 | + UUID idOffset = entities.get(index).getUuidId(); | ||
41 | + pageLink.setIdOffset(idOffset); | ||
42 | + } | ||
43 | + } | ||
44 | + } | ||
45 | + | ||
46 | + protected abstract List<D> findEntities(I id, TimePageLink pageLink); | ||
47 | + | ||
48 | + protected abstract void removeEntity(D entity); | ||
49 | + | ||
50 | +} |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; | ||
17 | 17 | ||
18 | import org.thingsboard.server.common.data.id.EntityId; | 18 | import org.thingsboard.server.common.data.id.EntityId; |
19 | import org.thingsboard.server.common.data.id.UUIDBased; | 19 | import org.thingsboard.server.common.data.id.UUIDBased; |
20 | +import org.thingsboard.server.common.data.page.BasePageLink; | ||
20 | import org.thingsboard.server.common.data.page.TextPageLink; | 21 | import org.thingsboard.server.common.data.page.TextPageLink; |
21 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 22 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
22 | 23 | ||
@@ -116,7 +117,7 @@ public class Validator { | @@ -116,7 +117,7 @@ public class Validator { | ||
116 | * @param pageLink the page link | 117 | * @param pageLink the page link |
117 | * @param errorMessage the error message for exception | 118 | * @param errorMessage the error message for exception |
118 | */ | 119 | */ |
119 | - public static void validatePageLink(TextPageLink pageLink, String errorMessage) { | 120 | + public static void validatePageLink(BasePageLink pageLink, String errorMessage) { |
120 | if (pageLink == null || pageLink.getLimit() < 1 || (pageLink.getIdOffset() != null && pageLink.getIdOffset().version() != 1)) { | 121 | if (pageLink == null || pageLink.getLimit() < 1 || (pageLink.getIdOffset() != null && pageLink.getIdOffset().version() != 1)) { |
121 | throw new IncorrectParameterException(errorMessage); | 122 | throw new IncorrectParameterException(errorMessage); |
122 | } | 123 | } |
@@ -39,12 +39,4 @@ public interface DashboardInfoRepository extends CrudRepository<DashboardInfoEnt | @@ -39,12 +39,4 @@ public interface DashboardInfoRepository extends CrudRepository<DashboardInfoEnt | ||
39 | @Param("idOffset") String idOffset, | 39 | @Param("idOffset") String idOffset, |
40 | Pageable pageable); | 40 | Pageable pageable); |
41 | 41 | ||
42 | - @Query("SELECT di FROM DashboardInfoEntity di WHERE di.tenantId = :tenantId " + | ||
43 | - "AND di.customerId = :customerId AND LOWER(di.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + | ||
44 | - "AND di.id > :idOffset ORDER BY di.id") | ||
45 | - List<DashboardInfoEntity> findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, | ||
46 | - @Param("customerId") String customerId, | ||
47 | - @Param("searchText") String searchText, | ||
48 | - @Param("idOffset") String idOffset, | ||
49 | - Pageable pageable); | ||
50 | } | 42 | } |
@@ -15,19 +15,31 @@ | @@ -15,19 +15,31 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.sql.dashboard; | 16 | package org.thingsboard.server.dao.sql.dashboard; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.AsyncFunction; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
20 | +import com.google.common.util.concurrent.ListenableFuture; | ||
21 | +import lombok.extern.slf4j.Slf4j; | ||
18 | import org.springframework.beans.factory.annotation.Autowired; | 22 | import org.springframework.beans.factory.annotation.Autowired; |
19 | import org.springframework.data.domain.PageRequest; | 23 | import org.springframework.data.domain.PageRequest; |
20 | import org.springframework.data.repository.CrudRepository; | 24 | import org.springframework.data.repository.CrudRepository; |
21 | import org.springframework.stereotype.Component; | 25 | import org.springframework.stereotype.Component; |
22 | import org.thingsboard.server.common.data.DashboardInfo; | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
27 | +import org.thingsboard.server.common.data.EntityType; | ||
23 | import org.thingsboard.server.common.data.UUIDConverter; | 28 | import org.thingsboard.server.common.data.UUIDConverter; |
29 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
24 | import org.thingsboard.server.common.data.page.TextPageLink; | 30 | import org.thingsboard.server.common.data.page.TextPageLink; |
31 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
32 | +import org.thingsboard.server.common.data.relation.EntityRelation; | ||
33 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | ||
25 | import org.thingsboard.server.dao.DaoUtil; | 34 | import org.thingsboard.server.dao.DaoUtil; |
26 | import org.thingsboard.server.dao.dashboard.DashboardInfoDao; | 35 | import org.thingsboard.server.dao.dashboard.DashboardInfoDao; |
27 | import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; | 36 | import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; |
37 | +import org.thingsboard.server.dao.relation.RelationDao; | ||
28 | import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; | 38 | import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
29 | import org.thingsboard.server.dao.util.SqlDao; | 39 | import org.thingsboard.server.dao.util.SqlDao; |
30 | 40 | ||
41 | +import java.sql.Time; | ||
42 | +import java.util.ArrayList; | ||
31 | import java.util.List; | 43 | import java.util.List; |
32 | import java.util.Objects; | 44 | import java.util.Objects; |
33 | import java.util.UUID; | 45 | import java.util.UUID; |
@@ -37,11 +49,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; | @@ -37,11 +49,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; | ||
37 | /** | 49 | /** |
38 | * Created by Valerii Sosliuk on 5/6/2017. | 50 | * Created by Valerii Sosliuk on 5/6/2017. |
39 | */ | 51 | */ |
52 | +@Slf4j | ||
40 | @Component | 53 | @Component |
41 | @SqlDao | 54 | @SqlDao |
42 | public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { | 55 | public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { |
43 | 56 | ||
44 | @Autowired | 57 | @Autowired |
58 | + private RelationDao relationDao; | ||
59 | + | ||
60 | + @Autowired | ||
45 | private DashboardInfoRepository dashboardInfoRepository; | 61 | private DashboardInfoRepository dashboardInfoRepository; |
46 | 62 | ||
47 | @Override | 63 | @Override |
@@ -65,13 +81,17 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE | @@ -65,13 +81,17 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE | ||
65 | } | 81 | } |
66 | 82 | ||
67 | @Override | 83 | @Override |
68 | - public List<DashboardInfo> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { | ||
69 | - return DaoUtil.convertDataList(dashboardInfoRepository | ||
70 | - .findByTenantIdAndCustomerId( | ||
71 | - UUIDConverter.fromTimeUUID(tenantId), | ||
72 | - UUIDConverter.fromTimeUUID(customerId), | ||
73 | - Objects.toString(pageLink.getTextSearch(), ""), | ||
74 | - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), | ||
75 | - new PageRequest(0, pageLink.getLimit()))); | 84 | + public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { |
85 | + log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); | ||
86 | + | ||
87 | + ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); | ||
88 | + | ||
89 | + return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<DashboardInfo>>) input -> { | ||
90 | + List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); | ||
91 | + for (EntityRelation relation : input) { | ||
92 | + dashboardFutures.add(findByIdAsync(relation.getTo().getId())); | ||
93 | + } | ||
94 | + return Futures.successfulAsList(dashboardFutures); | ||
95 | + }); | ||
76 | } | 96 | } |
77 | } | 97 | } |
@@ -364,26 +364,19 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.widget_type_by_tenant_and_ali | @@ -364,26 +364,19 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.widget_type_by_tenant_and_ali | ||
364 | CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( | 364 | CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( |
365 | id timeuuid, | 365 | id timeuuid, |
366 | tenant_id timeuuid, | 366 | tenant_id timeuuid, |
367 | - customer_id timeuuid, | ||
368 | title text, | 367 | title text, |
369 | search_text text, | 368 | search_text text, |
369 | + assigned_customers text, | ||
370 | configuration text, | 370 | configuration text, |
371 | - PRIMARY KEY (id, tenant_id, customer_id) | 371 | + PRIMARY KEY (id, tenant_id) |
372 | ); | 372 | ); |
373 | 373 | ||
374 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS | 374 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS |
375 | SELECT * | 375 | SELECT * |
376 | from thingsboard.dashboard | 376 | from thingsboard.dashboard |
377 | - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
378 | - PRIMARY KEY ( tenant_id, search_text, id, customer_id ) | ||
379 | - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC ); | ||
380 | - | ||
381 | -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_customer_and_search_text AS | ||
382 | - SELECT * | ||
383 | - from thingsboard.dashboard | ||
384 | - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
385 | - PRIMARY KEY ( customer_id, tenant_id, search_text, id ) | ||
386 | - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); | 377 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL |
378 | + PRIMARY KEY ( tenant_id, search_text, id ) | ||
379 | + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); | ||
387 | 380 | ||
388 | CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( | 381 | CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( |
389 | entity_type text, // (DEVICE, CUSTOMER, TENANT) | 382 | entity_type text, // (DEVICE, CUSTOMER, TENANT) |
@@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS customer ( | @@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS customer ( | ||
105 | CREATE TABLE IF NOT EXISTS dashboard ( | 105 | CREATE TABLE IF NOT EXISTS dashboard ( |
106 | id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, | 106 | id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, |
107 | configuration varchar(10000000), | 107 | configuration varchar(10000000), |
108 | - customer_id varchar(31), | 108 | + assigned_customers varchar(1000000), |
109 | search_text varchar(255), | 109 | search_text varchar(255), |
110 | tenant_id varchar(31), | 110 | tenant_id varchar(31), |
111 | title varchar(255) | 111 | title varchar(255) |
@@ -29,13 +29,17 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -29,13 +29,17 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
29 | import org.thingsboard.server.common.data.id.TenantId; | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | import org.thingsboard.server.common.data.page.TextPageData; | 30 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | import org.thingsboard.server.common.data.page.TextPageLink; | 31 | import org.thingsboard.server.common.data.page.TextPageLink; |
32 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
33 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
32 | import org.thingsboard.server.dao.exception.DataValidationException; | 34 | import org.thingsboard.server.dao.exception.DataValidationException; |
33 | import org.thingsboard.server.dao.model.ModelConstants; | 35 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | 36 | ||
35 | import java.io.IOException; | 37 | import java.io.IOException; |
38 | +import java.sql.Time; | ||
36 | import java.util.ArrayList; | 39 | import java.util.ArrayList; |
37 | import java.util.Collections; | 40 | import java.util.Collections; |
38 | import java.util.List; | 41 | import java.util.List; |
42 | +import java.util.concurrent.ExecutionException; | ||
39 | 43 | ||
40 | public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | 44 | public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
41 | 45 | ||
@@ -68,8 +72,6 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -68,8 +72,6 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
68 | Assert.assertNotNull(savedDashboard.getId()); | 72 | Assert.assertNotNull(savedDashboard.getId()); |
69 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); | 73 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); |
70 | Assert.assertEquals(dashboard.getTenantId(), savedDashboard.getTenantId()); | 74 | Assert.assertEquals(dashboard.getTenantId(), savedDashboard.getTenantId()); |
71 | - Assert.assertNotNull(savedDashboard.getCustomerId()); | ||
72 | - Assert.assertEquals(ModelConstants.NULL_UUID, savedDashboard.getCustomerId().getId()); | ||
73 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); | 75 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); |
74 | 76 | ||
75 | savedDashboard.setTitle("My new dashboard"); | 77 | savedDashboard.setTitle("My new dashboard"); |
@@ -280,7 +282,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -280,7 +282,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
280 | } | 282 | } |
281 | 283 | ||
282 | @Test | 284 | @Test |
283 | - public void testFindDashboardsByTenantIdAndCustomerId() { | 285 | + public void testFindDashboardsByTenantIdAndCustomerId() throws ExecutionException, InterruptedException { |
284 | Tenant tenant = new Tenant(); | 286 | Tenant tenant = new Tenant(); |
285 | tenant.setTitle("Test tenant"); | 287 | tenant.setTitle("Test tenant"); |
286 | tenant = tenantService.saveTenant(tenant); | 288 | tenant = tenantService.saveTenant(tenant); |
@@ -303,10 +305,10 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -303,10 +305,10 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
303 | } | 305 | } |
304 | 306 | ||
305 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); | 307 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); |
306 | - TextPageLink pageLink = new TextPageLink(23); | ||
307 | - TextPageData<DashboardInfo> pageData = null; | 308 | + TimePageLink pageLink = new TimePageLink(23); |
309 | + TimePageData<DashboardInfo> pageData = null; | ||
308 | do { | 310 | do { |
309 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | 311 | + pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); |
310 | loadedDashboards.addAll(pageData.getData()); | 312 | loadedDashboards.addAll(pageData.getData()); |
311 | if (pageData.hasNext()) { | 313 | if (pageData.hasNext()) { |
312 | pageLink = pageData.getNextPageLink(); | 314 | pageLink = pageData.getNextPageLink(); |
@@ -320,96 +322,12 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -320,96 +322,12 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
320 | 322 | ||
321 | dashboardService.unassignCustomerDashboards(tenantId, customerId); | 323 | dashboardService.unassignCustomerDashboards(tenantId, customerId); |
322 | 324 | ||
323 | - pageLink = new TextPageLink(42); | ||
324 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | 325 | + pageLink = new TimePageLink(42); |
326 | + pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); | ||
325 | Assert.assertFalse(pageData.hasNext()); | 327 | Assert.assertFalse(pageData.hasNext()); |
326 | Assert.assertTrue(pageData.getData().isEmpty()); | 328 | Assert.assertTrue(pageData.getData().isEmpty()); |
327 | 329 | ||
328 | tenantService.deleteTenant(tenantId); | 330 | tenantService.deleteTenant(tenantId); |
329 | } | 331 | } |
330 | - | ||
331 | - @Test | ||
332 | - public void testFindDashboardsByTenantIdCustomerIdAndTitle() { | ||
333 | - | ||
334 | - Customer customer = new Customer(); | ||
335 | - customer.setTitle("Test customer"); | ||
336 | - customer.setTenantId(tenantId); | ||
337 | - customer = customerService.saveCustomer(customer); | ||
338 | - CustomerId customerId = customer.getId(); | ||
339 | - | ||
340 | - String title1 = "Dashboard title 1"; | ||
341 | - List<DashboardInfo> dashboardsTitle1 = new ArrayList<>(); | ||
342 | - for (int i=0;i<124;i++) { | ||
343 | - Dashboard dashboard = new Dashboard(); | ||
344 | - dashboard.setTenantId(tenantId); | ||
345 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
346 | - String title = title1+suffix; | ||
347 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
348 | - dashboard.setTitle(title); | ||
349 | - dashboard = dashboardService.saveDashboard(dashboard); | ||
350 | - dashboardsTitle1.add(new DashboardInfo(dashboardService.assignDashboardToCustomer(dashboard.getId(), customerId))); | ||
351 | - } | ||
352 | - String title2 = "Dashboard title 2"; | ||
353 | - List<DashboardInfo> dashboardsTitle2 = new ArrayList<>(); | ||
354 | - for (int i=0;i<151;i++) { | ||
355 | - Dashboard dashboard = new Dashboard(); | ||
356 | - dashboard.setTenantId(tenantId); | ||
357 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
358 | - String title = title2+suffix; | ||
359 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
360 | - dashboard.setTitle(title); | ||
361 | - dashboard = dashboardService.saveDashboard(dashboard); | ||
362 | - dashboardsTitle2.add(new DashboardInfo(dashboardService.assignDashboardToCustomer(dashboard.getId(), customerId))); | ||
363 | - } | ||
364 | - | ||
365 | - List<DashboardInfo> loadedDashboardsTitle1 = new ArrayList<>(); | ||
366 | - TextPageLink pageLink = new TextPageLink(24, title1); | ||
367 | - TextPageData<DashboardInfo> pageData = null; | ||
368 | - do { | ||
369 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
370 | - loadedDashboardsTitle1.addAll(pageData.getData()); | ||
371 | - if (pageData.hasNext()) { | ||
372 | - pageLink = pageData.getNextPageLink(); | ||
373 | - } | ||
374 | - } while (pageData.hasNext()); | ||
375 | - | ||
376 | - Collections.sort(dashboardsTitle1, idComparator); | ||
377 | - Collections.sort(loadedDashboardsTitle1, idComparator); | ||
378 | - | ||
379 | - Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); | ||
380 | - | ||
381 | - List<DashboardInfo> loadedDashboardsTitle2 = new ArrayList<>(); | ||
382 | - pageLink = new TextPageLink(4, title2); | ||
383 | - do { | ||
384 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
385 | - loadedDashboardsTitle2.addAll(pageData.getData()); | ||
386 | - if (pageData.hasNext()) { | ||
387 | - pageLink = pageData.getNextPageLink(); | ||
388 | - } | ||
389 | - } while (pageData.hasNext()); | ||
390 | 332 | ||
391 | - Collections.sort(dashboardsTitle2, idComparator); | ||
392 | - Collections.sort(loadedDashboardsTitle2, idComparator); | ||
393 | - | ||
394 | - Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); | ||
395 | - | ||
396 | - for (DashboardInfo dashboard : loadedDashboardsTitle1) { | ||
397 | - dashboardService.deleteDashboard(dashboard.getId()); | ||
398 | - } | ||
399 | - | ||
400 | - pageLink = new TextPageLink(4, title1); | ||
401 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
402 | - Assert.assertFalse(pageData.hasNext()); | ||
403 | - Assert.assertEquals(0, pageData.getData().size()); | ||
404 | - | ||
405 | - for (DashboardInfo dashboard : loadedDashboardsTitle2) { | ||
406 | - dashboardService.deleteDashboard(dashboard.getId()); | ||
407 | - } | ||
408 | - | ||
409 | - pageLink = new TextPageLink(4, title2); | ||
410 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
411 | - Assert.assertFalse(pageData.hasNext()); | ||
412 | - Assert.assertEquals(0, pageData.getData().size()); | ||
413 | - customerService.deleteCustomer(customerId); | ||
414 | - } | ||
415 | } | 333 | } |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.server.dao.sql.dashboard; | 16 | package org.thingsboard.server.dao.sql.dashboard; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import org.junit.Assert; | ||
19 | import org.junit.Test; | 20 | import org.junit.Test; |
20 | import org.springframework.beans.factory.annotation.Autowired; | 21 | import org.springframework.beans.factory.annotation.Autowired; |
21 | import org.thingsboard.server.common.data.DashboardInfo; | 22 | import org.thingsboard.server.common.data.DashboardInfo; |
@@ -40,53 +41,26 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { | @@ -40,53 +41,26 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { | ||
40 | @Test | 41 | @Test |
41 | public void testFindDashboardsByTenantId() { | 42 | public void testFindDashboardsByTenantId() { |
42 | UUID tenantId1 = UUIDs.timeBased(); | 43 | UUID tenantId1 = UUIDs.timeBased(); |
43 | - UUID customerId1 = UUIDs.timeBased(); | ||
44 | UUID tenantId2 = UUIDs.timeBased(); | 44 | UUID tenantId2 = UUIDs.timeBased(); |
45 | - UUID customerId2 = UUIDs.timeBased(); | ||
46 | 45 | ||
47 | for (int i = 0; i < 20; i++) { | 46 | for (int i = 0; i < 20; i++) { |
48 | - createDashboard(tenantId1, customerId1, i); | ||
49 | - createDashboard(tenantId2, customerId2, i * 2); | 47 | + createDashboard(tenantId1, i); |
48 | + createDashboard(tenantId2, i * 2); | ||
50 | } | 49 | } |
51 | 50 | ||
52 | TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); | 51 | TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); |
53 | List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink1); | 52 | List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink1); |
54 | - assertEquals(15, dashboardInfos1.size()); | 53 | + Assert.assertEquals(15, dashboardInfos1.size()); |
55 | 54 | ||
56 | TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); | 55 | TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); |
57 | List<DashboardInfo> dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink2); | 56 | List<DashboardInfo> dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink2); |
58 | - assertEquals(5, dashboardInfos2.size()); | 57 | + Assert.assertEquals(5, dashboardInfos2.size()); |
59 | } | 58 | } |
60 | 59 | ||
61 | - @Test | ||
62 | - public void testFindDashboardsByTenantAndCustomerId() { | ||
63 | - UUID tenantId1 = UUIDs.timeBased(); | ||
64 | - UUID customerId1 = UUIDs.timeBased(); | ||
65 | - UUID tenantId2 = UUIDs.timeBased(); | ||
66 | - UUID customerId2 = UUIDs.timeBased(); | ||
67 | - | ||
68 | - for (int i = 0; i < 20; i++) { | ||
69 | - createDashboard(tenantId1, customerId1, i); | ||
70 | - createDashboard(tenantId2, customerId2, i * 2); | ||
71 | - } | ||
72 | - | ||
73 | - TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); | ||
74 | - List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId1, customerId1, pageLink1); | ||
75 | - assertEquals(15, dashboardInfos1.size()); | ||
76 | - | ||
77 | - TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); | ||
78 | - List<DashboardInfo> dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId1, customerId1, pageLink2); | ||
79 | - assertEquals(5, dashboardInfos2.size()); | ||
80 | - } | ||
81 | - | ||
82 | - private void assertEquals(int i, int size) { | ||
83 | - } | ||
84 | - | ||
85 | - private void createDashboard(UUID tenantId, UUID customerId, int index) { | 60 | + private void createDashboard(UUID tenantId, int index) { |
86 | DashboardInfo dashboardInfo = new DashboardInfo(); | 61 | DashboardInfo dashboardInfo = new DashboardInfo(); |
87 | dashboardInfo.setId(new DashboardId(UUIDs.timeBased())); | 62 | dashboardInfo.setId(new DashboardId(UUIDs.timeBased())); |
88 | dashboardInfo.setTenantId(new TenantId(tenantId)); | 63 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
89 | - dashboardInfo.setCustomerId(new CustomerId(customerId)); | ||
90 | dashboardInfo.setTitle("DASHBOARD_" + index); | 64 | dashboardInfo.setTitle("DASHBOARD_" + index); |
91 | dashboardInfoDao.save(dashboardInfo); | 65 | dashboardInfoDao.save(dashboardInfo); |
92 | } | 66 | } |