Commit ca6ea7dd6dfad805821be8df9ea56ae29b8646b0
Committed by
GitHub
Merge pull request #619 from thingsboard/feature/customer-dashboards
Feature/customer dashboards
Showing
58 changed files
with
1629 additions
and
817 deletions
... | ... | @@ -87,3 +87,26 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( |
87 | 87 | PRIMARY KEY (( tenant_id ), partition) |
88 | 88 | ) WITH CLUSTERING ORDER BY ( partition ASC ) |
89 | 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 | 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 | 423 | try { |
424 | 424 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
425 | 425 | Dashboard dashboard = dashboardService.findDashboardById(dashboardId); |
426 | - checkDashboard(dashboard, true); | |
426 | + checkDashboard(dashboard); | |
427 | 427 | return dashboard; |
428 | 428 | } catch (Exception e) { |
429 | 429 | throw handleException(e, false); |
... | ... | @@ -434,28 +434,23 @@ public abstract class BaseController { |
434 | 434 | try { |
435 | 435 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
436 | 436 | DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); |
437 | - SecurityUser authUser = getCurrentUser(); | |
438 | - checkDashboard(dashboardInfo, authUser.getAuthority() != Authority.SYS_ADMIN); | |
437 | + checkDashboard(dashboardInfo); | |
439 | 438 | return dashboardInfo; |
440 | 439 | } catch (Exception e) { |
441 | 440 | throw handleException(e, false); |
442 | 441 | } |
443 | 442 | } |
444 | 443 | |
445 | - private void checkDashboard(DashboardInfo dashboard, boolean checkCustomerId) throws ThingsboardException { | |
444 | + private void checkDashboard(DashboardInfo dashboard) throws ThingsboardException { | |
446 | 445 | checkNotNull(dashboard); |
447 | 446 | checkTenantId(dashboard.getTenantId()); |
448 | 447 | SecurityUser authUser = getCurrentUser(); |
449 | 448 | if (authUser.getAuthority() == Authority.CUSTOMER_USER) { |
450 | - if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | |
449 | + if (!dashboard.isAssignedToCustomer(authUser.getCustomerId())) { | |
451 | 450 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, |
452 | 451 | ThingsboardErrorCode.PERMISSION_DENIED); |
453 | 452 | } |
454 | 453 | } |
455 | - if (checkCustomerId && | |
456 | - dashboard.getCustomerId() != null && !dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | |
457 | - checkCustomerId(dashboard.getCustomerId()); | |
458 | - } | |
459 | 454 | } |
460 | 455 | |
461 | 456 | ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { | ... | ... |
... | ... | @@ -18,20 +18,22 @@ package org.thingsboard.server.controller; |
18 | 18 | import org.springframework.http.HttpStatus; |
19 | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | 20 | import org.springframework.web.bind.annotation.*; |
21 | -import org.thingsboard.server.common.data.Customer; | |
22 | -import org.thingsboard.server.common.data.Dashboard; | |
23 | -import org.thingsboard.server.common.data.DashboardInfo; | |
24 | -import org.thingsboard.server.common.data.EntityType; | |
21 | +import org.thingsboard.server.common.data.*; | |
25 | 22 | import org.thingsboard.server.common.data.audit.ActionType; |
26 | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
27 | 24 | import org.thingsboard.server.common.data.id.DashboardId; |
28 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 26 | import org.thingsboard.server.common.data.page.TextPageData; |
30 | 27 | import org.thingsboard.server.common.data.page.TextPageLink; |
28 | +import org.thingsboard.server.common.data.page.TimePageData; | |
29 | +import org.thingsboard.server.common.data.page.TimePageLink; | |
31 | 30 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
32 | 31 | import org.thingsboard.server.dao.model.ModelConstants; |
33 | 32 | import org.thingsboard.server.exception.ThingsboardException; |
34 | 33 | |
34 | +import java.util.HashSet; | |
35 | +import java.util.Set; | |
36 | + | |
35 | 37 | @RestController |
36 | 38 | @RequestMapping("/api") |
37 | 39 | public class DashboardController extends BaseController { |
... | ... | @@ -80,7 +82,7 @@ public class DashboardController extends BaseController { |
80 | 82 | Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); |
81 | 83 | |
82 | 84 | logEntityAction(savedDashboard.getId(), savedDashboard, |
83 | - savedDashboard.getCustomerId(), | |
85 | + null, | |
84 | 86 | dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
85 | 87 | |
86 | 88 | return savedDashboard; |
... | ... | @@ -103,7 +105,7 @@ public class DashboardController extends BaseController { |
103 | 105 | dashboardService.deleteDashboard(dashboardId); |
104 | 106 | |
105 | 107 | logEntityAction(dashboardId, dashboard, |
106 | - dashboard.getCustomerId(), | |
108 | + null, | |
107 | 109 | ActionType.DELETED, null, strDashboardId); |
108 | 110 | |
109 | 111 | } catch (Exception e) { |
... | ... | @@ -134,7 +136,7 @@ public class DashboardController extends BaseController { |
134 | 136 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); |
135 | 137 | |
136 | 138 | logEntityAction(dashboardId, savedDashboard, |
137 | - savedDashboard.getCustomerId(), | |
139 | + customerId, | |
138 | 140 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); |
139 | 141 | |
140 | 142 | |
... | ... | @@ -150,23 +152,22 @@ public class DashboardController extends BaseController { |
150 | 152 | } |
151 | 153 | |
152 | 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 | 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 | 160 | checkParameter(DASHBOARD_ID, strDashboardId); |
157 | 161 | try { |
162 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
163 | + Customer customer = checkCustomerId(customerId); | |
158 | 164 | DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); |
159 | 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 | 166 | |
164 | - Customer customer = checkCustomerId(dashboard.getCustomerId()); | |
165 | - | |
166 | - Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId)); | |
167 | + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); | |
167 | 168 | |
168 | 169 | logEntityAction(dashboardId, dashboard, |
169 | - dashboard.getCustomerId(), | |
170 | + customerId, | |
170 | 171 | ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); |
171 | 172 | |
172 | 173 | return savedDashboard; |
... | ... | @@ -181,6 +182,158 @@ public class DashboardController extends BaseController { |
181 | 182 | } |
182 | 183 | |
183 | 184 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
185 | + @RequestMapping(value = "/dashboard/{dashboardId}/customers", method = RequestMethod.POST) | |
186 | + @ResponseBody | |
187 | + public Dashboard updateDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
188 | + @RequestBody String[] strCustomerIds) throws ThingsboardException { | |
189 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
190 | + try { | |
191 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
192 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
193 | + | |
194 | + Set<CustomerId> customerIds = new HashSet<>(); | |
195 | + if (strCustomerIds != null) { | |
196 | + for (String strCustomerId : strCustomerIds) { | |
197 | + customerIds.add(new CustomerId(toUUID(strCustomerId))); | |
198 | + } | |
199 | + } | |
200 | + | |
201 | + Set<CustomerId> addedCustomerIds = new HashSet<>(); | |
202 | + Set<CustomerId> removedCustomerIds = new HashSet<>(); | |
203 | + for (CustomerId customerId : customerIds) { | |
204 | + if (!dashboard.isAssignedToCustomer(customerId)) { | |
205 | + addedCustomerIds.add(customerId); | |
206 | + } | |
207 | + } | |
208 | + | |
209 | + Set<ShortCustomerInfo> assignedCustomers = dashboard.getAssignedCustomers(); | |
210 | + if (assignedCustomers != null) { | |
211 | + for (ShortCustomerInfo customerInfo : assignedCustomers) { | |
212 | + if (!customerIds.contains(customerInfo.getCustomerId())) { | |
213 | + removedCustomerIds.add(customerInfo.getCustomerId()); | |
214 | + } | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + if (addedCustomerIds.isEmpty() && removedCustomerIds.isEmpty()) { | |
219 | + return dashboard; | |
220 | + } else { | |
221 | + Dashboard savedDashboard = null; | |
222 | + for (CustomerId customerId : addedCustomerIds) { | |
223 | + savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | |
224 | + ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); | |
225 | + logEntityAction(dashboardId, savedDashboard, | |
226 | + customerId, | |
227 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
228 | + } | |
229 | + for (CustomerId customerId : removedCustomerIds) { | |
230 | + ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); | |
231 | + savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); | |
232 | + logEntityAction(dashboardId, dashboard, | |
233 | + customerId, | |
234 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
235 | + | |
236 | + } | |
237 | + return savedDashboard; | |
238 | + } | |
239 | + } catch (Exception e) { | |
240 | + | |
241 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
242 | + null, | |
243 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId); | |
244 | + | |
245 | + throw handleException(e); | |
246 | + } | |
247 | + } | |
248 | + | |
249 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
250 | + @RequestMapping(value = "/dashboard/{dashboardId}/customers/add", method = RequestMethod.POST) | |
251 | + @ResponseBody | |
252 | + public Dashboard addDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
253 | + @RequestBody String[] strCustomerIds) throws ThingsboardException { | |
254 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
255 | + try { | |
256 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
257 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
258 | + | |
259 | + Set<CustomerId> customerIds = new HashSet<>(); | |
260 | + if (strCustomerIds != null) { | |
261 | + for (String strCustomerId : strCustomerIds) { | |
262 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
263 | + if (!dashboard.isAssignedToCustomer(customerId)) { | |
264 | + customerIds.add(customerId); | |
265 | + } | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + if (customerIds.isEmpty()) { | |
270 | + return dashboard; | |
271 | + } else { | |
272 | + Dashboard savedDashboard = null; | |
273 | + for (CustomerId customerId : customerIds) { | |
274 | + savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | |
275 | + ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); | |
276 | + logEntityAction(dashboardId, savedDashboard, | |
277 | + customerId, | |
278 | + ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
279 | + } | |
280 | + return savedDashboard; | |
281 | + } | |
282 | + } catch (Exception e) { | |
283 | + | |
284 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
285 | + null, | |
286 | + ActionType.ASSIGNED_TO_CUSTOMER, e, strDashboardId); | |
287 | + | |
288 | + throw handleException(e); | |
289 | + } | |
290 | + } | |
291 | + | |
292 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
293 | + @RequestMapping(value = "/dashboard/{dashboardId}/customers/remove", method = RequestMethod.POST) | |
294 | + @ResponseBody | |
295 | + public Dashboard removeDashboardCustomers(@PathVariable(DASHBOARD_ID) String strDashboardId, | |
296 | + @RequestBody String[] strCustomerIds) throws ThingsboardException { | |
297 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
298 | + try { | |
299 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
300 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
301 | + | |
302 | + Set<CustomerId> customerIds = new HashSet<>(); | |
303 | + if (strCustomerIds != null) { | |
304 | + for (String strCustomerId : strCustomerIds) { | |
305 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | |
306 | + if (dashboard.isAssignedToCustomer(customerId)) { | |
307 | + customerIds.add(customerId); | |
308 | + } | |
309 | + } | |
310 | + } | |
311 | + | |
312 | + if (customerIds.isEmpty()) { | |
313 | + return dashboard; | |
314 | + } else { | |
315 | + Dashboard savedDashboard = null; | |
316 | + for (CustomerId customerId : customerIds) { | |
317 | + ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); | |
318 | + savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, customerId)); | |
319 | + logEntityAction(dashboardId, dashboard, | |
320 | + customerId, | |
321 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); | |
322 | + | |
323 | + } | |
324 | + return savedDashboard; | |
325 | + } | |
326 | + } catch (Exception e) { | |
327 | + | |
328 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
329 | + null, | |
330 | + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId); | |
331 | + | |
332 | + throw handleException(e); | |
333 | + } | |
334 | + } | |
335 | + | |
336 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
184 | 337 | @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) |
185 | 338 | @ResponseBody |
186 | 339 | public Dashboard assignDashboardToPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { |
... | ... | @@ -192,7 +345,7 @@ public class DashboardController extends BaseController { |
192 | 345 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); |
193 | 346 | |
194 | 347 | logEntityAction(dashboardId, savedDashboard, |
195 | - savedDashboard.getCustomerId(), | |
348 | + publicCustomer.getId(), | |
196 | 349 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); |
197 | 350 | |
198 | 351 | return savedDashboard; |
... | ... | @@ -206,6 +359,33 @@ public class DashboardController extends BaseController { |
206 | 359 | } |
207 | 360 | } |
208 | 361 | |
362 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | |
363 | + @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE) | |
364 | + @ResponseBody | |
365 | + public Dashboard unassignDashboardFromPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | |
366 | + checkParameter(DASHBOARD_ID, strDashboardId); | |
367 | + try { | |
368 | + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | |
369 | + Dashboard dashboard = checkDashboardId(dashboardId); | |
370 | + Customer publicCustomer = customerService.findOrCreatePublicCustomer(dashboard.getTenantId()); | |
371 | + | |
372 | + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(dashboardId, publicCustomer.getId())); | |
373 | + | |
374 | + logEntityAction(dashboardId, dashboard, | |
375 | + publicCustomer.getId(), | |
376 | + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); | |
377 | + | |
378 | + return savedDashboard; | |
379 | + } catch (Exception e) { | |
380 | + | |
381 | + logEntityAction(emptyId(EntityType.DASHBOARD), null, | |
382 | + null, | |
383 | + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strDashboardId); | |
384 | + | |
385 | + throw handleException(e); | |
386 | + } | |
387 | + } | |
388 | + | |
209 | 389 | @PreAuthorize("hasAuthority('SYS_ADMIN')") |
210 | 390 | @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET) |
211 | 391 | @ResponseBody |
... | ... | @@ -245,19 +425,20 @@ public class DashboardController extends BaseController { |
245 | 425 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
246 | 426 | @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) |
247 | 427 | @ResponseBody |
248 | - public TextPageData<DashboardInfo> getCustomerDashboards( | |
428 | + public TimePageData<DashboardInfo> getCustomerDashboards( | |
249 | 429 | @PathVariable("customerId") String strCustomerId, |
250 | 430 | @RequestParam int limit, |
251 | - @RequestParam(required = false) String textSearch, | |
252 | - @RequestParam(required = false) String idOffset, | |
253 | - @RequestParam(required = false) String textOffset) throws ThingsboardException { | |
431 | + @RequestParam(required = false) Long startTime, | |
432 | + @RequestParam(required = false) Long endTime, | |
433 | + @RequestParam(required = false, defaultValue = "false") boolean ascOrder, | |
434 | + @RequestParam(required = false) String offset) throws ThingsboardException { | |
254 | 435 | checkParameter("customerId", strCustomerId); |
255 | 436 | try { |
256 | 437 | TenantId tenantId = getCurrentUser().getTenantId(); |
257 | 438 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
258 | 439 | checkCustomerId(customerId); |
259 | - TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | |
260 | - return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); | |
440 | + TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); | |
441 | + return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get()); | |
261 | 442 | } catch (Exception e) { |
262 | 443 | throw handleException(e); |
263 | 444 | } | ... | ... |
... | ... | @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Profile; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | 25 | import org.thingsboard.server.dao.cassandra.CassandraCluster; |
26 | 26 | import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; |
27 | +import org.thingsboard.server.dao.dashboard.DashboardService; | |
27 | 28 | import org.thingsboard.server.dao.util.NoSqlDao; |
28 | 29 | import org.thingsboard.server.service.install.cql.CQLStatementsParser; |
29 | 30 | import org.thingsboard.server.service.install.cql.CassandraDbHelper; |
... | ... | @@ -33,6 +34,8 @@ import java.nio.file.Path; |
33 | 34 | import java.nio.file.Paths; |
34 | 35 | import java.util.List; |
35 | 36 | |
37 | +import static org.thingsboard.server.service.install.DatabaseHelper.*; | |
38 | + | |
36 | 39 | @Service |
37 | 40 | @NoSqlDao |
38 | 41 | @Profile("install") |
... | ... | @@ -40,12 +43,6 @@ import java.util.List; |
40 | 43 | public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
41 | 44 | |
42 | 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 | 47 | @Value("${install.data_dir}") |
51 | 48 | private String dataDir; |
... | ... | @@ -56,6 +53,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
56 | 53 | @Autowired |
57 | 54 | private CassandraInstallCluster installCluster; |
58 | 55 | |
56 | + @Autowired | |
57 | + private DashboardService dashboardService; | |
58 | + | |
59 | 59 | @Override |
60 | 60 | public void upgradeDatabase(String fromVersion) throws Exception { |
61 | 61 | |
... | ... | @@ -160,10 +160,32 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
160 | 160 | case "1.3.0": |
161 | 161 | break; |
162 | 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", true); | |
173 | + log.info("Dashboards dumped."); | |
174 | + | |
175 | + | |
163 | 176 | log.info("Updating schema ..."); |
164 | 177 | schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); |
165 | 178 | loadCql(schemaUpdateFile); |
166 | 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, true); | |
185 | + DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, false); | |
186 | + Files.deleteIfExists(dashboardsDump); | |
187 | + } | |
188 | + log.info("Dashboards restored."); | |
167 | 189 | break; |
168 | 190 | default: |
169 | 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.JavaType; | |
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
21 | +import org.apache.commons.csv.CSVFormat; | |
22 | +import org.apache.commons.csv.CSVParser; | |
23 | +import org.apache.commons.lang3.StringUtils; | |
24 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
25 | +import org.thingsboard.server.common.data.UUIDConverter; | |
26 | +import org.thingsboard.server.common.data.id.CustomerId; | |
27 | +import org.thingsboard.server.common.data.id.DashboardId; | |
28 | +import org.thingsboard.server.dao.dashboard.DashboardService; | |
29 | + | |
30 | +import java.io.IOException; | |
31 | +import java.nio.file.Files; | |
32 | +import java.nio.file.Path; | |
33 | +import java.util.*; | |
34 | + | |
35 | +/** | |
36 | + * Created by igor on 2/27/18. | |
37 | + */ | |
38 | +@Slf4j | |
39 | +public class DatabaseHelper { | |
40 | + | |
41 | + public static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); | |
42 | + | |
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 | + public static final String DASHBOARD = "dashboard"; | |
50 | + public static final String ID = "id"; | |
51 | + public static final String TITLE = "title"; | |
52 | + public static final String ASSIGNED_CUSTOMERS = "assigned_customers"; | |
53 | + public static final String CONFIGURATION = "configuration"; | |
54 | + | |
55 | + public static final ObjectMapper objectMapper = new ObjectMapper(); | |
56 | + | |
57 | + public static void upgradeTo40_assignDashboards(Path dashboardsDump, DashboardService dashboardService, boolean sql) throws Exception { | |
58 | + JavaType assignedCustomersType = | |
59 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
60 | + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(dashboardsDump), CSV_DUMP_FORMAT.withFirstRecordAsHeader())) { | |
61 | + csvParser.forEach(record -> { | |
62 | + String customerIdString = record.get(CUSTOMER_ID); | |
63 | + String assignedCustomersString = record.get(ASSIGNED_CUSTOMERS); | |
64 | + DashboardId dashboardId = new DashboardId(toUUID(record.get(ID), sql)); | |
65 | + List<CustomerId> customerIds = new ArrayList<>(); | |
66 | + if (!StringUtils.isEmpty(assignedCustomersString)) { | |
67 | + try { | |
68 | + Set<ShortCustomerInfo> assignedCustomers = objectMapper.readValue(assignedCustomersString, assignedCustomersType); | |
69 | + assignedCustomers.forEach((customerInfo) -> { | |
70 | + CustomerId customerId = customerInfo.getCustomerId(); | |
71 | + if (!customerId.isNullUid()) { | |
72 | + customerIds.add(customerId); | |
73 | + } | |
74 | + }); | |
75 | + } catch (IOException e) { | |
76 | + log.error("Unable to parse assigned customers field", e); | |
77 | + } | |
78 | + } | |
79 | + if (!StringUtils.isEmpty(customerIdString)) { | |
80 | + CustomerId customerId = new CustomerId(toUUID(customerIdString, sql)); | |
81 | + if (!customerId.isNullUid()) { | |
82 | + customerIds.add(customerId); | |
83 | + } | |
84 | + } | |
85 | + for (CustomerId customerId : customerIds) { | |
86 | + dashboardService.assignDashboardToCustomer(dashboardId, customerId); | |
87 | + } | |
88 | + }); | |
89 | + } | |
90 | + } | |
91 | + | |
92 | + private static UUID toUUID(String src, boolean sql) { | |
93 | + if (sql) { | |
94 | + return UUIDConverter.fromString(src); | |
95 | + } else { | |
96 | + return UUID.fromString(src); | |
97 | + } | |
98 | + } | |
99 | + | |
100 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -339,8 +339,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
339 | 339 | JsonNode dashboardJson = objectMapper.readTree(path.toFile()); |
340 | 340 | Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); |
341 | 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 | 346 | } catch (Exception e) { |
345 | 347 | log.error("Unable to load dashboard from json: [{}]", path.toString()); |
346 | 348 | throw new RuntimeException("Unable to load dashboard from json", e); | ... | ... |
... | ... | @@ -17,18 +17,26 @@ |
17 | 17 | package org.thingsboard.server.service.install; |
18 | 18 | |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.springframework.beans.factory.annotation.Autowired; | |
20 | 21 | import org.springframework.beans.factory.annotation.Value; |
21 | 22 | import org.springframework.context.annotation.Profile; |
22 | 23 | import org.springframework.stereotype.Service; |
24 | +import org.thingsboard.server.dao.dashboard.DashboardService; | |
23 | 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 | 29 | import java.nio.charset.Charset; |
26 | 30 | import java.nio.file.Files; |
27 | 31 | import java.nio.file.Path; |
28 | 32 | import java.nio.file.Paths; |
29 | 33 | import java.sql.Connection; |
34 | +import java.sql.DatabaseMetaData; | |
30 | 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 | 40 | @Service |
33 | 41 | @Profile("install") |
34 | 42 | @Slf4j |
... | ... | @@ -49,6 +57,9 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
49 | 57 | @Value("${spring.datasource.password}") |
50 | 58 | private String dbPassword; |
51 | 59 | |
60 | + @Autowired | |
61 | + private DashboardService dashboardService; | |
62 | + | |
52 | 63 | @Override |
53 | 64 | public void upgradeDatabase(String fromVersion) throws Exception { |
54 | 65 | switch (fromVersion) { |
... | ... | @@ -62,13 +73,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
62 | 73 | log.info("Schema updated."); |
63 | 74 | break; |
64 | 75 | case "1.3.1": |
65 | - log.info("Updating schema ..."); | |
66 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); | |
67 | 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", true); | |
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 | 87 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); |
69 | 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, true); | |
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 | 100 | break; |
73 | 101 | default: |
74 | 102 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | ... | ... |
... | ... | @@ -28,16 +28,25 @@ import java.nio.file.Path; |
28 | 28 | import java.nio.file.StandardCopyOption; |
29 | 29 | import java.util.*; |
30 | 30 | |
31 | -public class CassandraDbHelper { | |
31 | +import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; | |
32 | 32 | |
33 | - private static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N"); | |
33 | +public class CassandraDbHelper { | |
34 | 34 | |
35 | 35 | public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, |
36 | 36 | String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { |
37 | + return dumpCfIfExists(ks, session, cfName, columns, defaultValues, dumpPrefix, false); | |
38 | + } | |
39 | + | |
40 | + public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, | |
41 | + String[] columns, String[] defaultValues, String dumpPrefix, boolean printHeader) throws Exception { | |
37 | 42 | if (ks.getTable(cfName) != null) { |
38 | 43 | Path dumpFile = Files.createTempFile(dumpPrefix, null); |
39 | 44 | Files.deleteIfExists(dumpFile); |
40 | - try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), CSV_DUMP_FORMAT)) { | |
45 | + CSVFormat csvFormat = CSV_DUMP_FORMAT; | |
46 | + if (printHeader) { | |
47 | + csvFormat = csvFormat.withHeader(columns); | |
48 | + } | |
49 | + try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), csvFormat)) { | |
41 | 50 | Statement stmt = new SimpleStatement("SELECT * FROM " + cfName); |
42 | 51 | stmt.setFetchSize(1000); |
43 | 52 | ResultSet rs = session.execute(stmt); |
... | ... | @@ -75,9 +84,19 @@ public class CassandraDbHelper { |
75 | 84 | } |
76 | 85 | |
77 | 86 | public static void loadCf(KeyspaceMetadata ks, Session session, String cfName, String[] columns, Path sourceFile) throws Exception { |
87 | + loadCf(ks, session, cfName, columns, sourceFile, false); | |
88 | + } | |
89 | + | |
90 | + public static void loadCf(KeyspaceMetadata ks, Session session, String cfName, String[] columns, Path sourceFile, boolean parseHeader) throws Exception { | |
78 | 91 | TableMetadata tableMetadata = ks.getTable(cfName); |
79 | 92 | PreparedStatement prepared = session.prepare(createInsertStatement(cfName, columns)); |
80 | - try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), CSV_DUMP_FORMAT.withHeader(columns))) { | |
93 | + CSVFormat csvFormat = CSV_DUMP_FORMAT; | |
94 | + if (parseHeader) { | |
95 | + csvFormat = csvFormat.withFirstRecordAsHeader(); | |
96 | + } else { | |
97 | + csvFormat = CSV_DUMP_FORMAT.withHeader(columns); | |
98 | + } | |
99 | + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), csvFormat)) { | |
81 | 100 | csvParser.forEach(record -> { |
82 | 101 | BoundStatement boundStatement = prepared.bind(); |
83 | 102 | for (String column : columns) { | ... | ... |
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.CSVFormat; | |
20 | +import org.apache.commons.csv.CSVParser; | |
21 | +import org.apache.commons.csv.CSVPrinter; | |
22 | +import org.apache.commons.csv.CSVRecord; | |
23 | + | |
24 | +import java.nio.file.Files; | |
25 | +import java.nio.file.Path; | |
26 | +import java.sql.*; | |
27 | +import java.util.ArrayList; | |
28 | +import java.util.HashMap; | |
29 | +import java.util.List; | |
30 | +import java.util.Map; | |
31 | + | |
32 | +import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FORMAT; | |
33 | + | |
34 | +/** | |
35 | + * Created by igor on 2/27/18. | |
36 | + */ | |
37 | +@Slf4j | |
38 | +public class SqlDbHelper { | |
39 | + | |
40 | + public static Path dumpTableIfExists(Connection conn, String tableName, | |
41 | + String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { | |
42 | + return dumpTableIfExists(conn, tableName, columns, defaultValues, dumpPrefix, false); | |
43 | + } | |
44 | + | |
45 | + public static Path dumpTableIfExists(Connection conn, String tableName, | |
46 | + String[] columns, String[] defaultValues, String dumpPrefix, boolean printHeader) throws Exception { | |
47 | + | |
48 | + if (tableExists(conn, tableName)) { | |
49 | + Path dumpFile = Files.createTempFile(dumpPrefix, null); | |
50 | + Files.deleteIfExists(dumpFile); | |
51 | + CSVFormat csvFormat = CSV_DUMP_FORMAT; | |
52 | + if (printHeader) { | |
53 | + csvFormat = csvFormat.withHeader(columns); | |
54 | + } | |
55 | + try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), csvFormat)) { | |
56 | + try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM " + tableName)) { | |
57 | + try (ResultSet tableRes = stmt.executeQuery()) { | |
58 | + ResultSetMetaData resMetaData = tableRes.getMetaData(); | |
59 | + Map<String, Integer> columnIndexMap = new HashMap<>(); | |
60 | + for (int i = 1; i <= resMetaData.getColumnCount(); i++) { | |
61 | + String columnName = resMetaData.getColumnName(i); | |
62 | + columnIndexMap.put(columnName.toUpperCase(), i); | |
63 | + } | |
64 | + while(tableRes.next()) { | |
65 | + dumpRow(tableRes, columnIndexMap, columns, defaultValues, csvPrinter); | |
66 | + } | |
67 | + } | |
68 | + } | |
69 | + } | |
70 | + return dumpFile; | |
71 | + } else { | |
72 | + return null; | |
73 | + } | |
74 | + } | |
75 | + | |
76 | + private static boolean tableExists(Connection conn, String tableName) { | |
77 | + try (Statement stmt = conn.createStatement()) { | |
78 | + stmt.executeQuery("select * from " + tableName + " where 1=0"); | |
79 | + return true; | |
80 | + } catch (Exception e) { | |
81 | + return false; | |
82 | + } | |
83 | + } | |
84 | + | |
85 | + public static void loadTable(Connection conn, String tableName, String[] columns, Path sourceFile) throws Exception { | |
86 | + loadTable(conn, tableName, columns, sourceFile, false); | |
87 | + } | |
88 | + | |
89 | + public static void loadTable(Connection conn, String tableName, String[] columns, Path sourceFile, boolean parseHeader) throws Exception { | |
90 | + CSVFormat csvFormat = CSV_DUMP_FORMAT; | |
91 | + if (parseHeader) { | |
92 | + csvFormat = csvFormat.withFirstRecordAsHeader(); | |
93 | + } else { | |
94 | + csvFormat = CSV_DUMP_FORMAT.withHeader(columns); | |
95 | + } | |
96 | + try (PreparedStatement prepared = conn.prepareStatement(createInsertStatement(tableName, columns))) { | |
97 | + try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), csvFormat)) { | |
98 | + csvParser.forEach(record -> { | |
99 | + try { | |
100 | + for (int i = 0; i < columns.length; i++) { | |
101 | + setColumnValue(i, columns[i], record, prepared); | |
102 | + } | |
103 | + prepared.execute(); | |
104 | + } catch (SQLException e) { | |
105 | + log.error("Unable to load table record!", e); | |
106 | + } | |
107 | + }); | |
108 | + } | |
109 | + } | |
110 | + } | |
111 | + | |
112 | + private static void dumpRow(ResultSet res, Map<String, Integer> columnIndexMap, String[] columns, | |
113 | + String[] defaultValues, CSVPrinter csvPrinter) throws Exception { | |
114 | + List<String> record = new ArrayList<>(); | |
115 | + for (int i=0;i<columns.length;i++) { | |
116 | + String column = columns[i]; | |
117 | + String defaultValue; | |
118 | + if (defaultValues != null && i < defaultValues.length) { | |
119 | + defaultValue = defaultValues[i]; | |
120 | + } else { | |
121 | + defaultValue = ""; | |
122 | + } | |
123 | + record.add(getColumnValue(column, defaultValue, columnIndexMap, res)); | |
124 | + } | |
125 | + csvPrinter.printRecord(record); | |
126 | + } | |
127 | + | |
128 | + private static String getColumnValue(String column, String defaultValue, Map<String, Integer> columnIndexMap, ResultSet res) { | |
129 | + int index = columnIndexMap.containsKey(column.toUpperCase()) ? columnIndexMap.get(column.toUpperCase()) : -1; | |
130 | + if (index > -1) { | |
131 | + String str; | |
132 | + try { | |
133 | + Object obj = res.getObject(index); | |
134 | + if (obj == null) { | |
135 | + return null; | |
136 | + } else { | |
137 | + str = obj.toString(); | |
138 | + } | |
139 | + } catch (Exception e) { | |
140 | + str = ""; | |
141 | + } | |
142 | + return str; | |
143 | + } else { | |
144 | + return defaultValue; | |
145 | + } | |
146 | + } | |
147 | + | |
148 | + private static void setColumnValue(int index, String column, | |
149 | + CSVRecord record, PreparedStatement preparedStatement) throws SQLException { | |
150 | + String value = record.get(column); | |
151 | + int type = preparedStatement.getParameterMetaData().getParameterType(index + 1); | |
152 | + preparedStatement.setObject(index + 1, value, type); | |
153 | + } | |
154 | + | |
155 | + private static String createInsertStatement(String tableName, String[] columns) { | |
156 | + StringBuilder insertStatementBuilder = new StringBuilder(); | |
157 | + insertStatementBuilder.append("INSERT INTO ").append(tableName).append(" ("); | |
158 | + for (String column : columns) { | |
159 | + insertStatementBuilder.append(column).append(","); | |
160 | + } | |
161 | + insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); | |
162 | + insertStatementBuilder.append(") VALUES ("); | |
163 | + for (String column : columns) { | |
164 | + insertStatementBuilder.append("?").append(","); | |
165 | + } | |
166 | + insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1); | |
167 | + insertStatementBuilder.append(")"); | |
168 | + return insertStatementBuilder.toString(); | |
169 | + } | |
170 | + | |
171 | +} | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.containsString; |
19 | 19 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
20 | 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
21 | 21 | |
22 | +import java.sql.Time; | |
22 | 23 | import java.util.ArrayList; |
23 | 24 | import java.util.Collections; |
24 | 25 | import java.util.List; |
... | ... | @@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.*; |
29 | 30 | import org.thingsboard.server.common.data.id.CustomerId; |
30 | 31 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | 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 | 35 | import org.thingsboard.server.common.data.security.Authority; |
33 | 36 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | 37 | import org.junit.After; |
... | ... | @@ -82,8 +85,6 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest |
82 | 85 | Assert.assertNotNull(savedDashboard.getId()); |
83 | 86 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); |
84 | 87 | Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); |
85 | - Assert.assertNotNull(savedDashboard.getCustomerId()); | |
86 | - Assert.assertEquals(NULL_UUID, savedDashboard.getCustomerId().getId()); | |
87 | 88 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); |
88 | 89 | |
89 | 90 | savedDashboard.setTitle("My new dashboard"); |
... | ... | @@ -136,17 +137,20 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest |
136 | 137 | |
137 | 138 | Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() |
138 | 139 | + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
139 | - Assert.assertEquals(savedCustomer.getId(), assignedDashboard.getCustomerId()); | |
140 | - | |
140 | + | |
141 | + Assert.assertTrue(assignedDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); | |
142 | + | |
141 | 143 | Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
142 | - Assert.assertEquals(savedCustomer.getId(), foundDashboard.getCustomerId()); | |
144 | + Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); | |
143 | 145 | |
144 | 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 | 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 | 156 | @Test |
... | ... | @@ -320,11 +324,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest |
320 | 324 | } |
321 | 325 | |
322 | 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 | 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 | 332 | loadedDashboards.addAll(pageData.getData()); |
329 | 333 | if (pageData.hasNext()) { |
330 | 334 | pageLink = pageData.getNextPageLink(); |
... | ... | @@ -336,93 +340,5 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest |
336 | 340 | |
337 | 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 | } | ... | ... |
... | ... | @@ -69,6 +69,11 @@ public class Customer extends ContactBased<CustomerId> implements HasName { |
69 | 69 | return false; |
70 | 70 | } |
71 | 71 | |
72 | + @JsonIgnore | |
73 | + public ShortCustomerInfo toShortCustomerInfo() { | |
74 | + return new ShortCustomerInfo(id, title, isPublic()); | |
75 | + } | |
76 | + | |
72 | 77 | @Override |
73 | 78 | @JsonProperty(access = Access.READ_ONLY) |
74 | 79 | public String getName() { | ... | ... |
... | ... | @@ -79,8 +79,6 @@ public class Dashboard extends DashboardInfo { |
79 | 79 | StringBuilder builder = new StringBuilder(); |
80 | 80 | builder.append("Dashboard [tenantId="); |
81 | 81 | builder.append(getTenantId()); |
82 | - builder.append(", customerId="); | |
83 | - builder.append(getCustomerId()); | |
84 | 82 | builder.append(", title="); |
85 | 83 | builder.append(getTitle()); |
86 | 84 | builder.append(", configuration="); | ... | ... |
... | ... | @@ -20,11 +20,13 @@ import org.thingsboard.server.common.data.id.CustomerId; |
20 | 20 | import org.thingsboard.server.common.data.id.DashboardId; |
21 | 21 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 22 | |
23 | +import java.util.*; | |
24 | + | |
23 | 25 | public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { |
24 | 26 | |
25 | 27 | private TenantId tenantId; |
26 | - private CustomerId customerId; | |
27 | 28 | private String title; |
29 | + private Set<ShortCustomerInfo> assignedCustomers; | |
28 | 30 | |
29 | 31 | public DashboardInfo() { |
30 | 32 | super(); |
... | ... | @@ -37,8 +39,8 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
37 | 39 | public DashboardInfo(DashboardInfo dashboardInfo) { |
38 | 40 | super(dashboardInfo); |
39 | 41 | this.tenantId = dashboardInfo.getTenantId(); |
40 | - this.customerId = dashboardInfo.getCustomerId(); | |
41 | 42 | this.title = dashboardInfo.getTitle(); |
43 | + this.assignedCustomers = dashboardInfo.getAssignedCustomers(); | |
42 | 44 | } |
43 | 45 | |
44 | 46 | public TenantId getTenantId() { |
... | ... | @@ -49,14 +51,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
49 | 51 | this.tenantId = tenantId; |
50 | 52 | } |
51 | 53 | |
52 | - public CustomerId getCustomerId() { | |
53 | - return customerId; | |
54 | - } | |
55 | - | |
56 | - public void setCustomerId(CustomerId customerId) { | |
57 | - this.customerId = customerId; | |
58 | - } | |
59 | - | |
60 | 54 | public String getTitle() { |
61 | 55 | return title; |
62 | 56 | } |
... | ... | @@ -65,6 +59,62 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
65 | 59 | this.title = title; |
66 | 60 | } |
67 | 61 | |
62 | + public Set<ShortCustomerInfo> getAssignedCustomers() { | |
63 | + return assignedCustomers; | |
64 | + } | |
65 | + | |
66 | + public void setAssignedCustomers(Set<ShortCustomerInfo> assignedCustomers) { | |
67 | + this.assignedCustomers = assignedCustomers; | |
68 | + } | |
69 | + | |
70 | + public boolean isAssignedToCustomer(CustomerId customerId) { | |
71 | + return this.assignedCustomers != null && this.assignedCustomers.contains(new ShortCustomerInfo(customerId, null, false)); | |
72 | + } | |
73 | + | |
74 | + public ShortCustomerInfo getAssignedCustomerInfo(CustomerId customerId) { | |
75 | + if (this.assignedCustomers != null) { | |
76 | + for (ShortCustomerInfo customerInfo : this.assignedCustomers) { | |
77 | + if (customerInfo.getCustomerId().equals(customerId)) { | |
78 | + return customerInfo; | |
79 | + } | |
80 | + } | |
81 | + } | |
82 | + return null; | |
83 | + } | |
84 | + | |
85 | + public boolean addAssignedCustomer(Customer customer) { | |
86 | + ShortCustomerInfo customerInfo = customer.toShortCustomerInfo(); | |
87 | + if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) { | |
88 | + return false; | |
89 | + } else { | |
90 | + if (this.assignedCustomers == null) { | |
91 | + this.assignedCustomers = new HashSet<>(); | |
92 | + } | |
93 | + this.assignedCustomers.add(customerInfo); | |
94 | + return true; | |
95 | + } | |
96 | + } | |
97 | + | |
98 | + public boolean updateAssignedCustomer(Customer customer) { | |
99 | + ShortCustomerInfo customerInfo = customer.toShortCustomerInfo(); | |
100 | + if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) { | |
101 | + this.assignedCustomers.add(customerInfo); | |
102 | + return true; | |
103 | + } else { | |
104 | + return false; | |
105 | + } | |
106 | + } | |
107 | + | |
108 | + public boolean removeAssignedCustomer(Customer customer) { | |
109 | + ShortCustomerInfo customerInfo = customer.toShortCustomerInfo(); | |
110 | + if (this.assignedCustomers != null && this.assignedCustomers.contains(customerInfo)) { | |
111 | + this.assignedCustomers.remove(customerInfo); | |
112 | + return true; | |
113 | + } else { | |
114 | + return false; | |
115 | + } | |
116 | + } | |
117 | + | |
68 | 118 | @Override |
69 | 119 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) |
70 | 120 | public String getName() { |
... | ... | @@ -80,7 +130,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
80 | 130 | public int hashCode() { |
81 | 131 | final int prime = 31; |
82 | 132 | int result = super.hashCode(); |
83 | - result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); | |
84 | 133 | result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); |
85 | 134 | result = prime * result + ((title == null) ? 0 : title.hashCode()); |
86 | 135 | return result; |
... | ... | @@ -95,11 +144,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
95 | 144 | if (getClass() != obj.getClass()) |
96 | 145 | return false; |
97 | 146 | 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 | 147 | if (tenantId == null) { |
104 | 148 | if (other.tenantId != null) |
105 | 149 | return false; |
... | ... | @@ -118,8 +162,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
118 | 162 | StringBuilder builder = new StringBuilder(); |
119 | 163 | builder.append("DashboardInfo [tenantId="); |
120 | 164 | builder.append(tenantId); |
121 | - builder.append(", customerId="); | |
122 | - builder.append(customerId); | |
123 | 165 | builder.append(", title="); |
124 | 166 | builder.append(title); |
125 | 167 | builder.append("]"); | ... | ... |
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.common.data; | |
17 | + | |
18 | +import lombok.AllArgsConstructor; | |
19 | +import lombok.Getter; | |
20 | +import lombok.Setter; | |
21 | +import org.thingsboard.server.common.data.id.CustomerId; | |
22 | + | |
23 | +/** | |
24 | + * Created by igor on 2/27/18. | |
25 | + */ | |
26 | + | |
27 | +@AllArgsConstructor | |
28 | +public class ShortCustomerInfo { | |
29 | + | |
30 | + @Getter @Setter | |
31 | + private CustomerId customerId; | |
32 | + | |
33 | + @Getter @Setter | |
34 | + private String title; | |
35 | + | |
36 | + @Getter @Setter | |
37 | + private boolean isPublic; | |
38 | + | |
39 | + @Override | |
40 | + public boolean equals(Object o) { | |
41 | + if (this == o) return true; | |
42 | + if (o == null || getClass() != o.getClass()) return false; | |
43 | + | |
44 | + ShortCustomerInfo that = (ShortCustomerInfo) o; | |
45 | + | |
46 | + return customerId.equals(that.customerId); | |
47 | + | |
48 | + } | |
49 | + | |
50 | + @Override | |
51 | + public int hashCode() { | |
52 | + return customerId.hashCode(); | |
53 | + } | |
54 | +} | ... | ... |
... | ... | @@ -97,7 +97,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom |
97 | 97 | public Customer saveCustomer(Customer customer) { |
98 | 98 | log.trace("Executing saveCustomer [{}]", customer); |
99 | 99 | customerValidator.validate(customer); |
100 | - return customerDao.save(customer); | |
100 | + Customer savedCustomer = customerDao.save(customer); | |
101 | + dashboardService.updateCustomerDashboards(savedCustomer.getId()); | |
102 | + return savedCustomer; | |
101 | 103 | } |
102 | 104 | |
103 | 105 | @Override |
... | ... | @@ -108,7 +110,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom |
108 | 110 | if (customer == null) { |
109 | 111 | throw new IncorrectParameterException("Unable to delete non-existent customer."); |
110 | 112 | } |
111 | - dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId); | |
113 | + dashboardService.unassignCustomerDashboards(customerId); | |
112 | 114 | assetService.unassignCustomerAssets(customer.getTenantId(), customerId); |
113 | 115 | deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); |
114 | 116 | userService.deleteCustomerUsers(customer.getTenantId(), customerId); | ... | ... |
... | ... | @@ -15,16 +15,26 @@ |
15 | 15 | */ |
16 | 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 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | +import org.springframework.beans.factory.annotation.Autowired; | |
19 | 23 | import org.springframework.stereotype.Component; |
20 | 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 | 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 | 31 | import org.thingsboard.server.dao.DaoUtil; |
23 | 32 | import org.thingsboard.server.dao.model.nosql.DashboardInfoEntity; |
24 | 33 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; |
34 | +import org.thingsboard.server.dao.relation.RelationDao; | |
25 | 35 | import org.thingsboard.server.dao.util.NoSqlDao; |
26 | 36 | |
27 | -import java.util.Arrays; | |
37 | +import java.util.ArrayList; | |
28 | 38 | import java.util.Collections; |
29 | 39 | import java.util.List; |
30 | 40 | import java.util.UUID; |
... | ... | @@ -37,6 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; |
37 | 47 | @NoSqlDao |
38 | 48 | public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { |
39 | 49 | |
50 | + @Autowired | |
51 | + private RelationDao relationDao; | |
52 | + | |
40 | 53 | @Override |
41 | 54 | protected Class<DashboardInfoEntity> getColumnFamilyClass() { |
42 | 55 | return DashboardInfoEntity.class; |
... | ... | @@ -59,15 +72,18 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da |
59 | 72 | } |
60 | 73 | |
61 | 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 | 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 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
18 | 19 | import org.thingsboard.server.common.data.DashboardInfo; |
19 | 20 | import org.thingsboard.server.common.data.page.TextPageLink; |
21 | +import org.thingsboard.server.common.data.page.TimePageLink; | |
20 | 22 | import org.thingsboard.server.dao.Dao; |
21 | 23 | |
22 | 24 | import java.util.List; |
... | ... | @@ -44,6 +46,6 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> { |
44 | 46 | * @param pageLink the page link |
45 | 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 | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 24 | import org.thingsboard.server.common.data.page.TextPageData; |
25 | 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.util.Set; | |
26 | 30 | |
27 | 31 | public interface DashboardService { |
28 | 32 | |
... | ... | @@ -38,7 +42,7 @@ public interface DashboardService { |
38 | 42 | |
39 | 43 | Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); |
40 | 44 | |
41 | - Dashboard unassignDashboardFromCustomer(DashboardId dashboardId); | |
45 | + Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId); | |
42 | 46 | |
43 | 47 | void deleteDashboard(DashboardId dashboardId); |
44 | 48 | |
... | ... | @@ -46,8 +50,10 @@ public interface DashboardService { |
46 | 50 | |
47 | 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); | |
54 | + | |
55 | + void unassignCustomerDashboards(CustomerId customerId); | |
50 | 56 | |
51 | - void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId); | |
57 | + void updateCustomerDashboards(CustomerId customerId); | |
52 | 58 | |
53 | 59 | } | ... | ... |
... | ... | @@ -15,30 +15,42 @@ |
15 | 15 | */ |
16 | 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 | 20 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 21 | import lombok.extern.slf4j.Slf4j; |
20 | 22 | import org.apache.commons.lang3.StringUtils; |
21 | 23 | import org.springframework.beans.factory.annotation.Autowired; |
22 | 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 | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
28 | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
29 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | 30 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | 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 | 36 | import org.thingsboard.server.dao.customer.CustomerDao; |
33 | 37 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
34 | 38 | import org.thingsboard.server.dao.exception.DataValidationException; |
35 | 39 | import org.thingsboard.server.dao.model.ModelConstants; |
40 | +import org.thingsboard.server.dao.relation.RelationDao; | |
36 | 41 | import org.thingsboard.server.dao.service.DataValidator; |
37 | 42 | import org.thingsboard.server.dao.service.PaginatedRemover; |
43 | +import org.thingsboard.server.dao.service.TimePaginatedRemover; | |
38 | 44 | import org.thingsboard.server.dao.service.Validator; |
39 | 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 | 51 | import java.util.List; |
52 | +import java.util.Set; | |
53 | +import java.util.concurrent.ExecutionException; | |
42 | 54 | |
43 | 55 | import static org.thingsboard.server.dao.service.Validator.validateId; |
44 | 56 | |
... | ... | @@ -59,7 +71,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
59 | 71 | |
60 | 72 | @Autowired |
61 | 73 | private CustomerDao customerDao; |
62 | - | |
74 | + | |
63 | 75 | @Override |
64 | 76 | public Dashboard findDashboardById(DashboardId dashboardId) { |
65 | 77 | log.trace("Executing findDashboardById [{}]", dashboardId); |
... | ... | @@ -98,15 +110,63 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
98 | 110 | @Override |
99 | 111 | public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { |
100 | 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(customer)) { | |
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 | 133 | @Override |
106 | - public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId) { | |
134 | + public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { | |
107 | 135 | Dashboard dashboard = findDashboardById(dashboardId); |
108 | - dashboard.setCustomerId(null); | |
109 | - return saveDashboard(dashboard); | |
136 | + Customer customer = customerDao.findById(customerId.getId()); | |
137 | + if (customer == null) { | |
138 | + throw new DataValidationException("Can't unassign dashboard from non-existent customer!"); | |
139 | + } | |
140 | + if (dashboard.removeAssignedCustomer(customer)) { | |
141 | + try { | |
142 | + deleteRelation(new EntityRelation(customerId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD)); | |
143 | + } catch (ExecutionException | InterruptedException e) { | |
144 | + log.warn("[{}] Failed to delete dashboard relation. Customer Id: [{}]", dashboardId, customerId); | |
145 | + throw new RuntimeException(e); | |
146 | + } | |
147 | + return saveDashboard(dashboard); | |
148 | + } else { | |
149 | + return dashboard; | |
150 | + } | |
151 | + } | |
152 | + | |
153 | + private Dashboard updateAssignedCustomer(DashboardId dashboardId, Customer customer) { | |
154 | + Dashboard dashboard = findDashboardById(dashboardId); | |
155 | + if (dashboard.updateAssignedCustomer(customer)) { | |
156 | + return saveDashboard(dashboard); | |
157 | + } else { | |
158 | + return dashboard; | |
159 | + } | |
160 | + } | |
161 | + | |
162 | + private void deleteRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { | |
163 | + log.debug("Deleting Dashboard relation: {}", dashboardRelation); | |
164 | + relationService.deleteRelationAsync(dashboardRelation).get(); | |
165 | + } | |
166 | + | |
167 | + private void createRelation(EntityRelation dashboardRelation) throws ExecutionException, InterruptedException { | |
168 | + log.debug("Creating Dashboard relation: {}", dashboardRelation); | |
169 | + relationService.saveRelationAsync(dashboardRelation).get(); | |
110 | 170 | } |
111 | 171 | |
112 | 172 | @Override |
... | ... | @@ -134,23 +194,44 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
134 | 194 | } |
135 | 195 | |
136 | 196 | @Override |
137 | - public TextPageData<DashboardInfo> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink) { | |
197 | + public ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink) { | |
138 | 198 | log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); |
139 | 199 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
140 | 200 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
141 | 201 | Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); |
142 | - List<DashboardInfo> dashboards = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); | |
143 | - return new TextPageData<>(dashboards, pageLink); | |
202 | + ListenableFuture<List<DashboardInfo>> dashboards = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); | |
203 | + | |
204 | + return Futures.transform(dashboards, new Function<List<DashboardInfo>, TimePageData<DashboardInfo>>() { | |
205 | + @Nullable | |
206 | + @Override | |
207 | + public TimePageData<DashboardInfo> apply(@Nullable List<DashboardInfo> dashboards) { | |
208 | + return new TimePageData<>(dashboards, pageLink); | |
209 | + } | |
210 | + }); | |
144 | 211 | } |
145 | 212 | |
146 | 213 | @Override |
147 | - public void unassignCustomerDashboards(TenantId tenantId, CustomerId customerId) { | |
148 | - log.trace("Executing unassignCustomerDashboards, tenantId [{}], customerId [{}]", tenantId, customerId); | |
149 | - Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | |
214 | + public void unassignCustomerDashboards(CustomerId customerId) { | |
215 | + log.trace("Executing unassignCustomerDashboards, customerId [{}]", customerId); | |
150 | 216 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
151 | - new CustomerDashboardsUnassigner(tenantId).removeEntities(customerId); | |
217 | + Customer customer = customerDao.findById(customerId.getId()); | |
218 | + if (customer == null) { | |
219 | + throw new DataValidationException("Can't unassign dashboards from non-existent customer!"); | |
220 | + } | |
221 | + new CustomerDashboardsUnassigner(customer).removeEntities(customer); | |
152 | 222 | } |
153 | - | |
223 | + | |
224 | + @Override | |
225 | + public void updateCustomerDashboards(CustomerId customerId) { | |
226 | + log.trace("Executing updateCustomerDashboards, customerId [{}]", customerId); | |
227 | + Validator.validateId(customerId, "Incorrect customerId " + customerId); | |
228 | + Customer customer = customerDao.findById(customerId.getId()); | |
229 | + if (customer == null) { | |
230 | + throw new DataValidationException("Can't update dashboards for non-existent customer!"); | |
231 | + } | |
232 | + new CustomerDashboardsUpdater(customer).removeEntities(customer); | |
233 | + } | |
234 | + | |
154 | 235 | private DataValidator<Dashboard> dashboardValidator = |
155 | 236 | new DataValidator<Dashboard>() { |
156 | 237 | @Override |
... | ... | @@ -166,17 +247,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
166 | 247 | throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); |
167 | 248 | } |
168 | 249 | } |
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 | 250 | } |
181 | 251 | }; |
182 | 252 | |
... | ... | @@ -194,24 +264,54 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb |
194 | 264 | } |
195 | 265 | }; |
196 | 266 | |
197 | - private class CustomerDashboardsUnassigner extends PaginatedRemover<CustomerId, DashboardInfo> { | |
198 | - | |
199 | - private TenantId tenantId; | |
267 | + private class CustomerDashboardsUnassigner extends TimePaginatedRemover<Customer, DashboardInfo> { | |
200 | 268 | |
201 | - CustomerDashboardsUnassigner(TenantId tenantId) { | |
202 | - this.tenantId = tenantId; | |
269 | + private Customer customer; | |
270 | + | |
271 | + CustomerDashboardsUnassigner(Customer customer) { | |
272 | + this.customer = customer; | |
203 | 273 | } |
204 | 274 | |
205 | 275 | @Override |
206 | - protected List<DashboardInfo> findEntities(CustomerId id, TextPageLink pageLink) { | |
207 | - return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink); | |
276 | + protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) { | |
277 | + try { | |
278 | + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(customer.getTenantId().getId(), customer.getId().getId(), pageLink).get(); | |
279 | + } catch (InterruptedException | ExecutionException e) { | |
280 | + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", customer.getTenantId().getId(), customer.getId().getId()); | |
281 | + throw new RuntimeException(e); | |
282 | + } | |
208 | 283 | } |
209 | 284 | |
210 | 285 | @Override |
211 | 286 | protected void removeEntity(DashboardInfo entity) { |
212 | - unassignDashboardFromCustomer(new DashboardId(entity.getUuidId())); | |
287 | + unassignDashboardFromCustomer(new DashboardId(entity.getUuidId()), this.customer.getId()); | |
213 | 288 | } |
214 | 289 | |
215 | 290 | } |
216 | 291 | |
292 | + private class CustomerDashboardsUpdater extends TimePaginatedRemover<Customer, DashboardInfo> { | |
293 | + | |
294 | + private Customer customer; | |
295 | + | |
296 | + CustomerDashboardsUpdater(Customer customer) { | |
297 | + this.customer = customer; | |
298 | + } | |
299 | + | |
300 | + @Override | |
301 | + protected List<DashboardInfo> findEntities(Customer customer, TimePageLink pageLink) { | |
302 | + try { | |
303 | + return dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(customer.getTenantId().getId(), customer.getId().getId(), pageLink).get(); | |
304 | + } catch (InterruptedException | ExecutionException e) { | |
305 | + log.warn("Failed to get dashboards by tenantId [{}] and customerId [{}].", customer.getTenantId().getId(), customer.getId().getId()); | |
306 | + throw new RuntimeException(e); | |
307 | + } | |
308 | + } | |
309 | + | |
310 | + @Override | |
311 | + protected void removeEntity(DashboardInfo entity) { | |
312 | + updateAssignedCustomer(new DashboardId(entity.getUuidId()), this.customer); | |
313 | + } | |
314 | + | |
315 | + } | |
316 | + | |
217 | 317 | } | ... | ... |
... | ... | @@ -266,13 +266,11 @@ public class ModelConstants { |
266 | 266 | */ |
267 | 267 | public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; |
268 | 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 | 269 | public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; |
271 | 270 | public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; |
271 | + public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; | |
272 | 272 | |
273 | 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 | 276 | * Cassandra plugin metadata constants. | ... | ... |
... | ... | @@ -19,16 +19,23 @@ import com.datastax.driver.core.utils.UUIDs; |
19 | 19 | import com.datastax.driver.mapping.annotations.Column; |
20 | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
23 | +import com.fasterxml.jackson.databind.JavaType; | |
22 | 24 | import com.fasterxml.jackson.databind.JsonNode; |
25 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
23 | 26 | import lombok.EqualsAndHashCode; |
24 | 27 | import lombok.ToString; |
28 | +import lombok.extern.slf4j.Slf4j; | |
29 | +import org.springframework.util.StringUtils; | |
25 | 30 | import org.thingsboard.server.common.data.Dashboard; |
26 | -import org.thingsboard.server.common.data.id.CustomerId; | |
31 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
27 | 32 | import org.thingsboard.server.common.data.id.DashboardId; |
28 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
29 | 34 | import org.thingsboard.server.dao.model.SearchTextEntity; |
30 | 35 | import org.thingsboard.server.dao.model.type.JsonCodec; |
31 | 36 | |
37 | +import java.io.IOException; | |
38 | +import java.util.HashSet; | |
32 | 39 | import java.util.UUID; |
33 | 40 | |
34 | 41 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
... | ... | @@ -36,8 +43,13 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; |
36 | 43 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) |
37 | 44 | @EqualsAndHashCode |
38 | 45 | @ToString |
46 | +@Slf4j | |
39 | 47 | public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
40 | - | |
48 | + | |
49 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | |
50 | + private static final JavaType assignedCustomersType = | |
51 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
52 | + | |
41 | 53 | @PartitionKey(value = 0) |
42 | 54 | @Column(name = ID_PROPERTY) |
43 | 55 | private UUID id; |
... | ... | @@ -46,16 +58,15 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
46 | 58 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) |
47 | 59 | private UUID tenantId; |
48 | 60 | |
49 | - @PartitionKey(value = 2) | |
50 | - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) | |
51 | - private UUID customerId; | |
52 | - | |
53 | 61 | @Column(name = DASHBOARD_TITLE_PROPERTY) |
54 | 62 | private String title; |
55 | 63 | |
56 | 64 | @Column(name = SEARCH_TEXT_PROPERTY) |
57 | 65 | private String searchText; |
58 | - | |
66 | + | |
67 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | |
68 | + private String assignedCustomers; | |
69 | + | |
59 | 70 | @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) |
60 | 71 | private JsonNode configuration; |
61 | 72 | |
... | ... | @@ -70,10 +81,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
70 | 81 | if (dashboard.getTenantId() != null) { |
71 | 82 | this.tenantId = dashboard.getTenantId().getId(); |
72 | 83 | } |
73 | - if (dashboard.getCustomerId() != null) { | |
74 | - this.customerId = dashboard.getCustomerId().getId(); | |
75 | - } | |
76 | 84 | this.title = dashboard.getTitle(); |
85 | + if (dashboard.getAssignedCustomers() != null) { | |
86 | + try { | |
87 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers()); | |
88 | + } catch (JsonProcessingException e) { | |
89 | + log.error("Unable to serialize assigned customers to string!", e); | |
90 | + } | |
91 | + } | |
77 | 92 | this.configuration = dashboard.getConfiguration(); |
78 | 93 | } |
79 | 94 | |
... | ... | @@ -93,14 +108,6 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
93 | 108 | this.tenantId = tenantId; |
94 | 109 | } |
95 | 110 | |
96 | - public UUID getCustomerId() { | |
97 | - return customerId; | |
98 | - } | |
99 | - | |
100 | - public void setCustomerId(UUID customerId) { | |
101 | - this.customerId = customerId; | |
102 | - } | |
103 | - | |
104 | 111 | public String getTitle() { |
105 | 112 | return title; |
106 | 113 | } |
... | ... | @@ -109,6 +116,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
109 | 116 | this.title = title; |
110 | 117 | } |
111 | 118 | |
119 | + public String getAssignedCustomers() { | |
120 | + return assignedCustomers; | |
121 | + } | |
122 | + | |
123 | + public void setAssignedCustomers(String assignedCustomers) { | |
124 | + this.assignedCustomers = assignedCustomers; | |
125 | + } | |
126 | + | |
112 | 127 | public JsonNode getConfiguration() { |
113 | 128 | return configuration; |
114 | 129 | } |
... | ... | @@ -138,10 +153,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { |
138 | 153 | if (tenantId != null) { |
139 | 154 | dashboard.setTenantId(new TenantId(tenantId)); |
140 | 155 | } |
141 | - if (customerId != null) { | |
142 | - dashboard.setCustomerId(new CustomerId(customerId)); | |
143 | - } | |
144 | 156 | dashboard.setTitle(title); |
157 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
158 | + try { | |
159 | + dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
160 | + } catch (IOException e) { | |
161 | + log.warn("Unable to parse assigned customers!", e); | |
162 | + } | |
163 | + } | |
145 | 164 | dashboard.setConfiguration(configuration); |
146 | 165 | return dashboard; |
147 | 166 | } | ... | ... |
... | ... | @@ -19,14 +19,21 @@ import com.datastax.driver.core.utils.UUIDs; |
19 | 19 | import com.datastax.driver.mapping.annotations.Column; |
20 | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
23 | +import com.fasterxml.jackson.databind.JavaType; | |
24 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
22 | 25 | import lombok.EqualsAndHashCode; |
23 | 26 | import lombok.ToString; |
27 | +import lombok.extern.slf4j.Slf4j; | |
28 | +import org.springframework.util.StringUtils; | |
24 | 29 | import org.thingsboard.server.common.data.DashboardInfo; |
25 | -import org.thingsboard.server.common.data.id.CustomerId; | |
30 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
26 | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
27 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 33 | import org.thingsboard.server.dao.model.SearchTextEntity; |
29 | 34 | |
35 | +import java.io.IOException; | |
36 | +import java.util.HashSet; | |
30 | 37 | import java.util.UUID; |
31 | 38 | |
32 | 39 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
... | ... | @@ -34,8 +41,13 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; |
34 | 41 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) |
35 | 42 | @EqualsAndHashCode |
36 | 43 | @ToString |
44 | +@Slf4j | |
37 | 45 | public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
38 | 46 | |
47 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | |
48 | + private static final JavaType assignedCustomersType = | |
49 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
50 | + | |
39 | 51 | @PartitionKey(value = 0) |
40 | 52 | @Column(name = ID_PROPERTY) |
41 | 53 | private UUID id; |
... | ... | @@ -44,16 +56,15 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
44 | 56 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) |
45 | 57 | private UUID tenantId; |
46 | 58 | |
47 | - @PartitionKey(value = 2) | |
48 | - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) | |
49 | - private UUID customerId; | |
50 | - | |
51 | 59 | @Column(name = DASHBOARD_TITLE_PROPERTY) |
52 | 60 | private String title; |
53 | 61 | |
54 | 62 | @Column(name = SEARCH_TEXT_PROPERTY) |
55 | 63 | private String searchText; |
56 | 64 | |
65 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | |
66 | + private String assignedCustomers; | |
67 | + | |
57 | 68 | public DashboardInfoEntity() { |
58 | 69 | super(); |
59 | 70 | } |
... | ... | @@ -65,10 +76,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
65 | 76 | if (dashboardInfo.getTenantId() != null) { |
66 | 77 | this.tenantId = dashboardInfo.getTenantId().getId(); |
67 | 78 | } |
68 | - if (dashboardInfo.getCustomerId() != null) { | |
69 | - this.customerId = dashboardInfo.getCustomerId().getId(); | |
70 | - } | |
71 | 79 | this.title = dashboardInfo.getTitle(); |
80 | + if (dashboardInfo.getAssignedCustomers() != null) { | |
81 | + try { | |
82 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers()); | |
83 | + } catch (JsonProcessingException e) { | |
84 | + log.error("Unable to serialize assigned customers to string!", e); | |
85 | + } | |
86 | + } | |
72 | 87 | } |
73 | 88 | |
74 | 89 | public UUID getId() { |
... | ... | @@ -87,14 +102,6 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
87 | 102 | this.tenantId = tenantId; |
88 | 103 | } |
89 | 104 | |
90 | - public UUID getCustomerId() { | |
91 | - return customerId; | |
92 | - } | |
93 | - | |
94 | - public void setCustomerId(UUID customerId) { | |
95 | - this.customerId = customerId; | |
96 | - } | |
97 | - | |
98 | 105 | public String getTitle() { |
99 | 106 | return title; |
100 | 107 | } |
... | ... | @@ -103,6 +110,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
103 | 110 | this.title = title; |
104 | 111 | } |
105 | 112 | |
113 | + public String getAssignedCustomers() { | |
114 | + return assignedCustomers; | |
115 | + } | |
116 | + | |
117 | + public void setAssignedCustomers(String assignedCustomers) { | |
118 | + this.assignedCustomers = assignedCustomers; | |
119 | + } | |
120 | + | |
106 | 121 | @Override |
107 | 122 | public String getSearchTextSource() { |
108 | 123 | return getTitle(); |
... | ... | @@ -124,10 +139,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
124 | 139 | if (tenantId != null) { |
125 | 140 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
126 | 141 | } |
127 | - if (customerId != null) { | |
128 | - dashboardInfo.setCustomerId(new CustomerId(customerId)); | |
129 | - } | |
130 | 142 | dashboardInfo.setTitle(title); |
143 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
144 | + try { | |
145 | + dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
146 | + } catch (IOException e) { | |
147 | + log.warn("Unable to parse assigned customers!", e); | |
148 | + } | |
149 | + } | |
131 | 150 | return dashboardInfo; |
132 | 151 | } |
133 | 152 | ... | ... |
... | ... | @@ -16,13 +16,18 @@ |
16 | 16 | package org.thingsboard.server.dao.model.sql; |
17 | 17 | |
18 | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
20 | +import com.fasterxml.jackson.databind.JavaType; | |
19 | 21 | import com.fasterxml.jackson.databind.JsonNode; |
22 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
20 | 23 | import lombok.Data; |
21 | 24 | import lombok.EqualsAndHashCode; |
25 | +import lombok.extern.slf4j.Slf4j; | |
22 | 26 | import org.hibernate.annotations.Type; |
23 | 27 | import org.hibernate.annotations.TypeDef; |
28 | +import org.springframework.util.StringUtils; | |
24 | 29 | import org.thingsboard.server.common.data.Dashboard; |
25 | -import org.thingsboard.server.common.data.id.CustomerId; | |
30 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
26 | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
27 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 33 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
... | ... | @@ -33,26 +38,33 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; |
33 | 38 | import javax.persistence.Column; |
34 | 39 | import javax.persistence.Entity; |
35 | 40 | import javax.persistence.Table; |
41 | +import java.io.IOException; | |
42 | +import java.util.HashSet; | |
36 | 43 | |
37 | 44 | @Data |
45 | +@Slf4j | |
38 | 46 | @EqualsAndHashCode(callSuper = true) |
39 | 47 | @Entity |
40 | 48 | @TypeDef(name = "json", typeClass = JsonStringType.class) |
41 | 49 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) |
42 | 50 | public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> { |
43 | 51 | |
52 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | |
53 | + private static final JavaType assignedCustomersType = | |
54 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
55 | + | |
44 | 56 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
45 | 57 | private String tenantId; |
46 | 58 | |
47 | - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) | |
48 | - private String customerId; | |
49 | - | |
50 | 59 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) |
51 | 60 | private String title; |
52 | 61 | |
53 | 62 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
54 | 63 | private String searchText; |
55 | 64 | |
65 | + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | |
66 | + private String assignedCustomers; | |
67 | + | |
56 | 68 | @Type(type = "json") |
57 | 69 | @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) |
58 | 70 | private JsonNode configuration; |
... | ... | @@ -68,10 +80,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S |
68 | 80 | if (dashboard.getTenantId() != null) { |
69 | 81 | this.tenantId = toString(dashboard.getTenantId().getId()); |
70 | 82 | } |
71 | - if (dashboard.getCustomerId() != null) { | |
72 | - this.customerId = toString(dashboard.getCustomerId().getId()); | |
73 | - } | |
74 | 83 | this.title = dashboard.getTitle(); |
84 | + if (dashboard.getAssignedCustomers() != null) { | |
85 | + try { | |
86 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboard.getAssignedCustomers()); | |
87 | + } catch (JsonProcessingException e) { | |
88 | + log.error("Unable to serialize assigned customers to string!", e); | |
89 | + } | |
90 | + } | |
75 | 91 | this.configuration = dashboard.getConfiguration(); |
76 | 92 | } |
77 | 93 | |
... | ... | @@ -92,10 +108,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S |
92 | 108 | if (tenantId != null) { |
93 | 109 | dashboard.setTenantId(new TenantId(toUUID(tenantId))); |
94 | 110 | } |
95 | - if (customerId != null) { | |
96 | - dashboard.setCustomerId(new CustomerId(toUUID(customerId))); | |
97 | - } | |
98 | 111 | dashboard.setTitle(title); |
112 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
113 | + try { | |
114 | + dashboard.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
115 | + } catch (IOException e) { | |
116 | + log.warn("Unable to parse assigned customers!", e); | |
117 | + } | |
118 | + } | |
99 | 119 | dashboard.setConfiguration(configuration); |
100 | 120 | return dashboard; |
101 | 121 | } | ... | ... |
... | ... | @@ -16,10 +16,15 @@ |
16 | 16 | package org.thingsboard.server.dao.model.sql; |
17 | 17 | |
18 | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
20 | +import com.fasterxml.jackson.databind.JavaType; | |
21 | +import com.fasterxml.jackson.databind.ObjectMapper; | |
19 | 22 | import lombok.Data; |
20 | 23 | import lombok.EqualsAndHashCode; |
24 | +import lombok.extern.slf4j.Slf4j; | |
25 | +import org.springframework.util.StringUtils; | |
21 | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
22 | -import org.thingsboard.server.common.data.id.CustomerId; | |
27 | +import org.thingsboard.server.common.data.ShortCustomerInfo; | |
23 | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
24 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 30 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
... | ... | @@ -29,25 +34,32 @@ import org.thingsboard.server.dao.model.SearchTextEntity; |
29 | 34 | import javax.persistence.Column; |
30 | 35 | import javax.persistence.Entity; |
31 | 36 | import javax.persistence.Table; |
37 | +import java.io.IOException; | |
38 | +import java.util.HashSet; | |
32 | 39 | |
33 | 40 | @Data |
41 | +@Slf4j | |
34 | 42 | @EqualsAndHashCode(callSuper = true) |
35 | 43 | @Entity |
36 | 44 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) |
37 | 45 | public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { |
38 | 46 | |
47 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | |
48 | + private static final JavaType assignedCustomersType = | |
49 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | |
50 | + | |
39 | 51 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
40 | 52 | private String tenantId; |
41 | 53 | |
42 | - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) | |
43 | - private String customerId; | |
44 | - | |
45 | 54 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) |
46 | 55 | private String title; |
47 | 56 | |
48 | 57 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
49 | 58 | private String searchText; |
50 | 59 | |
60 | + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | |
61 | + private String assignedCustomers; | |
62 | + | |
51 | 63 | public DashboardInfoEntity() { |
52 | 64 | super(); |
53 | 65 | } |
... | ... | @@ -59,10 +71,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements |
59 | 71 | if (dashboardInfo.getTenantId() != null) { |
60 | 72 | this.tenantId = toString(dashboardInfo.getTenantId().getId()); |
61 | 73 | } |
62 | - if (dashboardInfo.getCustomerId() != null) { | |
63 | - this.customerId = toString(dashboardInfo.getCustomerId().getId()); | |
64 | - } | |
65 | 74 | this.title = dashboardInfo.getTitle(); |
75 | + if (dashboardInfo.getAssignedCustomers() != null) { | |
76 | + try { | |
77 | + this.assignedCustomers = objectMapper.writeValueAsString(dashboardInfo.getAssignedCustomers()); | |
78 | + } catch (JsonProcessingException e) { | |
79 | + log.error("Unable to serialize assigned customers to string!", e); | |
80 | + } | |
81 | + } | |
66 | 82 | } |
67 | 83 | |
68 | 84 | @Override |
... | ... | @@ -86,10 +102,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements |
86 | 102 | if (tenantId != null) { |
87 | 103 | dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); |
88 | 104 | } |
89 | - if (customerId != null) { | |
90 | - dashboardInfo.setCustomerId(new CustomerId(toUUID(customerId))); | |
91 | - } | |
92 | 105 | dashboardInfo.setTitle(title); |
106 | + if (!StringUtils.isEmpty(assignedCustomers)) { | |
107 | + try { | |
108 | + dashboardInfo.setAssignedCustomers(objectMapper.readValue(assignedCustomers, assignedCustomersType)); | |
109 | + } catch (IOException e) { | |
110 | + log.warn("Unable to parse assigned customers!", e); | |
111 | + } | |
112 | + } | |
93 | 113 | return dashboardInfo; |
94 | 114 | } |
95 | 115 | ... | ... |
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 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.id.EntityId; |
19 | 19 | import org.thingsboard.server.common.data.id.UUIDBased; |
20 | +import org.thingsboard.server.common.data.page.BasePageLink; | |
20 | 21 | import org.thingsboard.server.common.data.page.TextPageLink; |
21 | 22 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
22 | 23 | |
... | ... | @@ -116,7 +117,7 @@ public class Validator { |
116 | 117 | * @param pageLink the page link |
117 | 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 | 121 | if (pageLink == null || pageLink.getLimit() < 1 || (pageLink.getIdOffset() != null && pageLink.getIdOffset().version() != 1)) { |
121 | 122 | throw new IncorrectParameterException(errorMessage); |
122 | 123 | } | ... | ... |
... | ... | @@ -39,12 +39,4 @@ public interface DashboardInfoRepository extends CrudRepository<DashboardInfoEnt |
39 | 39 | @Param("idOffset") String idOffset, |
40 | 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 | 15 | */ |
16 | 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 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
19 | 23 | import org.springframework.data.domain.PageRequest; |
20 | 24 | import org.springframework.data.repository.CrudRepository; |
21 | 25 | import org.springframework.stereotype.Component; |
22 | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
27 | +import org.thingsboard.server.common.data.EntityType; | |
23 | 28 | import org.thingsboard.server.common.data.UUIDConverter; |
29 | +import org.thingsboard.server.common.data.id.CustomerId; | |
24 | 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 | 34 | import org.thingsboard.server.dao.DaoUtil; |
26 | 35 | import org.thingsboard.server.dao.dashboard.DashboardInfoDao; |
27 | 36 | import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; |
37 | +import org.thingsboard.server.dao.relation.RelationDao; | |
28 | 38 | import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
29 | 39 | import org.thingsboard.server.dao.util.SqlDao; |
30 | 40 | |
41 | +import java.sql.Time; | |
42 | +import java.util.ArrayList; | |
31 | 43 | import java.util.List; |
32 | 44 | import java.util.Objects; |
33 | 45 | import java.util.UUID; |
... | ... | @@ -37,11 +49,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; |
37 | 49 | /** |
38 | 50 | * Created by Valerii Sosliuk on 5/6/2017. |
39 | 51 | */ |
52 | +@Slf4j | |
40 | 53 | @Component |
41 | 54 | @SqlDao |
42 | 55 | public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { |
43 | 56 | |
44 | 57 | @Autowired |
58 | + private RelationDao relationDao; | |
59 | + | |
60 | + @Autowired | |
45 | 61 | private DashboardInfoRepository dashboardInfoRepository; |
46 | 62 | |
47 | 63 | @Override |
... | ... | @@ -65,13 +81,17 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE |
65 | 81 | } |
66 | 82 | |
67 | 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 | 364 | CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( |
365 | 365 | id timeuuid, |
366 | 366 | tenant_id timeuuid, |
367 | - customer_id timeuuid, | |
368 | 367 | title text, |
369 | 368 | search_text text, |
369 | + assigned_customers text, | |
370 | 370 | configuration text, |
371 | - PRIMARY KEY (id, tenant_id, customer_id) | |
371 | + PRIMARY KEY (id, tenant_id) | |
372 | 372 | ); |
373 | 373 | |
374 | 374 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS |
375 | 375 | SELECT * |
376 | 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 | 381 | CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( |
389 | 382 | entity_type text, // (DEVICE, CUSTOMER, TENANT) | ... | ... |
... | ... | @@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS customer ( |
105 | 105 | CREATE TABLE IF NOT EXISTS dashboard ( |
106 | 106 | id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, |
107 | 107 | configuration varchar(10000000), |
108 | - customer_id varchar(31), | |
108 | + assigned_customers varchar(1000000), | |
109 | 109 | search_text varchar(255), |
110 | 110 | tenant_id varchar(31), |
111 | 111 | title varchar(255) | ... | ... |
... | ... | @@ -29,13 +29,17 @@ import org.thingsboard.server.common.data.id.CustomerId; |
29 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | 30 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | 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 | 34 | import org.thingsboard.server.dao.exception.DataValidationException; |
33 | 35 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | 36 | |
35 | 37 | import java.io.IOException; |
38 | +import java.sql.Time; | |
36 | 39 | import java.util.ArrayList; |
37 | 40 | import java.util.Collections; |
38 | 41 | import java.util.List; |
42 | +import java.util.concurrent.ExecutionException; | |
39 | 43 | |
40 | 44 | public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
41 | 45 | |
... | ... | @@ -68,8 +72,6 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
68 | 72 | Assert.assertNotNull(savedDashboard.getId()); |
69 | 73 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); |
70 | 74 | Assert.assertEquals(dashboard.getTenantId(), savedDashboard.getTenantId()); |
71 | - Assert.assertNotNull(savedDashboard.getCustomerId()); | |
72 | - Assert.assertEquals(ModelConstants.NULL_UUID, savedDashboard.getCustomerId().getId()); | |
73 | 75 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); |
74 | 76 | |
75 | 77 | savedDashboard.setTitle("My new dashboard"); |
... | ... | @@ -280,7 +282,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
280 | 282 | } |
281 | 283 | |
282 | 284 | @Test |
283 | - public void testFindDashboardsByTenantIdAndCustomerId() { | |
285 | + public void testFindDashboardsByTenantIdAndCustomerId() throws ExecutionException, InterruptedException { | |
284 | 286 | Tenant tenant = new Tenant(); |
285 | 287 | tenant.setTitle("Test tenant"); |
286 | 288 | tenant = tenantService.saveTenant(tenant); |
... | ... | @@ -303,10 +305,10 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
303 | 305 | } |
304 | 306 | |
305 | 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 | 310 | do { |
309 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | |
311 | + pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); | |
310 | 312 | loadedDashboards.addAll(pageData.getData()); |
311 | 313 | if (pageData.hasNext()) { |
312 | 314 | pageLink = pageData.getNextPageLink(); |
... | ... | @@ -318,98 +320,14 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
318 | 320 | |
319 | 321 | Assert.assertEquals(dashboards, loadedDashboards); |
320 | 322 | |
321 | - dashboardService.unassignCustomerDashboards(tenantId, customerId); | |
323 | + dashboardService.unassignCustomerDashboards(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 | 327 | Assert.assertFalse(pageData.hasNext()); |
326 | 328 | Assert.assertTrue(pageData.getData().isEmpty()); |
327 | 329 | |
328 | 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 | 16 | package org.thingsboard.server.dao.sql.dashboard; |
17 | 17 | |
18 | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import org.junit.Assert; | |
19 | 20 | import org.junit.Test; |
20 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 22 | import org.thingsboard.server.common.data.DashboardInfo; |
... | ... | @@ -40,53 +41,26 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { |
40 | 41 | @Test |
41 | 42 | public void testFindDashboardsByTenantId() { |
42 | 43 | UUID tenantId1 = UUIDs.timeBased(); |
43 | - UUID customerId1 = UUIDs.timeBased(); | |
44 | 44 | UUID tenantId2 = UUIDs.timeBased(); |
45 | - UUID customerId2 = UUIDs.timeBased(); | |
46 | 45 | |
47 | 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 | 51 | TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); |
53 | 52 | List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink1); |
54 | - assertEquals(15, dashboardInfos1.size()); | |
53 | + Assert.assertEquals(15, dashboardInfos1.size()); | |
55 | 54 | |
56 | 55 | TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); |
57 | 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 | 61 | DashboardInfo dashboardInfo = new DashboardInfo(); |
87 | 62 | dashboardInfo.setId(new DashboardId(UUIDs.timeBased())); |
88 | 63 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
89 | - dashboardInfo.setCustomerId(new CustomerId(customerId)); | |
90 | 64 | dashboardInfo.setTitle("DASHBOARD_" + index); |
91 | 65 | dashboardInfoDao.save(dashboardInfo); |
92 | 66 | } | ... | ... |
... | ... | @@ -17,7 +17,7 @@ export default angular.module('thingsboard.api.dashboard', []) |
17 | 17 | .factory('dashboardService', DashboardService).name; |
18 | 18 | |
19 | 19 | /*@ngInject*/ |
20 | -function DashboardService($rootScope, $http, $q, $location, customerService) { | |
20 | +function DashboardService($rootScope, $http, $q, $location, $filter) { | |
21 | 21 | |
22 | 22 | var stDiffPromise; |
23 | 23 | |
... | ... | @@ -37,7 +37,11 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
37 | 37 | deleteDashboard: deleteDashboard, |
38 | 38 | saveDashboard: saveDashboard, |
39 | 39 | unassignDashboardFromCustomer: unassignDashboardFromCustomer, |
40 | + updateDashboardCustomers: updateDashboardCustomers, | |
41 | + addDashboardCustomers: addDashboardCustomers, | |
42 | + removeDashboardCustomers: removeDashboardCustomers, | |
40 | 43 | makeDashboardPublic: makeDashboardPublic, |
44 | + makeDashboardPrivate: makeDashboardPrivate, | |
41 | 45 | getPublicDashboardLink: getPublicDashboardLink |
42 | 46 | } |
43 | 47 | |
... | ... | @@ -56,14 +60,14 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
56 | 60 | url += '&textOffset=' + pageLink.textOffset; |
57 | 61 | } |
58 | 62 | $http.get(url, config).then(function success(response) { |
59 | - deferred.resolve(response.data); | |
63 | + deferred.resolve(prepareDashboards(response.data)); | |
60 | 64 | }, function fail() { |
61 | 65 | deferred.reject(); |
62 | 66 | }); |
63 | 67 | return deferred.promise; |
64 | 68 | } |
65 | 69 | |
66 | - function getTenantDashboards(pageLink, applyCustomersInfo, config) { | |
70 | + function getTenantDashboards(pageLink, config) { | |
67 | 71 | var deferred = $q.defer(); |
68 | 72 | var url = '/api/tenant/dashboards?limit=' + pageLink.limit; |
69 | 73 | if (angular.isDefined(pageLink.textSearch)) { |
... | ... | @@ -76,51 +80,25 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
76 | 80 | url += '&textOffset=' + pageLink.textOffset; |
77 | 81 | } |
78 | 82 | $http.get(url, config).then(function success(response) { |
79 | - if (applyCustomersInfo) { | |
80 | - customerService.applyAssignedCustomersInfo(response.data.data).then( | |
81 | - function success(data) { | |
82 | - response.data.data = data; | |
83 | - deferred.resolve(response.data); | |
84 | - }, | |
85 | - function fail() { | |
86 | - deferred.reject(); | |
87 | - } | |
88 | - ); | |
89 | - } else { | |
90 | - deferred.resolve(response.data); | |
91 | - } | |
83 | + deferred.resolve(prepareDashboards(response.data)); | |
92 | 84 | }, function fail() { |
93 | 85 | deferred.reject(); |
94 | 86 | }); |
95 | 87 | return deferred.promise; |
96 | 88 | } |
97 | 89 | |
98 | - function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) { | |
90 | + function getCustomerDashboards(customerId, pageLink, config) { | |
99 | 91 | var deferred = $q.defer(); |
100 | 92 | var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; |
101 | - if (angular.isDefined(pageLink.textSearch)) { | |
102 | - url += '&textSearch=' + pageLink.textSearch; | |
103 | - } | |
104 | 93 | if (angular.isDefined(pageLink.idOffset)) { |
105 | - url += '&idOffset=' + pageLink.idOffset; | |
106 | - } | |
107 | - if (angular.isDefined(pageLink.textOffset)) { | |
108 | - url += '&textOffset=' + pageLink.textOffset; | |
94 | + url += '&offset=' + pageLink.idOffset; | |
109 | 95 | } |
110 | 96 | $http.get(url, config).then(function success(response) { |
111 | - if (applyCustomersInfo) { | |
112 | - customerService.applyAssignedCustomerInfo(response.data.data, customerId).then( | |
113 | - function success(data) { | |
114 | - response.data.data = data; | |
115 | - deferred.resolve(response.data); | |
116 | - }, | |
117 | - function fail() { | |
118 | - deferred.reject(); | |
119 | - } | |
120 | - ); | |
121 | - } else { | |
122 | - deferred.resolve(response.data); | |
97 | + response.data = prepareDashboards(response.data); | |
98 | + if (pageLink.textSearch) { | |
99 | + response.data.data = $filter('filter')(response.data.data, {title: pageLink.textSearch}); | |
123 | 100 | } |
101 | + deferred.resolve(response.data); | |
124 | 102 | }, function fail() { |
125 | 103 | deferred.reject(); |
126 | 104 | }); |
... | ... | @@ -151,7 +129,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
151 | 129 | var deferred = $q.defer(); |
152 | 130 | var url = '/api/dashboard/' + dashboardId; |
153 | 131 | $http.get(url, null).then(function success(response) { |
154 | - deferred.resolve(response.data); | |
132 | + deferred.resolve(prepareDashboard(response.data)); | |
155 | 133 | }, function fail() { |
156 | 134 | deferred.reject(); |
157 | 135 | }); |
... | ... | @@ -162,7 +140,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
162 | 140 | var deferred = $q.defer(); |
163 | 141 | var url = '/api/dashboard/info/' + dashboardId; |
164 | 142 | $http.get(url, config).then(function success(response) { |
165 | - deferred.resolve(response.data); | |
143 | + deferred.resolve(prepareDashboard(response.data)); | |
166 | 144 | }, function fail() { |
167 | 145 | deferred.reject(); |
168 | 146 | }); |
... | ... | @@ -172,8 +150,8 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
172 | 150 | function saveDashboard(dashboard) { |
173 | 151 | var deferred = $q.defer(); |
174 | 152 | var url = '/api/dashboard'; |
175 | - $http.post(url, dashboard).then(function success(response) { | |
176 | - deferred.resolve(response.data); | |
153 | + $http.post(url, cleanDashboard(dashboard)).then(function success(response) { | |
154 | + deferred.resolve(prepareDashboard(response.data)); | |
177 | 155 | }, function fail() { |
178 | 156 | deferred.reject(); |
179 | 157 | }); |
... | ... | @@ -195,18 +173,51 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
195 | 173 | var deferred = $q.defer(); |
196 | 174 | var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; |
197 | 175 | $http.post(url, null).then(function success(response) { |
198 | - deferred.resolve(response.data); | |
176 | + deferred.resolve(prepareDashboard(response.data)); | |
199 | 177 | }, function fail() { |
200 | 178 | deferred.reject(); |
201 | 179 | }); |
202 | 180 | return deferred.promise; |
203 | 181 | } |
204 | 182 | |
205 | - function unassignDashboardFromCustomer(dashboardId) { | |
183 | + function unassignDashboardFromCustomer(customerId, dashboardId) { | |
206 | 184 | var deferred = $q.defer(); |
207 | - var url = '/api/customer/dashboard/' + dashboardId; | |
185 | + var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; | |
208 | 186 | $http.delete(url).then(function success(response) { |
209 | - deferred.resolve(response.data); | |
187 | + deferred.resolve(prepareDashboard(response.data)); | |
188 | + }, function fail() { | |
189 | + deferred.reject(); | |
190 | + }); | |
191 | + return deferred.promise; | |
192 | + } | |
193 | + | |
194 | + function updateDashboardCustomers(dashboardId, customerIds) { | |
195 | + var deferred = $q.defer(); | |
196 | + var url = '/api/dashboard/' + dashboardId + '/customers'; | |
197 | + $http.post(url, customerIds).then(function success(response) { | |
198 | + deferred.resolve(prepareDashboard(response.data)); | |
199 | + }, function fail() { | |
200 | + deferred.reject(); | |
201 | + }); | |
202 | + return deferred.promise; | |
203 | + } | |
204 | + | |
205 | + function addDashboardCustomers(dashboardId, customerIds) { | |
206 | + var deferred = $q.defer(); | |
207 | + var url = '/api/dashboard/' + dashboardId + '/customers/add'; | |
208 | + $http.post(url, customerIds).then(function success(response) { | |
209 | + deferred.resolve(prepareDashboard(response.data)); | |
210 | + }, function fail() { | |
211 | + deferred.reject(); | |
212 | + }); | |
213 | + return deferred.promise; | |
214 | + } | |
215 | + | |
216 | + function removeDashboardCustomers(dashboardId, customerIds) { | |
217 | + var deferred = $q.defer(); | |
218 | + var url = '/api/dashboard/' + dashboardId + '/customers/remove'; | |
219 | + $http.post(url, customerIds).then(function success(response) { | |
220 | + deferred.resolve(prepareDashboard(response.data)); | |
210 | 221 | }, function fail() { |
211 | 222 | deferred.reject(); |
212 | 223 | }); |
... | ... | @@ -217,7 +228,18 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
217 | 228 | var deferred = $q.defer(); |
218 | 229 | var url = '/api/customer/public/dashboard/' + dashboardId; |
219 | 230 | $http.post(url, null).then(function success(response) { |
220 | - deferred.resolve(response.data); | |
231 | + deferred.resolve(prepareDashboard(response.data)); | |
232 | + }, function fail() { | |
233 | + deferred.reject(); | |
234 | + }); | |
235 | + return deferred.promise; | |
236 | + } | |
237 | + | |
238 | + function makeDashboardPrivate(dashboardId) { | |
239 | + var deferred = $q.defer(); | |
240 | + var url = '/api/customer/public/dashboard/' + dashboardId; | |
241 | + $http.delete(url).then(function success(response) { | |
242 | + deferred.resolve(prepareDashboard(response.data)); | |
221 | 243 | }, function fail() { |
222 | 244 | deferred.reject(); |
223 | 245 | }); |
... | ... | @@ -230,8 +252,44 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { |
230 | 252 | if (port != 80 && port != 443) { |
231 | 253 | url += ":" + port; |
232 | 254 | } |
233 | - url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.customerId.id; | |
255 | + url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId; | |
234 | 256 | return url; |
235 | 257 | } |
236 | 258 | |
259 | + function prepareDashboards(dashboardsData) { | |
260 | + if (dashboardsData.data) { | |
261 | + for (var i = 0; i < dashboardsData.data.length; i++) { | |
262 | + dashboardsData.data[i] = prepareDashboard(dashboardsData.data[i]); | |
263 | + } | |
264 | + } | |
265 | + return dashboardsData; | |
266 | + } | |
267 | + | |
268 | + function prepareDashboard(dashboard) { | |
269 | + dashboard.publicCustomerId = null; | |
270 | + dashboard.assignedCustomersText = ""; | |
271 | + dashboard.assignedCustomersIds = []; | |
272 | + if (dashboard.assignedCustomers && dashboard.assignedCustomers.length) { | |
273 | + var assignedCustomersTitles = []; | |
274 | + for (var i = 0; i < dashboard.assignedCustomers.length; i++) { | |
275 | + var assignedCustomer = dashboard.assignedCustomers[i]; | |
276 | + dashboard.assignedCustomersIds.push(assignedCustomer.customerId.id); | |
277 | + if (assignedCustomer.public) { | |
278 | + dashboard.publicCustomerId = assignedCustomer.customerId.id; | |
279 | + } else { | |
280 | + assignedCustomersTitles.push(assignedCustomer.title); | |
281 | + } | |
282 | + } | |
283 | + dashboard.assignedCustomersText = assignedCustomersTitles.join(', '); | |
284 | + } | |
285 | + return dashboard; | |
286 | + } | |
287 | + | |
288 | + function cleanDashboard(dashboard) { | |
289 | + delete dashboard.publicCustomerId; | |
290 | + delete dashboard.assignedCustomersText; | |
291 | + delete dashboard.assignedCustomersIds; | |
292 | + return dashboard; | |
293 | + } | |
294 | + | |
237 | 295 | } | ... | ... |
... | ... | @@ -273,9 +273,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
273 | 273 | break; |
274 | 274 | case types.entityType.dashboard: |
275 | 275 | if (user.authority === 'CUSTOMER_USER') { |
276 | - promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config); | |
276 | + promise = dashboardService.getCustomerDashboards(customerId, pageLink, config); | |
277 | 277 | } else { |
278 | - promise = dashboardService.getTenantDashboards(pageLink, false, config); | |
278 | + promise = dashboardService.getTenantDashboards(pageLink, config); | |
279 | 279 | } |
280 | 280 | break; |
281 | 281 | case types.entityType.user: |
... | ... | @@ -403,6 +403,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
403 | 403 | return deferred.promise; |
404 | 404 | } |
405 | 405 | |
406 | + function resolveAliasEntityId(entityType, id) { | |
407 | + var entityId = { | |
408 | + entityType: entityType, | |
409 | + id: id | |
410 | + }; | |
411 | + if (entityType == types.aliasEntityType.current_customer) { | |
412 | + var user = userService.getCurrentUser(); | |
413 | + entityId.entityType = types.entityType.customer; | |
414 | + if (user.authority === 'CUSTOMER_USER') { | |
415 | + entityId.id = user.customerId; | |
416 | + } | |
417 | + } | |
418 | + return entityId; | |
419 | + } | |
420 | + | |
406 | 421 | function getStateEntityId(filter, stateParams) { |
407 | 422 | var entityId = null; |
408 | 423 | if (stateParams) { |
... | ... | @@ -417,6 +432,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
417 | 432 | if (!entityId) { |
418 | 433 | entityId = filter.defaultStateEntity; |
419 | 434 | } |
435 | + if (entityId) { | |
436 | + entityId = resolveAliasEntityId(entityId.entityType, entityId.id); | |
437 | + } | |
420 | 438 | return entityId; |
421 | 439 | } |
422 | 440 | |
... | ... | @@ -432,7 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
432 | 450 | var stateEntityId = getStateEntityId(filter, stateParams); |
433 | 451 | switch (filter.type) { |
434 | 452 | case types.aliasFilterType.singleEntity.value: |
435 | - getEntity(filter.singleEntity.entityType, filter.singleEntity.id, {ignoreLoading: true}).then( | |
453 | + var aliasEntityId = resolveAliasEntityId(filter.singleEntity.entityType, filter.singleEntity.id); | |
454 | + getEntity(aliasEntityId.entityType, aliasEntityId.id, {ignoreLoading: true}).then( | |
436 | 455 | function success(entity) { |
437 | 456 | result.entities = entitiesToEntitiesInfo([entity]); |
438 | 457 | deferred.resolve(result); |
... | ... | @@ -530,10 +549,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
530 | 549 | rootEntityId = filter.rootEntity.id; |
531 | 550 | } |
532 | 551 | if (rootEntityType && rootEntityId) { |
552 | + var relationQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId); | |
533 | 553 | var searchQuery = { |
534 | 554 | parameters: { |
535 | - rootId: rootEntityId, | |
536 | - rootType: rootEntityType, | |
555 | + rootId: relationQueryRootEntityId.id, | |
556 | + rootType: relationQueryRootEntityId.entityType, | |
537 | 557 | direction: filter.direction |
538 | 558 | }, |
539 | 559 | filters: filter.filters |
... | ... | @@ -571,10 +591,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
571 | 591 | rootEntityId = filter.rootEntity.id; |
572 | 592 | } |
573 | 593 | if (rootEntityType && rootEntityId) { |
594 | + var searchQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId); | |
574 | 595 | searchQuery = { |
575 | 596 | parameters: { |
576 | - rootId: rootEntityId, | |
577 | - rootType: rootEntityType, | |
597 | + rootId: searchQueryRootEntityId.id, | |
598 | + rootType: searchQueryRootEntityId.entityType, | |
578 | 599 | direction: filter.direction |
579 | 600 | }, |
580 | 601 | relationType: filter.relationType |
... | ... | @@ -709,7 +730,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
709 | 730 | return result; |
710 | 731 | } |
711 | 732 | |
712 | - function prepareAllowedEntityTypesList(allowedEntityTypes) { | |
733 | + function prepareAllowedEntityTypesList(allowedEntityTypes, useAliasEntityTypes) { | |
713 | 734 | var authority = userService.getAuthority(); |
714 | 735 | var entityTypes = {}; |
715 | 736 | switch(authority) { |
... | ... | @@ -726,12 +747,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device |
726 | 747 | entityTypes.rule = types.entityType.rule; |
727 | 748 | entityTypes.plugin = types.entityType.plugin; |
728 | 749 | entityTypes.dashboard = types.entityType.dashboard; |
750 | + if (useAliasEntityTypes) { | |
751 | + entityTypes.current_customer = types.aliasEntityType.current_customer; | |
752 | + } | |
729 | 753 | break; |
730 | 754 | case 'CUSTOMER_USER': |
731 | 755 | entityTypes.device = types.entityType.device; |
732 | 756 | entityTypes.asset = types.entityType.asset; |
733 | 757 | entityTypes.customer = types.entityType.customer; |
734 | 758 | entityTypes.dashboard = types.entityType.dashboard; |
759 | + if (useAliasEntityTypes) { | |
760 | + entityTypes.current_customer = types.aliasEntityType.current_customer; | |
761 | + } | |
735 | 762 | break; |
736 | 763 | } |
737 | 764 | ... | ... |
... | ... | @@ -266,9 +266,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi |
266 | 266 | var pageLink = {limit: 100}; |
267 | 267 | var fetchDashboardsPromise; |
268 | 268 | if (currentUser.authority === 'TENANT_ADMIN') { |
269 | - fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false); | |
269 | + fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink); | |
270 | 270 | } else { |
271 | - fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false); | |
271 | + fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink); | |
272 | 272 | } |
273 | 273 | fetchDashboardsPromise.then( |
274 | 274 | function success(result) { | ... | ... |
... | ... | @@ -296,6 +296,9 @@ export default angular.module('thingsboard.types', []) |
296 | 296 | dashboard: "DASHBOARD", |
297 | 297 | alarm: "ALARM" |
298 | 298 | }, |
299 | + aliasEntityType: { | |
300 | + current_customer: "CURRENT_CUSTOMER" | |
301 | + }, | |
299 | 302 | entityTypeTranslations: { |
300 | 303 | "DEVICE": { |
301 | 304 | type: 'entity.type-device', |
... | ... | @@ -350,6 +353,10 @@ export default angular.module('thingsboard.types', []) |
350 | 353 | typePlural: 'entity.type-alarms', |
351 | 354 | list: 'entity.list-of-alarms', |
352 | 355 | nameStartsWith: 'entity.alarm-name-starts-with' |
356 | + }, | |
357 | + "CURRENT_CUSTOMER": { | |
358 | + type: 'entity.type-current-customer', | |
359 | + list: 'entity.type-current-customer' | |
353 | 360 | } |
354 | 361 | }, |
355 | 362 | entitySearchDirection: { | ... | ... |
... | ... | @@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u |
48 | 48 | var promise; |
49 | 49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
50 | 50 | if (scope.customerId) { |
51 | - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true}); | |
51 | + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true}); | |
52 | 52 | } else { |
53 | 53 | promise = $q.when({data: []}); |
54 | 54 | } |
... | ... | @@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u |
60 | 60 | promise = $q.when({data: []}); |
61 | 61 | } |
62 | 62 | } else { |
63 | - promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true}); | |
63 | + promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); | |
64 | 64 | } |
65 | 65 | } |
66 | 66 | ... | ... |
... | ... | @@ -48,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu |
48 | 48 | var promise; |
49 | 49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
50 | 50 | if (scope.customerId && scope.customerId != types.id.nullUid) { |
51 | - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true}); | |
51 | + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true}); | |
52 | 52 | } else { |
53 | 53 | promise = $q.when({data: []}); |
54 | 54 | } |
55 | 55 | } else { |
56 | - promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true}); | |
56 | + promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); | |
57 | 57 | } |
58 | 58 | |
59 | 59 | promise.then(function success(result) { | ... | ... |
... | ... | @@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD |
52 | 52 | fetchMoreItems_: function () { |
53 | 53 | if (vm.dashboards.hasNext && !vm.dashboards.pending) { |
54 | 54 | vm.dashboards.pending = true; |
55 | - dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then( | |
55 | + dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then( | |
56 | 56 | function success(dashboards) { |
57 | 57 | vm.dashboards.data = vm.dashboards.data.concat(dashboards.data); |
58 | 58 | vm.dashboards.nextPageLink = dashboards.nextPageLink; | ... | ... |
ui/src/app/dashboard/assign-to-customer.controller.js
deleted
100644 → 0
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 | -/*@ngInject*/ | |
17 | -export default function AssignDashboardToCustomerController(customerService, dashboardService, $mdDialog, $q, dashboardIds, customers) { | |
18 | - | |
19 | - var vm = this; | |
20 | - | |
21 | - vm.customers = customers; | |
22 | - vm.searchText = ''; | |
23 | - | |
24 | - vm.assign = assign; | |
25 | - vm.cancel = cancel; | |
26 | - vm.isCustomerSelected = isCustomerSelected; | |
27 | - vm.hasData = hasData; | |
28 | - vm.noData = noData; | |
29 | - vm.searchCustomerTextUpdated = searchCustomerTextUpdated; | |
30 | - vm.toggleCustomerSelection = toggleCustomerSelection; | |
31 | - | |
32 | - vm.theCustomers = { | |
33 | - getItemAtIndex: function (index) { | |
34 | - if (index > vm.customers.data.length) { | |
35 | - vm.theCustomers.fetchMoreItems_(index); | |
36 | - return null; | |
37 | - } | |
38 | - var item = vm.customers.data[index]; | |
39 | - if (item) { | |
40 | - item.indexNumber = index + 1; | |
41 | - } | |
42 | - return item; | |
43 | - }, | |
44 | - | |
45 | - getLength: function () { | |
46 | - if (vm.customers.hasNext) { | |
47 | - return vm.customers.data.length + vm.customers.nextPageLink.limit; | |
48 | - } else { | |
49 | - return vm.customers.data.length; | |
50 | - } | |
51 | - }, | |
52 | - | |
53 | - fetchMoreItems_: function () { | |
54 | - if (vm.customers.hasNext && !vm.customers.pending) { | |
55 | - vm.customers.pending = true; | |
56 | - customerService.getCustomers(vm.customers.nextPageLink).then( | |
57 | - function success(customers) { | |
58 | - vm.customers.data = vm.customers.data.concat(customers.data); | |
59 | - vm.customers.nextPageLink = customers.nextPageLink; | |
60 | - vm.customers.hasNext = customers.hasNext; | |
61 | - if (vm.customers.hasNext) { | |
62 | - vm.customers.nextPageLink.limit = vm.customers.pageSize; | |
63 | - } | |
64 | - vm.customers.pending = false; | |
65 | - }, | |
66 | - function fail() { | |
67 | - vm.customers.hasNext = false; | |
68 | - vm.customers.pending = false; | |
69 | - }); | |
70 | - } | |
71 | - } | |
72 | - }; | |
73 | - | |
74 | - function cancel () { | |
75 | - $mdDialog.cancel(); | |
76 | - } | |
77 | - | |
78 | - function assign () { | |
79 | - var tasks = []; | |
80 | - for (var dashboardId in dashboardIds) { | |
81 | - tasks.push(dashboardService.assignDashboardToCustomer(vm.customers.selection.id.id, dashboardIds[dashboardId])); | |
82 | - } | |
83 | - $q.all(tasks).then(function () { | |
84 | - $mdDialog.hide(); | |
85 | - }); | |
86 | - } | |
87 | - | |
88 | - function noData () { | |
89 | - return vm.customers.data.length == 0 && !vm.customers.hasNext; | |
90 | - } | |
91 | - | |
92 | - function hasData () { | |
93 | - return vm.customers.data.length > 0; | |
94 | - } | |
95 | - | |
96 | - function toggleCustomerSelection ($event, customer) { | |
97 | - $event.stopPropagation(); | |
98 | - if (vm.isCustomerSelected(customer)) { | |
99 | - vm.customers.selection = null; | |
100 | - } else { | |
101 | - vm.customers.selection = customer; | |
102 | - } | |
103 | - } | |
104 | - | |
105 | - function isCustomerSelected (customer) { | |
106 | - return vm.customers.selection != null && customer && | |
107 | - customer.id.id === vm.customers.selection.id.id; | |
108 | - } | |
109 | - | |
110 | - function searchCustomerTextUpdated () { | |
111 | - vm.customers = { | |
112 | - pageSize: vm.customers.pageSize, | |
113 | - data: [], | |
114 | - nextPageLink: { | |
115 | - limit: vm.customers.pageSize, | |
116 | - textSearch: vm.searchText | |
117 | - }, | |
118 | - selection: null, | |
119 | - hasNext: true, | |
120 | - pending: false | |
121 | - }; | |
122 | - } | |
123 | -} |
ui/src/app/dashboard/dashboard-card.scss
0 → 100644
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 | + | |
17 | +.tb-dashboard-assigned-customers { | |
18 | + display: block; | |
19 | + display: -webkit-box; | |
20 | + height: 34px; | |
21 | + overflow: hidden; | |
22 | + text-overflow: ellipsis; | |
23 | + -webkit-line-clamp: 2; | |
24 | + -webkit-box-orient: vertical; | |
25 | + margin-bottom: 4px; | |
26 | +} | |
27 | + | ... | ... |
... | ... | @@ -15,6 +15,6 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'dashboard.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div> | |
19 | -<div class="tb-small" ng-show="vm.isPublic()">{{'dashboard.public' | translate}}</div> | |
18 | +<div class="tb-small tb-dashboard-assigned-customers" ng-show="vm.parentCtl.dashboardsScope === 'tenant' && vm.item.assignedCustomersText">{{'dashboard.assignedToCustomers' | translate}}: '{{vm.item.assignedCustomersText}}'</div> | |
19 | +<div class="tb-small" ng-show="vm.parentCtl.dashboardsScope === 'tenant' && vm.item.publicCustomerId">{{'dashboard.public' | translate}}</div> | |
20 | 20 | ... | ... |
... | ... | @@ -19,24 +19,29 @@ |
19 | 19 | ng-show="!isEdit && dashboardScope === 'tenant'" |
20 | 20 | class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button> |
21 | 21 | <md-button ng-click="onMakePublic({event: $event})" |
22 | - ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer && !isPublic" | |
22 | + ng-show="!isEdit && dashboardScope === 'tenant' && !dashboard.publicCustomerId" | |
23 | 23 | class="md-raised md-primary">{{ 'dashboard.make-public' | translate }}</md-button> |
24 | -<md-button ng-click="onAssignToCustomer({event: $event})" | |
25 | - ng-show="!isEdit && dashboardScope === 'tenant' && !isAssignedToCustomer" | |
26 | - class="md-raised md-primary">{{ 'dashboard.assign-to-customer' | translate }}</md-button> | |
27 | -<md-button ng-click="onUnassignFromCustomer({event: $event, isPublic: isPublic})" | |
28 | - ng-show="!isEdit && (dashboardScope === 'customer' || dashboardScope === 'tenant') && isAssignedToCustomer" | |
29 | - class="md-raised md-primary">{{ isPublic ? 'dashboard.make-private' : 'dashboard.unassign-from-customer' | translate }}</md-button> | |
24 | +<md-button ng-click="onMakePrivate({event: $event})" | |
25 | + ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId || | |
26 | + dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))" | |
27 | + class="md-raised md-primary">{{ 'dashboard.make-private' | translate }}</md-button> | |
28 | +<md-button ng-click="onManageAssignedCustomers({event: $event})" | |
29 | + ng-show="!isEdit && dashboardScope === 'tenant'" | |
30 | + class="md-raised md-primary">{{ 'dashboard.manage-assigned-customers' | translate }}</md-button> | |
31 | +<md-button ng-click="onUnassignFromCustomer({event: $event})" | |
32 | + ng-show="!isEdit && dashboardScope === 'customer' && customerId != dashboard.publicCustomerId" | |
33 | + class="md-raised md-primary">{{ 'dashboard.unassign-from-customer' | translate }}</md-button> | |
30 | 34 | <md-button ng-click="onDeleteDashboard({event: $event})" |
31 | 35 | ng-show="!isEdit && dashboardScope === 'tenant'" |
32 | 36 | class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button> |
33 | 37 | <md-content class="md-padding" layout="column"> |
34 | 38 | <md-input-container class="md-block" |
35 | - ng-show="!isEdit && isAssignedToCustomer && !isPublic && dashboardScope === 'tenant'"> | |
36 | - <label translate>dashboard.assignedToCustomer</label> | |
37 | - <input ng-model="assignedCustomer.title" disabled> | |
39 | + ng-show="!isEdit && dashboard.assignedCustomersText && dashboardScope === 'tenant'"> | |
40 | + <label translate>dashboard.assignedToCustomers</label> | |
41 | + <input ng-model="dashboard.assignedCustomersText" disabled> | |
38 | 42 | </md-input-container> |
39 | - <div layout="column" ng-show="!isEdit && isPublic && (dashboardScope === 'customer' || dashboardScope === 'tenant')"> | |
43 | + <div layout="column" ng-show="!isEdit && ((dashboardScope === 'tenant' && dashboard.publicCustomerId || | |
44 | + dashboardScope === 'customer' && customerId == dashboard.publicCustomerId))"> | |
40 | 45 | <tb-social-share-panel style="padding-bottom: 10px;" |
41 | 46 | share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}" |
42 | 47 | share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}" | ... | ... |
... | ... | @@ -20,36 +20,17 @@ import dashboardFieldsetTemplate from './dashboard-fieldset.tpl.html'; |
20 | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | |
22 | 22 | /*@ngInject*/ |
23 | -export default function DashboardDirective($compile, $templateCache, $translate, types, toast, customerService, dashboardService) { | |
23 | +export default function DashboardDirective($compile, $templateCache, $translate, types, toast, dashboardService) { | |
24 | 24 | var linker = function (scope, element) { |
25 | 25 | var template = $templateCache.get(dashboardFieldsetTemplate); |
26 | 26 | element.html(template); |
27 | - | |
28 | - scope.isAssignedToCustomer = false; | |
29 | - scope.isPublic = false; | |
30 | - scope.assignedCustomer = null; | |
31 | 27 | scope.publicLink = null; |
32 | - | |
33 | 28 | scope.$watch('dashboard', function(newVal) { |
34 | 29 | if (newVal) { |
35 | - if (scope.dashboard.customerId && scope.dashboard.customerId.id !== types.id.nullUid) { | |
36 | - scope.isAssignedToCustomer = true; | |
37 | - customerService.getShortCustomerInfo(scope.dashboard.customerId.id).then( | |
38 | - function success(customer) { | |
39 | - scope.assignedCustomer = customer; | |
40 | - scope.isPublic = customer.isPublic; | |
41 | - if (scope.isPublic) { | |
42 | - scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard); | |
43 | - } else { | |
44 | - scope.publicLink = null; | |
45 | - } | |
46 | - } | |
47 | - ); | |
30 | + if (scope.dashboard.publicCustomerId) { | |
31 | + scope.publicLink = dashboardService.getPublicDashboardLink(scope.dashboard); | |
48 | 32 | } else { |
49 | - scope.isAssignedToCustomer = false; | |
50 | - scope.isPublic = false; | |
51 | 33 | scope.publicLink = null; |
52 | - scope.assignedCustomer = null; | |
53 | 34 | } |
54 | 35 | } |
55 | 36 | }); |
... | ... | @@ -66,10 +47,12 @@ export default function DashboardDirective($compile, $templateCache, $translate, |
66 | 47 | scope: { |
67 | 48 | dashboard: '=', |
68 | 49 | isEdit: '=', |
50 | + customerId: '=', | |
69 | 51 | dashboardScope: '=', |
70 | 52 | theForm: '=', |
71 | - onAssignToCustomer: '&', | |
72 | 53 | onMakePublic: '&', |
54 | + onMakePrivate: '&', | |
55 | + onManageAssignedCustomers: '&', | |
73 | 56 | onUnassignFromCustomer: '&', |
74 | 57 | onExportDashboard: '&', |
75 | 58 | onDeleteDashboard: '&' | ... | ... |
... | ... | @@ -17,12 +17,14 @@ |
17 | 17 | |
18 | 18 | import addDashboardTemplate from './add-dashboard.tpl.html'; |
19 | 19 | import dashboardCard from './dashboard-card.tpl.html'; |
20 | -import assignToCustomerTemplate from './assign-to-customer.tpl.html'; | |
21 | 20 | import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html'; |
22 | 21 | import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html'; |
22 | +import manageAssignedCustomersTemplate from './manage-assigned-customers.tpl.html'; | |
23 | 23 | |
24 | 24 | /* eslint-enable import/no-unresolved, import/default */ |
25 | 25 | |
26 | +import './dashboard-card.scss'; | |
27 | + | |
26 | 28 | /*@ngInject*/ |
27 | 29 | export function MakeDashboardPublicDialogController($mdDialog, $translate, toast, dashboardService, dashboard) { |
28 | 30 | |
... | ... | @@ -48,23 +50,8 @@ export function MakeDashboardPublicDialogController($mdDialog, $translate, toast |
48 | 50 | export function DashboardCardController(types) { |
49 | 51 | |
50 | 52 | var vm = this; |
51 | - | |
52 | 53 | vm.types = types; |
53 | 54 | |
54 | - vm.isAssignedToCustomer = function() { | |
55 | - if (vm.item && vm.item.customerId && vm.parentCtl.dashboardsScope === 'tenant' && | |
56 | - vm.item.customerId.id != vm.types.id.nullUid && !vm.item.assignedCustomer.isPublic) { | |
57 | - return true; | |
58 | - } | |
59 | - return false; | |
60 | - } | |
61 | - | |
62 | - vm.isPublic = function() { | |
63 | - if (vm.item && vm.item.assignedCustomer && vm.parentCtl.dashboardsScope === 'tenant' && vm.item.assignedCustomer.isPublic) { | |
64 | - return true; | |
65 | - } | |
66 | - return false; | |
67 | - } | |
68 | 55 | } |
69 | 56 | |
70 | 57 | /*@ngInject*/ |
... | ... | @@ -135,8 +122,9 @@ export function DashboardsController(userService, dashboardService, customerServ |
135 | 122 | |
136 | 123 | vm.dashboardsScope = $state.$current.data.dashboardsType; |
137 | 124 | |
138 | - vm.assignToCustomer = assignToCustomer; | |
139 | 125 | vm.makePublic = makePublic; |
126 | + vm.makePrivate = makePrivate; | |
127 | + vm.manageAssignedCustomers = manageAssignedCustomers; | |
140 | 128 | vm.unassignFromCustomer = unassignFromCustomer; |
141 | 129 | vm.exportDashboard = exportDashboard; |
142 | 130 | |
... | ... | @@ -155,6 +143,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
155 | 143 | } |
156 | 144 | |
157 | 145 | if (customerId) { |
146 | + vm.customerId = customerId; | |
158 | 147 | vm.customerDashboardsTitle = $translate.instant('customer.dashboards'); |
159 | 148 | customerService.getShortCustomerInfo(customerId).then( |
160 | 149 | function success(info) { |
... | ... | @@ -167,7 +156,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
167 | 156 | |
168 | 157 | if (vm.dashboardsScope === 'tenant') { |
169 | 158 | fetchDashboardsFunction = function (pageLink) { |
170 | - return dashboardService.getTenantDashboards(pageLink, true); | |
159 | + return dashboardService.getTenantDashboards(pageLink); | |
171 | 160 | }; |
172 | 161 | deleteDashboardFunction = function (dashboardId) { |
173 | 162 | return dashboardService.deleteDashboard(dashboardId); |
... | ... | @@ -194,11 +183,33 @@ export function DashboardsController(userService, dashboardService, customerServ |
194 | 183 | details: function() { return $translate.instant('dashboard.make-public') }, |
195 | 184 | icon: "share", |
196 | 185 | isEnabled: function(dashboard) { |
197 | - return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); | |
186 | + return dashboard && !dashboard.publicCustomerId; | |
198 | 187 | } |
199 | 188 | }); |
200 | - | |
201 | 189 | dashboardActionsList.push({ |
190 | + onAction: function ($event, item) { | |
191 | + makePrivate($event, item); | |
192 | + }, | |
193 | + name: function() { return $translate.instant('action.make-private') }, | |
194 | + details: function() { return $translate.instant('dashboard.make-private') }, | |
195 | + icon: "reply", | |
196 | + isEnabled: function(dashboard) { | |
197 | + return dashboard && dashboard.publicCustomerId; | |
198 | + } | |
199 | + }); | |
200 | + dashboardActionsList.push({ | |
201 | + onAction: function ($event, item) { | |
202 | + manageAssignedCustomers($event, item); | |
203 | + }, | |
204 | + name: function() { return $translate.instant('action.assign') }, | |
205 | + details: function() { return $translate.instant('dashboard.manage-assigned-customers') }, | |
206 | + icon: "assignment_ind", | |
207 | + isEnabled: function(dashboard) { | |
208 | + return dashboard; | |
209 | + } | |
210 | + }); | |
211 | + | |
212 | + /*dashboardActionsList.push({ | |
202 | 213 | onAction: function ($event, item) { |
203 | 214 | assignToCustomer($event, [ item.id.id ]); |
204 | 215 | }, |
... | ... | @@ -208,8 +219,8 @@ export function DashboardsController(userService, dashboardService, customerServ |
208 | 219 | isEnabled: function(dashboard) { |
209 | 220 | return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); |
210 | 221 | } |
211 | - }); | |
212 | - dashboardActionsList.push({ | |
222 | + });*/ | |
223 | + /*dashboardActionsList.push({ | |
213 | 224 | onAction: function ($event, item) { |
214 | 225 | unassignFromCustomer($event, item, false); |
215 | 226 | }, |
... | ... | @@ -219,18 +230,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
219 | 230 | isEnabled: function(dashboard) { |
220 | 231 | return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && !dashboard.assignedCustomer.isPublic; |
221 | 232 | } |
222 | - }); | |
223 | - dashboardActionsList.push({ | |
224 | - onAction: function ($event, item) { | |
225 | - unassignFromCustomer($event, item, true); | |
226 | - }, | |
227 | - name: function() { return $translate.instant('action.make-private') }, | |
228 | - details: function() { return $translate.instant('dashboard.make-private') }, | |
229 | - icon: "reply", | |
230 | - isEnabled: function(dashboard) { | |
231 | - return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && dashboard.assignedCustomer.isPublic; | |
232 | - } | |
233 | - }); | |
233 | + });*/ | |
234 | 234 | |
235 | 235 | dashboardActionsList.push( |
236 | 236 | { |
... | ... | @@ -246,7 +246,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
246 | 246 | dashboardGroupActionsList.push( |
247 | 247 | { |
248 | 248 | onAction: function ($event, items) { |
249 | - assignDashboardsToCustomer($event, items); | |
249 | + assignDashboardsToCustomers($event, items); | |
250 | 250 | }, |
251 | 251 | name: function() { return $translate.instant('dashboard.assign-dashboards') }, |
252 | 252 | details: function(selectedCount) { |
... | ... | @@ -255,6 +255,17 @@ export function DashboardsController(userService, dashboardService, customerServ |
255 | 255 | icon: "assignment_ind" |
256 | 256 | } |
257 | 257 | ); |
258 | + dashboardGroupActionsList.push( | |
259 | + { | |
260 | + onAction: function ($event, items) { | |
261 | + unassignDashboardsFromCustomers($event, items); | |
262 | + }, | |
263 | + name: function() { return $translate.instant('dashboard.unassign-dashboards') }, | |
264 | + details: function(selectedCount) { | |
265 | + return $translate.instant('dashboard.unassign-dashboards-action-text', {count: selectedCount}, "messageformat"); | |
266 | + }, | |
267 | + icon: "assignment_return" } | |
268 | + ); | |
258 | 269 | |
259 | 270 | dashboardGroupActionsList.push( |
260 | 271 | { |
... | ... | @@ -290,10 +301,10 @@ export function DashboardsController(userService, dashboardService, customerServ |
290 | 301 | }); |
291 | 302 | } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') { |
292 | 303 | fetchDashboardsFunction = function (pageLink) { |
293 | - return dashboardService.getCustomerDashboards(customerId, pageLink, true); | |
304 | + return dashboardService.getCustomerDashboards(customerId, pageLink); | |
294 | 305 | }; |
295 | 306 | deleteDashboardFunction = function (dashboardId) { |
296 | - return dashboardService.unassignDashboardFromCustomer(dashboardId); | |
307 | + return dashboardService.unassignDashboardFromCustomer(customerId, dashboardId); | |
297 | 308 | }; |
298 | 309 | refreshDashboardsParamsFunction = function () { |
299 | 310 | return {"customerId": customerId, "topIndex": vm.topIndex}; |
... | ... | @@ -314,26 +325,27 @@ export function DashboardsController(userService, dashboardService, customerServ |
314 | 325 | dashboardActionsList.push( |
315 | 326 | { |
316 | 327 | onAction: function ($event, item) { |
317 | - unassignFromCustomer($event, item, false); | |
328 | + makePrivate($event, item); | |
318 | 329 | }, |
319 | - name: function() { return $translate.instant('action.unassign') }, | |
320 | - details: function() { return $translate.instant('dashboard.unassign-from-customer') }, | |
321 | - icon: "assignment_return", | |
330 | + name: function() { return $translate.instant('action.make-private') }, | |
331 | + details: function() { return $translate.instant('dashboard.make-private') }, | |
332 | + icon: "reply", | |
322 | 333 | isEnabled: function(dashboard) { |
323 | - return dashboard && !dashboard.assignedCustomer.isPublic; | |
334 | + return dashboard && customerId == dashboard.publicCustomerId; | |
324 | 335 | } |
325 | 336 | } |
326 | 337 | ); |
338 | + | |
327 | 339 | dashboardActionsList.push( |
328 | 340 | { |
329 | 341 | onAction: function ($event, item) { |
330 | - unassignFromCustomer($event, item, true); | |
342 | + unassignFromCustomer($event, item, customerId); | |
331 | 343 | }, |
332 | - name: function() { return $translate.instant('action.make-private') }, | |
333 | - details: function() { return $translate.instant('dashboard.make-private') }, | |
334 | - icon: "reply", | |
344 | + name: function() { return $translate.instant('action.unassign') }, | |
345 | + details: function() { return $translate.instant('dashboard.unassign-from-customer') }, | |
346 | + icon: "assignment_return", | |
335 | 347 | isEnabled: function(dashboard) { |
336 | - return dashboard && dashboard.assignedCustomer.isPublic; | |
348 | + return dashboard && customerId != dashboard.publicCustomerId; | |
337 | 349 | } |
338 | 350 | } |
339 | 351 | ); |
... | ... | @@ -341,7 +353,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
341 | 353 | dashboardGroupActionsList.push( |
342 | 354 | { |
343 | 355 | onAction: function ($event, items) { |
344 | - unassignDashboardsFromCustomer($event, items); | |
356 | + unassignDashboardsFromCustomer($event, items, customerId); | |
345 | 357 | }, |
346 | 358 | name: function() { return $translate.instant('dashboard.unassign-dashboards') }, |
347 | 359 | details: function(selectedCount) { |
... | ... | @@ -351,7 +363,6 @@ export function DashboardsController(userService, dashboardService, customerServ |
351 | 363 | } |
352 | 364 | ); |
353 | 365 | |
354 | - | |
355 | 366 | vm.dashboardGridConfig.addItemAction = { |
356 | 367 | onAction: function ($event) { |
357 | 368 | addDashboardsToCustomer($event); |
... | ... | @@ -428,39 +439,42 @@ export function DashboardsController(userService, dashboardService, customerServ |
428 | 439 | return deferred.promise; |
429 | 440 | } |
430 | 441 | |
431 | - function assignToCustomer($event, dashboardIds) { | |
442 | + function manageAssignedCustomers($event, dashboard) { | |
443 | + showManageAssignedCustomersDialog($event, [dashboard.id.id], 'manage', dashboard.assignedCustomersIds); | |
444 | + } | |
445 | + | |
446 | + function assignDashboardsToCustomers($event, items) { | |
447 | + var dashboardIds = []; | |
448 | + for (var id in items.selections) { | |
449 | + dashboardIds.push(id); | |
450 | + } | |
451 | + showManageAssignedCustomersDialog($event, dashboardIds, 'assign'); | |
452 | + } | |
453 | + | |
454 | + function unassignDashboardsFromCustomers($event, items) { | |
455 | + var dashboardIds = []; | |
456 | + for (var id in items.selections) { | |
457 | + dashboardIds.push(id); | |
458 | + } | |
459 | + showManageAssignedCustomersDialog($event, dashboardIds, 'unassign'); | |
460 | + } | |
461 | + | |
462 | + function showManageAssignedCustomersDialog($event, dashboardIds, actionType, assignedCustomers) { | |
432 | 463 | if ($event) { |
433 | 464 | $event.stopPropagation(); |
434 | 465 | } |
435 | - var pageSize = 10; | |
436 | - customerService.getCustomers({limit: pageSize, textSearch: ''}).then( | |
437 | - function success(_customers) { | |
438 | - var customers = { | |
439 | - pageSize: pageSize, | |
440 | - data: _customers.data, | |
441 | - nextPageLink: _customers.nextPageLink, | |
442 | - selection: null, | |
443 | - hasNext: _customers.hasNext, | |
444 | - pending: false | |
445 | - }; | |
446 | - if (customers.hasNext) { | |
447 | - customers.nextPageLink.limit = pageSize; | |
448 | - } | |
449 | - $mdDialog.show({ | |
450 | - controller: 'AssignDashboardToCustomerController', | |
451 | - controllerAs: 'vm', | |
452 | - templateUrl: assignToCustomerTemplate, | |
453 | - locals: {dashboardIds: dashboardIds, customers: customers}, | |
454 | - parent: angular.element($document[0].body), | |
455 | - fullscreen: true, | |
456 | - targetEvent: $event | |
457 | - }).then(function () { | |
458 | - vm.grid.refreshList(); | |
459 | - }, function () { | |
460 | - }); | |
461 | - }, | |
462 | - function fail() { | |
463 | - }); | |
466 | + $mdDialog.show({ | |
467 | + controller: 'ManageAssignedCustomersController', | |
468 | + controllerAs: 'vm', | |
469 | + templateUrl: manageAssignedCustomersTemplate, | |
470 | + locals: {actionType: actionType, dashboardIds: dashboardIds, assignedCustomers: assignedCustomers}, | |
471 | + parent: angular.element($document[0].body), | |
472 | + fullscreen: true, | |
473 | + targetEvent: $event | |
474 | + }).then(function () { | |
475 | + vm.grid.refreshList(); | |
476 | + }, function () { | |
477 | + }); | |
464 | 478 | } |
465 | 479 | |
466 | 480 | function addDashboardsToCustomer($event) { |
... | ... | @@ -468,7 +482,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
468 | 482 | $event.stopPropagation(); |
469 | 483 | } |
470 | 484 | var pageSize = 10; |
471 | - dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}, false).then( | |
485 | + dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then( | |
472 | 486 | function success(_dashboards) { |
473 | 487 | var dashboards = { |
474 | 488 | pageSize: pageSize, |
... | ... | @@ -499,30 +513,13 @@ export function DashboardsController(userService, dashboardService, customerServ |
499 | 513 | }); |
500 | 514 | } |
501 | 515 | |
502 | - function assignDashboardsToCustomer($event, items) { | |
503 | - var dashboardIds = []; | |
504 | - for (var id in items.selections) { | |
505 | - dashboardIds.push(id); | |
506 | - } | |
507 | - assignToCustomer($event, dashboardIds); | |
508 | - } | |
509 | - | |
510 | - function unassignFromCustomer($event, dashboard, isPublic) { | |
516 | + function unassignFromCustomer($event, dashboard, customerId) { | |
511 | 517 | if ($event) { |
512 | 518 | $event.stopPropagation(); |
513 | 519 | } |
514 | - var title; | |
515 | - var content; | |
516 | - var label; | |
517 | - if (isPublic) { | |
518 | - title = $translate.instant('dashboard.make-private-dashboard-title', {dashboardTitle: dashboard.title}); | |
519 | - content = $translate.instant('dashboard.make-private-dashboard-text'); | |
520 | - label = $translate.instant('dashboard.make-private-dashboard'); | |
521 | - } else { | |
522 | - title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title}); | |
523 | - content = $translate.instant('dashboard.unassign-dashboard-text'); | |
524 | - label = $translate.instant('dashboard.unassign-dashboard'); | |
525 | - } | |
520 | + var title = $translate.instant('dashboard.unassign-dashboard-title', {dashboardTitle: dashboard.title}); | |
521 | + var content = $translate.instant('dashboard.unassign-dashboard-text'); | |
522 | + var label = $translate.instant('dashboard.unassign-dashboard'); | |
526 | 523 | var confirm = $mdDialog.confirm() |
527 | 524 | .targetEvent($event) |
528 | 525 | .title(title) |
... | ... | @@ -531,7 +528,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
531 | 528 | .cancel($translate.instant('action.no')) |
532 | 529 | .ok($translate.instant('action.yes')); |
533 | 530 | $mdDialog.show(confirm).then(function () { |
534 | - dashboardService.unassignDashboardFromCustomer(dashboard.id.id).then(function success() { | |
531 | + dashboardService.unassignDashboardFromCustomer(customerId, dashboard.id.id).then(function success() { | |
535 | 532 | vm.grid.refreshList(); |
536 | 533 | }); |
537 | 534 | }); |
... | ... | @@ -556,12 +553,33 @@ export function DashboardsController(userService, dashboardService, customerServ |
556 | 553 | }); |
557 | 554 | } |
558 | 555 | |
556 | + function makePrivate($event, dashboard) { | |
557 | + if ($event) { | |
558 | + $event.stopPropagation(); | |
559 | + } | |
560 | + var title = $translate.instant('dashboard.make-private-dashboard-title', {dashboardTitle: dashboard.title}); | |
561 | + var content = $translate.instant('dashboard.make-private-dashboard-text'); | |
562 | + var label = $translate.instant('dashboard.make-private-dashboard'); | |
563 | + var confirm = $mdDialog.confirm() | |
564 | + .targetEvent($event) | |
565 | + .title(title) | |
566 | + .htmlContent(content) | |
567 | + .ariaLabel(label) | |
568 | + .cancel($translate.instant('action.no')) | |
569 | + .ok($translate.instant('action.yes')); | |
570 | + $mdDialog.show(confirm).then(function () { | |
571 | + dashboardService.makeDashboardPrivate(dashboard.id.id).then(function success() { | |
572 | + vm.grid.refreshList(); | |
573 | + }); | |
574 | + }); | |
575 | + } | |
576 | + | |
559 | 577 | function exportDashboard($event, dashboard) { |
560 | 578 | $event.stopPropagation(); |
561 | 579 | importExport.exportDashboard(dashboard.id.id); |
562 | 580 | } |
563 | 581 | |
564 | - function unassignDashboardsFromCustomer($event, items) { | |
582 | + function unassignDashboardsFromCustomer($event, items, customerId) { | |
565 | 583 | var confirm = $mdDialog.confirm() |
566 | 584 | .targetEvent($event) |
567 | 585 | .title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat')) |
... | ... | @@ -572,7 +590,7 @@ export function DashboardsController(userService, dashboardService, customerServ |
572 | 590 | $mdDialog.show(confirm).then(function () { |
573 | 591 | var tasks = []; |
574 | 592 | for (var id in items.selections) { |
575 | - tasks.push(dashboardService.unassignDashboardFromCustomer(id)); | |
593 | + tasks.push(dashboardService.unassignDashboardFromCustomer(customerId, id)); | |
576 | 594 | } |
577 | 595 | $q.all(tasks).then(function () { |
578 | 596 | vm.grid.refreshList(); | ... | ... |
... | ... | @@ -25,10 +25,12 @@ |
25 | 25 | <tb-dashboard-details dashboard="vm.grid.operatingItem()" |
26 | 26 | is-edit="vm.grid.detailsConfig.isDetailsEditMode" |
27 | 27 | dashboard-scope="vm.dashboardsScope" |
28 | + customer-id="vm.customerId" | |
28 | 29 | the-form="vm.grid.detailsForm" |
29 | - on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])" | |
30 | 30 | on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)" |
31 | - on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, isPublic)" | |
31 | + on-make-private="vm.makePrivate(event, vm.grid.detailsConfig.currentItem)" | |
32 | + on-manage-assigned-customers="vm.manageAssignedCustomers(event, vm.grid.detailsConfig.currentItem)" | |
33 | + on-unassign-from-customer="vm.unassignFromCustomer(event, vm.grid.detailsConfig.currentItem, vm.customerId)" | |
32 | 34 | on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)" |
33 | 35 | on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details> |
34 | 36 | </md-tab> | ... | ... |
... | ... | @@ -40,8 +40,8 @@ import DashboardRoutes from './dashboard.routes'; |
40 | 40 | import {DashboardsController, DashboardCardController, MakeDashboardPublicDialogController} from './dashboards.controller'; |
41 | 41 | import DashboardController from './dashboard.controller'; |
42 | 42 | import DashboardSettingsController from './dashboard-settings.controller'; |
43 | -import AssignDashboardToCustomerController from './assign-to-customer.controller'; | |
44 | 43 | import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller'; |
44 | +import ManageAssignedCustomersController from './manage-assigned-customers.controller'; | |
45 | 45 | import AddWidgetController from './add-widget.controller'; |
46 | 46 | import DashboardDirective from './dashboard.directive'; |
47 | 47 | import EditWidgetDirective from './edit-widget.directive'; |
... | ... | @@ -74,8 +74,8 @@ export default angular.module('thingsboard.dashboard', [ |
74 | 74 | .controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController) |
75 | 75 | .controller('DashboardController', DashboardController) |
76 | 76 | .controller('DashboardSettingsController', DashboardSettingsController) |
77 | - .controller('AssignDashboardToCustomerController', AssignDashboardToCustomerController) | |
78 | 77 | .controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController) |
78 | + .controller('ManageAssignedCustomersController', ManageAssignedCustomersController) | |
79 | 79 | .controller('AddWidgetController', AddWidgetController) |
80 | 80 | .directive('tbDashboardDetails', DashboardDirective) |
81 | 81 | .directive('tbEditWidget', EditWidgetDirective) | ... | ... |
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 | +/*@ngInject*/ | |
17 | +export default function ManageAssignedCustomersController($mdDialog, $q, types, dashboardService, actionType, dashboardIds, assignedCustomers) { | |
18 | + | |
19 | + var vm = this; | |
20 | + | |
21 | + vm.types = types; | |
22 | + vm.actionType = actionType; | |
23 | + vm.dashboardIds = dashboardIds; | |
24 | + vm.assignedCustomers = assignedCustomers; | |
25 | + if (actionType != 'manage') { | |
26 | + vm.assignedCustomers = []; | |
27 | + } | |
28 | + | |
29 | + if (actionType == 'manage') { | |
30 | + vm.titleText = 'dashboard.manage-assigned-customers'; | |
31 | + vm.labelText = 'dashboard.assigned-customers'; | |
32 | + vm.actionName = 'action.update'; | |
33 | + } else if (actionType == 'assign') { | |
34 | + vm.titleText = 'dashboard.assign-to-customers'; | |
35 | + vm.labelText = 'dashboard.assign-to-customers-text'; | |
36 | + vm.actionName = 'action.assign'; | |
37 | + } else if (actionType == 'unassign') { | |
38 | + vm.titleText = 'dashboard.unassign-from-customers'; | |
39 | + vm.labelText = 'dashboard.unassign-from-customers-text'; | |
40 | + vm.actionName = 'action.unassign'; | |
41 | + } | |
42 | + | |
43 | + vm.submit = submit; | |
44 | + vm.cancel = cancel; | |
45 | + | |
46 | + function cancel () { | |
47 | + $mdDialog.cancel(); | |
48 | + } | |
49 | + | |
50 | + function submit () { | |
51 | + var tasks = []; | |
52 | + for (var i=0;i<vm.dashboardIds.length;i++) { | |
53 | + var dashboardId = vm.dashboardIds[i]; | |
54 | + var promise; | |
55 | + if (vm.actionType == 'manage') { | |
56 | + promise = dashboardService.updateDashboardCustomers(dashboardId, vm.assignedCustomers); | |
57 | + } else if (vm.actionType == 'assign') { | |
58 | + promise = dashboardService.addDashboardCustomers(dashboardId, vm.assignedCustomers); | |
59 | + } else if (vm.actionType == 'unassign') { | |
60 | + promise = dashboardService.removeDashboardCustomers(dashboardId, vm.assignedCustomers); | |
61 | + } | |
62 | + tasks.push(promise); | |
63 | + } | |
64 | + $q.all(tasks).then(function () { | |
65 | + $mdDialog.hide(); | |
66 | + }); | |
67 | + } | |
68 | + | |
69 | +} | ... | ... |
ui/src/app/dashboard/manage-assigned-customers.tpl.html
renamed from
ui/src/app/dashboard/assign-to-customer.tpl.html
... | ... | @@ -15,11 +15,11 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<md-dialog aria-label="{{ 'dashboard.assign-dashboard-to-customer' | translate }}"> | |
19 | - <form name="theForm" ng-submit="vm.assign()"> | |
18 | +<md-dialog aria-label="{{ vm.titleText | translate }}" style="width: 600px;"> | |
19 | + <form name="theForm" ng-submit="vm.submit()"> | |
20 | 20 | <md-toolbar> |
21 | 21 | <div class="md-toolbar-tools"> |
22 | - <h2 translate>dashboard.assign-dashboard-to-customer</h2> | |
22 | + <h2 translate>{{vm.titleText}}</h2> | |
23 | 23 | <span flex></span> |
24 | 24 | <md-button class="md-icon-button" ng-click="vm.cancel()"> |
25 | 25 | <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
... | ... | @@ -31,42 +31,17 @@ |
31 | 31 | <md-dialog-content> |
32 | 32 | <div class="md-dialog-content"> |
33 | 33 | <fieldset> |
34 | - <span translate>dashboard.assign-to-customer-text</span> | |
35 | - <md-input-container class="md-block" style='margin-bottom: 0px;'> | |
36 | - <label> </label> | |
37 | - <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons"> | |
38 | - search | |
39 | - </md-icon> | |
40 | - <input id="customer-search" autofocus ng-model="vm.searchText" | |
41 | - ng-change="vm.searchCustomerTextUpdated()" | |
42 | - placeholder="{{ 'common.enter-search' | translate }}"/> | |
43 | - </md-input-container> | |
44 | - <div style='min-height: 150px;'> | |
45 | - <span translate layout-align="center center" | |
46 | - style="text-transform: uppercase; display: flex; height: 150px;" | |
47 | - class="md-subhead" | |
48 | - ng-show="vm.noData()">customer.no-customers-text</span> | |
49 | - <md-virtual-repeat-container ng-show="vm.hasData()" | |
50 | - tb-scope-element="repeatContainer" md-top-index="vm.topIndex" flex | |
51 | - style='min-height: 150px; width: 100%;'> | |
52 | - <md-list> | |
53 | - <md-list-item md-virtual-repeat="customer in vm.theCustomers" md-on-demand | |
54 | - class="repeated-item" flex> | |
55 | - <md-checkbox ng-click="vm.toggleCustomerSelection($event, customer)" | |
56 | - aria-label="{{ 'item.selected' | translate }}" | |
57 | - ng-checked="vm.isCustomerSelected(customer)"></md-checkbox> | |
58 | - <span> {{ customer.title }} </span> | |
59 | - </md-list-item> | |
60 | - </md-list> | |
61 | - </md-virtual-repeat-container> | |
62 | - </div> | |
34 | + <span translate>{{vm.labelText}}</span> | |
35 | + <tb-entity-list ng-disabled="$root.loading" | |
36 | + ng-model="vm.assignedCustomers" | |
37 | + entity-type="vm.types.entityType.customer"></tb-entity-list> | |
63 | 38 | </fieldset> |
64 | 39 | </div> |
65 | 40 | </md-dialog-content> |
66 | 41 | <md-dialog-actions layout="row"> |
67 | 42 | <span flex></span> |
68 | - <md-button ng-disabled="$root.loading || vm.customers.selection==null" type="submit" class="md-raised md-primary"> | |
69 | - {{ 'action.assign' | translate }} | |
43 | + <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary"> | |
44 | + {{ vm.actionName | translate }} | |
70 | 45 | </md-button> |
71 | 46 | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
72 | 47 | translate }} | ... | ... |
... | ... | @@ -38,7 +38,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter |
38 | 38 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { |
39 | 39 | limit += scope.excludeEntityIds.length; |
40 | 40 | } |
41 | - entityService.getEntitiesByNameFilter(scope.entityType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) { | |
41 | + var targetType = scope.entityType; | |
42 | + if (targetType == types.aliasEntityType.current_customer) { | |
43 | + targetType = types.entityType.customer; | |
44 | + } | |
45 | + | |
46 | + entityService.getEntitiesByNameFilter(targetType, searchText, limit, {ignoreLoading: true}, scope.entitySubtype).then(function success(result) { | |
42 | 47 | if (result) { |
43 | 48 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { |
44 | 49 | var entities = []; |
... | ... | @@ -71,7 +76,11 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter |
71 | 76 | |
72 | 77 | ngModelCtrl.$render = function () { |
73 | 78 | if (ngModelCtrl.$viewValue) { |
74 | - entityService.getEntity(scope.entityType, ngModelCtrl.$viewValue).then( | |
79 | + var targetType = scope.entityType; | |
80 | + if (targetType == types.aliasEntityType.current_customer) { | |
81 | + targetType = types.entityType.customer; | |
82 | + } | |
83 | + entityService.getEntity(targetType, ngModelCtrl.$viewValue).then( | |
75 | 84 | function success(entity) { |
76 | 85 | scope.entity = entity; |
77 | 86 | }, |
... | ... | @@ -114,55 +123,61 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter |
114 | 123 | scope.selectEntityText = 'asset.select-asset'; |
115 | 124 | scope.entityText = 'asset.asset'; |
116 | 125 | scope.noEntitiesMatchingText = 'asset.no-assets-matching'; |
117 | - scope.entityRequiredText = 'asset.asset-required' | |
126 | + scope.entityRequiredText = 'asset.asset-required'; | |
118 | 127 | break; |
119 | 128 | case types.entityType.device: |
120 | 129 | scope.selectEntityText = 'device.select-device'; |
121 | 130 | scope.entityText = 'device.device'; |
122 | 131 | scope.noEntitiesMatchingText = 'device.no-devices-matching'; |
123 | - scope.entityRequiredText = 'device.device-required' | |
132 | + scope.entityRequiredText = 'device.device-required'; | |
124 | 133 | break; |
125 | 134 | case types.entityType.rule: |
126 | 135 | scope.selectEntityText = 'rule.select-rule'; |
127 | 136 | scope.entityText = 'rule.rule'; |
128 | 137 | scope.noEntitiesMatchingText = 'rule.no-rules-matching'; |
129 | - scope.entityRequiredText = 'rule.rule-required' | |
138 | + scope.entityRequiredText = 'rule.rule-required'; | |
130 | 139 | break; |
131 | 140 | case types.entityType.plugin: |
132 | 141 | scope.selectEntityText = 'plugin.select-plugin'; |
133 | 142 | scope.entityText = 'plugin.plugin'; |
134 | 143 | scope.noEntitiesMatchingText = 'plugin.no-plugins-matching'; |
135 | - scope.entityRequiredText = 'plugin.plugin-required' | |
144 | + scope.entityRequiredText = 'plugin.plugin-required'; | |
136 | 145 | break; |
137 | 146 | case types.entityType.tenant: |
138 | 147 | scope.selectEntityText = 'tenant.select-tenant'; |
139 | 148 | scope.entityText = 'tenant.tenant'; |
140 | 149 | scope.noEntitiesMatchingText = 'tenant.no-tenants-matching'; |
141 | - scope.entityRequiredText = 'tenant.tenant-required' | |
150 | + scope.entityRequiredText = 'tenant.tenant-required'; | |
142 | 151 | break; |
143 | 152 | case types.entityType.customer: |
144 | 153 | scope.selectEntityText = 'customer.select-customer'; |
145 | 154 | scope.entityText = 'customer.customer'; |
146 | 155 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; |
147 | - scope.entityRequiredText = 'customer.customer-required' | |
156 | + scope.entityRequiredText = 'customer.customer-required'; | |
148 | 157 | break; |
149 | 158 | case types.entityType.user: |
150 | 159 | scope.selectEntityText = 'user.select-user'; |
151 | 160 | scope.entityText = 'user.user'; |
152 | 161 | scope.noEntitiesMatchingText = 'user.no-users-matching'; |
153 | - scope.entityRequiredText = 'user.user-required' | |
162 | + scope.entityRequiredText = 'user.user-required'; | |
154 | 163 | break; |
155 | 164 | case types.entityType.dashboard: |
156 | 165 | scope.selectEntityText = 'dashboard.select-dashboard'; |
157 | 166 | scope.entityText = 'dashboard.dashboard'; |
158 | 167 | scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; |
159 | - scope.entityRequiredText = 'dashboard.dashboard-required' | |
168 | + scope.entityRequiredText = 'dashboard.dashboard-required'; | |
160 | 169 | break; |
161 | 170 | case types.entityType.alarm: |
162 | 171 | scope.selectEntityText = 'alarm.select-alarm'; |
163 | 172 | scope.entityText = 'alarm.alarm'; |
164 | 173 | scope.noEntitiesMatchingText = 'alarm.no-alarms-matching'; |
165 | - scope.entityRequiredText = 'alarm.alarm-required' | |
174 | + scope.entityRequiredText = 'alarm.alarm-required'; | |
175 | + break; | |
176 | + case types.aliasEntityType.current_customer: | |
177 | + scope.selectEntityText = 'customer.select-default-customer'; | |
178 | + scope.entityText = 'customer.default-customer'; | |
179 | + scope.noEntitiesMatchingText = 'customer.no-customers-matching'; | |
180 | + scope.entityRequiredText = 'customer.default-customer-required'; | |
166 | 181 | break; |
167 | 182 | } |
168 | 183 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { | ... | ... |
... | ... | @@ -32,6 +32,7 @@ |
32 | 32 | <tb-entity-select flex |
33 | 33 | the-form="theForm" |
34 | 34 | tb-required="true" |
35 | + use-alias-entity-types="true" | |
35 | 36 | ng-model="filter.singleEntity"> |
36 | 37 | </tb-entity-select> |
37 | 38 | </section> |
... | ... | @@ -78,6 +79,7 @@ |
78 | 79 | <tb-entity-select flex |
79 | 80 | the-form="theForm" |
80 | 81 | tb-required="false" |
82 | + use-alias-entity-types="true" | |
81 | 83 | ng-model="filter.defaultStateEntity"> |
82 | 84 | </tb-entity-select> |
83 | 85 | </div> |
... | ... | @@ -123,6 +125,7 @@ |
123 | 125 | the-form="theForm" |
124 | 126 | tb-required="!filter.rootStateEntity" |
125 | 127 | ng-disabled="filter.rootStateEntity" |
128 | + use-alias-entity-types="true" | |
126 | 129 | ng-model="filter.rootEntity"> |
127 | 130 | </tb-entity-select> |
128 | 131 | </div> |
... | ... | @@ -139,6 +142,7 @@ |
139 | 142 | <tb-entity-select flex |
140 | 143 | the-form="theForm" |
141 | 144 | tb-required="false" |
145 | + use-alias-entity-types="true" | |
142 | 146 | ng-model="filter.defaultStateEntity"> |
143 | 147 | </tb-entity-select> |
144 | 148 | </div> |
... | ... | @@ -182,6 +186,7 @@ |
182 | 186 | the-form="theForm" |
183 | 187 | tb-required="!filter.rootStateEntity" |
184 | 188 | ng-disabled="filter.rootStateEntity" |
189 | + use-alias-entity-types="true" | |
185 | 190 | ng-model="filter.rootEntity"> |
186 | 191 | </tb-entity-select> |
187 | 192 | </div> |
... | ... | @@ -198,6 +203,7 @@ |
198 | 203 | <tb-entity-select flex |
199 | 204 | the-form="theForm" |
200 | 205 | tb-required="false" |
206 | + use-alias-entity-types="true" | |
201 | 207 | ng-model="filter.defaultStateEntity"> |
202 | 208 | </tb-entity-select> |
203 | 209 | </div> |
... | ... | @@ -249,6 +255,7 @@ |
249 | 255 | the-form="theForm" |
250 | 256 | tb-required="!filter.rootStateEntity" |
251 | 257 | ng-disabled="filter.rootStateEntity" |
258 | + use-alias-entity-types="true" | |
252 | 259 | ng-model="filter.rootEntity"> |
253 | 260 | </tb-entity-select> |
254 | 261 | </div> |
... | ... | @@ -265,6 +272,7 @@ |
265 | 272 | <tb-entity-select flex |
266 | 273 | the-form="theForm" |
267 | 274 | tb-required="false" |
275 | + use-alias-entity-types="true" | |
268 | 276 | ng-model="filter.defaultStateEntity"> |
269 | 277 | </tb-entity-select> |
270 | 278 | </div> | ... | ... |
... | ... | @@ -39,7 +39,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity |
39 | 39 | |
40 | 40 | scope.ngModelCtrl = ngModelCtrl; |
41 | 41 | |
42 | - scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); | |
42 | + scope.entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes, scope.useAliasEntityTypes); | |
43 | 43 | |
44 | 44 | scope.typeName = function(type) { |
45 | 45 | return type ? types.entityTypeTranslations[type].type : ''; |
... | ... | @@ -79,7 +79,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity |
79 | 79 | theForm: '=?', |
80 | 80 | tbRequired: '=?', |
81 | 81 | disabled:'=ngDisabled', |
82 | - allowedEntityTypes: "=?" | |
82 | + allowedEntityTypes: "=?", | |
83 | + useAliasEntityTypes: "=?" | |
83 | 84 | } |
84 | 85 | }; |
85 | 86 | } | ... | ... |
... | ... | @@ -540,7 +540,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
540 | 540 | function success(dashboard) { |
541 | 541 | var name = dashboard.title; |
542 | 542 | name = name.toLowerCase().replace(/\W/g,"_"); |
543 | - exportToPc(prepareExport(dashboard), name + '.json'); | |
543 | + exportToPc(prepareDashboardExport(dashboard), name + '.json'); | |
544 | 544 | }, |
545 | 545 | function fail(rejection) { |
546 | 546 | var message = rejection; |
... | ... | @@ -552,6 +552,15 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, |
552 | 552 | ); |
553 | 553 | } |
554 | 554 | |
555 | + function prepareDashboardExport(dashboard) { | |
556 | + dashboard = prepareExport(dashboard); | |
557 | + delete dashboard.assignedCustomers; | |
558 | + delete dashboard.publicCustomerId; | |
559 | + delete dashboard.assignedCustomersText; | |
560 | + delete dashboard.assignedCustomersIds; | |
561 | + return dashboard; | |
562 | + } | |
563 | + | |
555 | 564 | function importDashboard($event) { |
556 | 565 | var deferred = $q.defer(); |
557 | 566 | openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then( | ... | ... |
... | ... | @@ -383,7 +383,10 @@ export default angular.module('thingsboard.locale', []) |
383 | 383 | "idCopiedMessage": "Customer Id has been copied to clipboard", |
384 | 384 | "select-customer": "Select customer", |
385 | 385 | "no-customers-matching": "No customers matching '{{entity}}' were found.", |
386 | - "customer-required": "Customer is required" | |
386 | + "customer-required": "Customer is required", | |
387 | + "select-default-customer": "Select default customer", | |
388 | + "default-customer": "Default customer", | |
389 | + "default-customer-required": "Default customer is required in order to debug dashboard on Tenant level" | |
387 | 390 | }, |
388 | 391 | "datetime": { |
389 | 392 | "date-from": "Date from", |
... | ... | @@ -404,6 +407,12 @@ export default angular.module('thingsboard.locale', []) |
404 | 407 | "unassign-from-customer": "Unassign from customer", |
405 | 408 | "make-public": "Make dashboard public", |
406 | 409 | "make-private": "Make dashboard private", |
410 | + "manage-assigned-customers": "Manage assigned customers", | |
411 | + "assigned-customers": "Assigned customers", | |
412 | + "assign-to-customers": "Assign Dashboard(s) To Customers", | |
413 | + "assign-to-customers-text": "Please select the customers to assign the dashboard(s)", | |
414 | + "unassign-from-customers": "Unassign Dashboard(s) From Customers", | |
415 | + "unassign-from-customers-text": "Please select the customers to unassign from the dashboard(s)", | |
407 | 416 | "no-dashboards-text": "No dashboards found", |
408 | 417 | "no-widgets": "No widgets configured", |
409 | 418 | "add-widget": "Add new widget", |
... | ... | @@ -418,7 +427,8 @@ export default angular.module('thingsboard.locale', []) |
418 | 427 | "add-dashboard-text": "Add new dashboard", |
419 | 428 | "assign-dashboards": "Assign dashboards", |
420 | 429 | "assign-new-dashboard": "Assign new dashboard", |
421 | - "assign-dashboards-text": "Assign { count, select, 1 {1 dashboard} other {# dashboards} } to customer", | |
430 | + "assign-dashboards-text": "Assign { count, select, 1 {1 dashboard} other {# dashboards} } to customers", | |
431 | + "unassign-dashboards-action-text": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customers", | |
422 | 432 | "delete-dashboards": "Delete dashboards", |
423 | 433 | "unassign-dashboards": "Unassign dashboards", |
424 | 434 | "unassign-dashboards-action-title": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customer", |
... | ... | @@ -500,6 +510,7 @@ export default angular.module('thingsboard.locale', []) |
500 | 510 | "Please contact your administrator in order to resolve this issue.", |
501 | 511 | "select-devices": "Select devices", |
502 | 512 | "assignedToCustomer": "Assigned to customer", |
513 | + "assignedToCustomers": "Assigned to customers", | |
503 | 514 | "public": "Public", |
504 | 515 | "public-link": "Public link", |
505 | 516 | "copy-public-link": "Copy public link", |
... | ... | @@ -735,6 +746,7 @@ export default angular.module('thingsboard.locale', []) |
735 | 746 | "type-alarms": "Alarms", |
736 | 747 | "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", |
737 | 748 | "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", |
749 | + "type-current-customer": "Current Customer", | |
738 | 750 | "search": "Search entities", |
739 | 751 | "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", |
740 | 752 | "entity-name": "Entity name", | ... | ... |