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,3 +87,26 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions ( | ||
87 | PRIMARY KEY (( tenant_id ), partition) | 87 | PRIMARY KEY (( tenant_id ), partition) |
88 | ) WITH CLUSTERING ORDER BY ( partition ASC ) | 88 | ) WITH CLUSTERING ORDER BY ( partition ASC ) |
89 | AND compaction = { 'class' : 'LeveledCompactionStrategy' }; | 89 | AND compaction = { 'class' : 'LeveledCompactionStrategy' }; |
90 | + | ||
91 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_tenant_and_search_text; | ||
92 | +DROP MATERIALIZED VIEW IF EXISTS thingsboard.dashboard_by_customer_and_search_text; | ||
93 | + | ||
94 | +DROP TABLE IF EXISTS thingsboard.dashboard; | ||
95 | + | ||
96 | +CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( | ||
97 | + id timeuuid, | ||
98 | + tenant_id timeuuid, | ||
99 | + title text, | ||
100 | + search_text text, | ||
101 | + assigned_customers text, | ||
102 | + configuration text, | ||
103 | + PRIMARY KEY (id, tenant_id) | ||
104 | +); | ||
105 | + | ||
106 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS | ||
107 | + SELECT * | ||
108 | + from thingsboard.dashboard | ||
109 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
110 | + PRIMARY KEY ( tenant_id, search_text, id ) | ||
111 | + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); | ||
112 | + |
@@ -29,3 +29,13 @@ CREATE TABLE IF NOT EXISTS audit_log ( | @@ -29,3 +29,13 @@ CREATE TABLE IF NOT EXISTS audit_log ( | ||
29 | action_failure_details varchar(1000000) | 29 | action_failure_details varchar(1000000) |
30 | ); | 30 | ); |
31 | 31 | ||
32 | +DROP TABLE IF EXISTS dashboard; | ||
33 | + | ||
34 | +CREATE TABLE IF NOT EXISTS dashboard ( | ||
35 | + id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, | ||
36 | + configuration varchar(10000000), | ||
37 | + assigned_customers varchar(1000000), | ||
38 | + search_text varchar(255), | ||
39 | + tenant_id varchar(31), | ||
40 | + title varchar(255) | ||
41 | +); |
@@ -423,7 +423,7 @@ public abstract class BaseController { | @@ -423,7 +423,7 @@ public abstract class BaseController { | ||
423 | try { | 423 | try { |
424 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | 424 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
425 | Dashboard dashboard = dashboardService.findDashboardById(dashboardId); | 425 | Dashboard dashboard = dashboardService.findDashboardById(dashboardId); |
426 | - checkDashboard(dashboard, true); | 426 | + checkDashboard(dashboard); |
427 | return dashboard; | 427 | return dashboard; |
428 | } catch (Exception e) { | 428 | } catch (Exception e) { |
429 | throw handleException(e, false); | 429 | throw handleException(e, false); |
@@ -434,28 +434,23 @@ public abstract class BaseController { | @@ -434,28 +434,23 @@ public abstract class BaseController { | ||
434 | try { | 434 | try { |
435 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | 435 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
436 | DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); | 436 | DashboardInfo dashboardInfo = dashboardService.findDashboardInfoById(dashboardId); |
437 | - SecurityUser authUser = getCurrentUser(); | ||
438 | - checkDashboard(dashboardInfo, authUser.getAuthority() != Authority.SYS_ADMIN); | 437 | + checkDashboard(dashboardInfo); |
439 | return dashboardInfo; | 438 | return dashboardInfo; |
440 | } catch (Exception e) { | 439 | } catch (Exception e) { |
441 | throw handleException(e, false); | 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 | checkNotNull(dashboard); | 445 | checkNotNull(dashboard); |
447 | checkTenantId(dashboard.getTenantId()); | 446 | checkTenantId(dashboard.getTenantId()); |
448 | SecurityUser authUser = getCurrentUser(); | 447 | SecurityUser authUser = getCurrentUser(); |
449 | if (authUser.getAuthority() == Authority.CUSTOMER_USER) { | 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 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, | 450 | throw new ThingsboardException(YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION, |
452 | ThingsboardErrorCode.PERMISSION_DENIED); | 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 | ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { | 456 | ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { |
@@ -18,20 +18,22 @@ package org.thingsboard.server.controller; | @@ -18,20 +18,22 @@ package org.thingsboard.server.controller; | ||
18 | import org.springframework.http.HttpStatus; | 18 | import org.springframework.http.HttpStatus; |
19 | import org.springframework.security.access.prepost.PreAuthorize; | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | import org.springframework.web.bind.annotation.*; | 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 | import org.thingsboard.server.common.data.audit.ActionType; | 22 | import org.thingsboard.server.common.data.audit.ActionType; |
26 | import org.thingsboard.server.common.data.id.CustomerId; | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
27 | import org.thingsboard.server.common.data.id.DashboardId; | 24 | import org.thingsboard.server.common.data.id.DashboardId; |
28 | import org.thingsboard.server.common.data.id.TenantId; | 25 | import org.thingsboard.server.common.data.id.TenantId; |
29 | import org.thingsboard.server.common.data.page.TextPageData; | 26 | import org.thingsboard.server.common.data.page.TextPageData; |
30 | import org.thingsboard.server.common.data.page.TextPageLink; | 27 | import org.thingsboard.server.common.data.page.TextPageLink; |
28 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
29 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
31 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 30 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
32 | import org.thingsboard.server.dao.model.ModelConstants; | 31 | import org.thingsboard.server.dao.model.ModelConstants; |
33 | import org.thingsboard.server.exception.ThingsboardException; | 32 | import org.thingsboard.server.exception.ThingsboardException; |
34 | 33 | ||
34 | +import java.util.HashSet; | ||
35 | +import java.util.Set; | ||
36 | + | ||
35 | @RestController | 37 | @RestController |
36 | @RequestMapping("/api") | 38 | @RequestMapping("/api") |
37 | public class DashboardController extends BaseController { | 39 | public class DashboardController extends BaseController { |
@@ -80,7 +82,7 @@ public class DashboardController extends BaseController { | @@ -80,7 +82,7 @@ public class DashboardController extends BaseController { | ||
80 | Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); | 82 | Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); |
81 | 83 | ||
82 | logEntityAction(savedDashboard.getId(), savedDashboard, | 84 | logEntityAction(savedDashboard.getId(), savedDashboard, |
83 | - savedDashboard.getCustomerId(), | 85 | + null, |
84 | dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); | 86 | dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
85 | 87 | ||
86 | return savedDashboard; | 88 | return savedDashboard; |
@@ -103,7 +105,7 @@ public class DashboardController extends BaseController { | @@ -103,7 +105,7 @@ public class DashboardController extends BaseController { | ||
103 | dashboardService.deleteDashboard(dashboardId); | 105 | dashboardService.deleteDashboard(dashboardId); |
104 | 106 | ||
105 | logEntityAction(dashboardId, dashboard, | 107 | logEntityAction(dashboardId, dashboard, |
106 | - dashboard.getCustomerId(), | 108 | + null, |
107 | ActionType.DELETED, null, strDashboardId); | 109 | ActionType.DELETED, null, strDashboardId); |
108 | 110 | ||
109 | } catch (Exception e) { | 111 | } catch (Exception e) { |
@@ -134,7 +136,7 @@ public class DashboardController extends BaseController { | @@ -134,7 +136,7 @@ public class DashboardController extends BaseController { | ||
134 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); | 136 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, customerId)); |
135 | 137 | ||
136 | logEntityAction(dashboardId, savedDashboard, | 138 | logEntityAction(dashboardId, savedDashboard, |
137 | - savedDashboard.getCustomerId(), | 139 | + customerId, |
138 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); | 140 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); |
139 | 141 | ||
140 | 142 | ||
@@ -150,23 +152,22 @@ public class DashboardController extends BaseController { | @@ -150,23 +152,22 @@ public class DashboardController extends BaseController { | ||
150 | } | 152 | } |
151 | 153 | ||
152 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | 154 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
153 | - @RequestMapping(value = "/customer/dashboard/{dashboardId}", method = RequestMethod.DELETE) | 155 | + @RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE) |
154 | @ResponseBody | 156 | @ResponseBody |
155 | - public Dashboard unassignDashboardFromCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | 157 | + public Dashboard unassignDashboardFromCustomer(@PathVariable("customerId") String strCustomerId, |
158 | + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | ||
159 | + checkParameter("customerId", strCustomerId); | ||
156 | checkParameter(DASHBOARD_ID, strDashboardId); | 160 | checkParameter(DASHBOARD_ID, strDashboardId); |
157 | try { | 161 | try { |
162 | + CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | ||
163 | + Customer customer = checkCustomerId(customerId); | ||
158 | DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); | 164 | DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); |
159 | Dashboard dashboard = checkDashboardId(dashboardId); | 165 | Dashboard dashboard = checkDashboardId(dashboardId); |
160 | - if (dashboard.getCustomerId() == null || dashboard.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
161 | - throw new IncorrectParameterException("Dashboard isn't assigned to any customer!"); | ||
162 | - } | ||
163 | 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 | logEntityAction(dashboardId, dashboard, | 169 | logEntityAction(dashboardId, dashboard, |
169 | - dashboard.getCustomerId(), | 170 | + customerId, |
170 | ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); | 171 | ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); |
171 | 172 | ||
172 | return savedDashboard; | 173 | return savedDashboard; |
@@ -181,6 +182,158 @@ public class DashboardController extends BaseController { | @@ -181,6 +182,158 @@ public class DashboardController extends BaseController { | ||
181 | } | 182 | } |
182 | 183 | ||
183 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | 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 | @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) | 337 | @RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST) |
185 | @ResponseBody | 338 | @ResponseBody |
186 | public Dashboard assignDashboardToPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { | 339 | public Dashboard assignDashboardToPublicCustomer(@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { |
@@ -192,7 +345,7 @@ public class DashboardController extends BaseController { | @@ -192,7 +345,7 @@ public class DashboardController extends BaseController { | ||
192 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); | 345 | Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(dashboardId, publicCustomer.getId())); |
193 | 346 | ||
194 | logEntityAction(dashboardId, savedDashboard, | 347 | logEntityAction(dashboardId, savedDashboard, |
195 | - savedDashboard.getCustomerId(), | 348 | + publicCustomer.getId(), |
196 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); | 349 | ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, publicCustomer.getId().toString(), publicCustomer.getName()); |
197 | 350 | ||
198 | return savedDashboard; | 351 | return savedDashboard; |
@@ -206,6 +359,33 @@ public class DashboardController extends BaseController { | @@ -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 | @PreAuthorize("hasAuthority('SYS_ADMIN')") | 389 | @PreAuthorize("hasAuthority('SYS_ADMIN')") |
210 | @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET) | 390 | @RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET) |
211 | @ResponseBody | 391 | @ResponseBody |
@@ -245,19 +425,20 @@ public class DashboardController extends BaseController { | @@ -245,19 +425,20 @@ public class DashboardController extends BaseController { | ||
245 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 425 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
246 | @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) | 426 | @RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET) |
247 | @ResponseBody | 427 | @ResponseBody |
248 | - public TextPageData<DashboardInfo> getCustomerDashboards( | 428 | + public TimePageData<DashboardInfo> getCustomerDashboards( |
249 | @PathVariable("customerId") String strCustomerId, | 429 | @PathVariable("customerId") String strCustomerId, |
250 | @RequestParam int limit, | 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 | checkParameter("customerId", strCustomerId); | 435 | checkParameter("customerId", strCustomerId); |
255 | try { | 436 | try { |
256 | TenantId tenantId = getCurrentUser().getTenantId(); | 437 | TenantId tenantId = getCurrentUser().getTenantId(); |
257 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); | 438 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
258 | checkCustomerId(customerId); | 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 | } catch (Exception e) { | 442 | } catch (Exception e) { |
262 | throw handleException(e); | 443 | throw handleException(e); |
263 | } | 444 | } |
@@ -24,6 +24,7 @@ import org.springframework.context.annotation.Profile; | @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Profile; | ||
24 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
25 | import org.thingsboard.server.dao.cassandra.CassandraCluster; | 25 | import org.thingsboard.server.dao.cassandra.CassandraCluster; |
26 | import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; | 26 | import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; |
27 | +import org.thingsboard.server.dao.dashboard.DashboardService; | ||
27 | import org.thingsboard.server.dao.util.NoSqlDao; | 28 | import org.thingsboard.server.dao.util.NoSqlDao; |
28 | import org.thingsboard.server.service.install.cql.CQLStatementsParser; | 29 | import org.thingsboard.server.service.install.cql.CQLStatementsParser; |
29 | import org.thingsboard.server.service.install.cql.CassandraDbHelper; | 30 | import org.thingsboard.server.service.install.cql.CassandraDbHelper; |
@@ -33,6 +34,8 @@ import java.nio.file.Path; | @@ -33,6 +34,8 @@ import java.nio.file.Path; | ||
33 | import java.nio.file.Paths; | 34 | import java.nio.file.Paths; |
34 | import java.util.List; | 35 | import java.util.List; |
35 | 36 | ||
37 | +import static org.thingsboard.server.service.install.DatabaseHelper.*; | ||
38 | + | ||
36 | @Service | 39 | @Service |
37 | @NoSqlDao | 40 | @NoSqlDao |
38 | @Profile("install") | 41 | @Profile("install") |
@@ -40,12 +43,6 @@ import java.util.List; | @@ -40,12 +43,6 @@ import java.util.List; | ||
40 | public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | 43 | public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
41 | 44 | ||
42 | private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; | 45 | private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; |
43 | - public static final String DEVICE = "device"; | ||
44 | - public static final String TENANT_ID = "tenant_id"; | ||
45 | - public static final String CUSTOMER_ID = "customer_id"; | ||
46 | - public static final String SEARCH_TEXT = "search_text"; | ||
47 | - public static final String ADDITIONAL_INFO = "additional_info"; | ||
48 | - public static final String ASSET = "asset"; | ||
49 | 46 | ||
50 | @Value("${install.data_dir}") | 47 | @Value("${install.data_dir}") |
51 | private String dataDir; | 48 | private String dataDir; |
@@ -56,6 +53,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -56,6 +53,9 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
56 | @Autowired | 53 | @Autowired |
57 | private CassandraInstallCluster installCluster; | 54 | private CassandraInstallCluster installCluster; |
58 | 55 | ||
56 | + @Autowired | ||
57 | + private DashboardService dashboardService; | ||
58 | + | ||
59 | @Override | 59 | @Override |
60 | public void upgradeDatabase(String fromVersion) throws Exception { | 60 | public void upgradeDatabase(String fromVersion) throws Exception { |
61 | 61 | ||
@@ -160,10 +160,32 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -160,10 +160,32 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
160 | case "1.3.0": | 160 | case "1.3.0": |
161 | break; | 161 | break; |
162 | case "1.3.1": | 162 | case "1.3.1": |
163 | + | ||
164 | + cluster.getSession(); | ||
165 | + | ||
166 | + ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); | ||
167 | + | ||
168 | + log.info("Dumping dashboards ..."); | ||
169 | + Path dashboardsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DASHBOARD, | ||
170 | + new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, | ||
171 | + new String[]{"", "", "", "", "", "", ""}, | ||
172 | + "tb-dashboards", true); | ||
173 | + log.info("Dashboards dumped."); | ||
174 | + | ||
175 | + | ||
163 | log.info("Updating schema ..."); | 176 | log.info("Updating schema ..."); |
164 | schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); | 177 | schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); |
165 | loadCql(schemaUpdateFile); | 178 | loadCql(schemaUpdateFile); |
166 | log.info("Schema updated."); | 179 | log.info("Schema updated."); |
180 | + | ||
181 | + log.info("Restoring dashboards ..."); | ||
182 | + if (dashboardsDump != null) { | ||
183 | + CassandraDbHelper.loadCf(ks, cluster.getSession(), DASHBOARD, | ||
184 | + new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump, true); | ||
185 | + DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, false); | ||
186 | + Files.deleteIfExists(dashboardsDump); | ||
187 | + } | ||
188 | + log.info("Dashboards restored."); | ||
167 | break; | 189 | break; |
168 | default: | 190 | default: |
169 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | 191 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
1 | +/** | ||
2 | + * Copyright © 2016-2017 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.install; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.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,8 +339,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
339 | JsonNode dashboardJson = objectMapper.readTree(path.toFile()); | 339 | JsonNode dashboardJson = objectMapper.readTree(path.toFile()); |
340 | Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); | 340 | Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); |
341 | dashboard.setTenantId(tenantId); | 341 | dashboard.setTenantId(tenantId); |
342 | - dashboard.setCustomerId(customerId); | ||
343 | - dashboardService.saveDashboard(dashboard); | 342 | + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); |
343 | + if (customerId != null && !customerId.isNullUid()) { | ||
344 | + dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); | ||
345 | + } | ||
344 | } catch (Exception e) { | 346 | } catch (Exception e) { |
345 | log.error("Unable to load dashboard from json: [{}]", path.toString()); | 347 | log.error("Unable to load dashboard from json: [{}]", path.toString()); |
346 | throw new RuntimeException("Unable to load dashboard from json", e); | 348 | throw new RuntimeException("Unable to load dashboard from json", e); |
@@ -17,18 +17,26 @@ | @@ -17,18 +17,26 @@ | ||
17 | package org.thingsboard.server.service.install; | 17 | package org.thingsboard.server.service.install; |
18 | 18 | ||
19 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
20 | import org.springframework.beans.factory.annotation.Value; | 21 | import org.springframework.beans.factory.annotation.Value; |
21 | import org.springframework.context.annotation.Profile; | 22 | import org.springframework.context.annotation.Profile; |
22 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
24 | +import org.thingsboard.server.dao.dashboard.DashboardService; | ||
23 | import org.thingsboard.server.dao.util.SqlDao; | 25 | import org.thingsboard.server.dao.util.SqlDao; |
26 | +import org.thingsboard.server.service.install.cql.CassandraDbHelper; | ||
27 | +import org.thingsboard.server.service.install.sql.SqlDbHelper; | ||
24 | 28 | ||
25 | import java.nio.charset.Charset; | 29 | import java.nio.charset.Charset; |
26 | import java.nio.file.Files; | 30 | import java.nio.file.Files; |
27 | import java.nio.file.Path; | 31 | import java.nio.file.Path; |
28 | import java.nio.file.Paths; | 32 | import java.nio.file.Paths; |
29 | import java.sql.Connection; | 33 | import java.sql.Connection; |
34 | +import java.sql.DatabaseMetaData; | ||
30 | import java.sql.DriverManager; | 35 | import java.sql.DriverManager; |
31 | 36 | ||
37 | +import static org.thingsboard.server.service.install.DatabaseHelper.*; | ||
38 | +import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; | ||
39 | + | ||
32 | @Service | 40 | @Service |
33 | @Profile("install") | 41 | @Profile("install") |
34 | @Slf4j | 42 | @Slf4j |
@@ -49,6 +57,9 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -49,6 +57,9 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
49 | @Value("${spring.datasource.password}") | 57 | @Value("${spring.datasource.password}") |
50 | private String dbPassword; | 58 | private String dbPassword; |
51 | 59 | ||
60 | + @Autowired | ||
61 | + private DashboardService dashboardService; | ||
62 | + | ||
52 | @Override | 63 | @Override |
53 | public void upgradeDatabase(String fromVersion) throws Exception { | 64 | public void upgradeDatabase(String fromVersion) throws Exception { |
54 | switch (fromVersion) { | 65 | switch (fromVersion) { |
@@ -62,13 +73,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -62,13 +73,30 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
62 | log.info("Schema updated."); | 73 | log.info("Schema updated."); |
63 | break; | 74 | break; |
64 | case "1.3.1": | 75 | case "1.3.1": |
65 | - log.info("Updating schema ..."); | ||
66 | - schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.4.0", SCHEMA_UPDATE_SQL); | ||
67 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 76 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
77 | + | ||
78 | + log.info("Dumping dashboards ..."); | ||
79 | + Path dashboardsDump = SqlDbHelper.dumpTableIfExists(conn, DASHBOARD, | ||
80 | + new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, | ||
81 | + new String[]{"", "", "", "", "", "", ""}, | ||
82 | + "tb-dashboards", 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 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); | 87 | String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8")); |
69 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script | 88 | conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script |
89 | + log.info("Schema updated."); | ||
90 | + | ||
91 | + log.info("Restoring dashboards ..."); | ||
92 | + if (dashboardsDump != null) { | ||
93 | + SqlDbHelper.loadTable(conn, DASHBOARD, | ||
94 | + new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump, 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 | break; | 100 | break; |
73 | default: | 101 | default: |
74 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 102 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
@@ -28,16 +28,25 @@ import java.nio.file.Path; | @@ -28,16 +28,25 @@ import java.nio.file.Path; | ||
28 | import java.nio.file.StandardCopyOption; | 28 | import java.nio.file.StandardCopyOption; |
29 | import java.util.*; | 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 | public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, | 35 | public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName, |
36 | String[] columns, String[] defaultValues, String dumpPrefix) throws Exception { | 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 | if (ks.getTable(cfName) != null) { | 42 | if (ks.getTable(cfName) != null) { |
38 | Path dumpFile = Files.createTempFile(dumpPrefix, null); | 43 | Path dumpFile = Files.createTempFile(dumpPrefix, null); |
39 | Files.deleteIfExists(dumpFile); | 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 | Statement stmt = new SimpleStatement("SELECT * FROM " + cfName); | 50 | Statement stmt = new SimpleStatement("SELECT * FROM " + cfName); |
42 | stmt.setFetchSize(1000); | 51 | stmt.setFetchSize(1000); |
43 | ResultSet rs = session.execute(stmt); | 52 | ResultSet rs = session.execute(stmt); |
@@ -75,9 +84,19 @@ public class CassandraDbHelper { | @@ -75,9 +84,19 @@ public class CassandraDbHelper { | ||
75 | } | 84 | } |
76 | 85 | ||
77 | public static void loadCf(KeyspaceMetadata ks, Session session, String cfName, String[] columns, Path sourceFile) throws Exception { | 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 | TableMetadata tableMetadata = ks.getTable(cfName); | 91 | TableMetadata tableMetadata = ks.getTable(cfName); |
79 | PreparedStatement prepared = session.prepare(createInsertStatement(cfName, columns)); | 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 | csvParser.forEach(record -> { | 100 | csvParser.forEach(record -> { |
82 | BoundStatement boundStatement = prepared.bind(); | 101 | BoundStatement boundStatement = prepared.bind(); |
83 | for (String column : columns) { | 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,6 +19,7 @@ import static org.hamcrest.Matchers.containsString; | ||
19 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; | 19 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | 20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
21 | 21 | ||
22 | +import java.sql.Time; | ||
22 | import java.util.ArrayList; | 23 | import java.util.ArrayList; |
23 | import java.util.Collections; | 24 | import java.util.Collections; |
24 | import java.util.List; | 25 | import java.util.List; |
@@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.*; | @@ -29,6 +30,8 @@ import org.thingsboard.server.common.data.*; | ||
29 | import org.thingsboard.server.common.data.id.CustomerId; | 30 | import org.thingsboard.server.common.data.id.CustomerId; |
30 | import org.thingsboard.server.common.data.page.TextPageData; | 31 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | import org.thingsboard.server.common.data.page.TextPageLink; | 32 | import org.thingsboard.server.common.data.page.TextPageLink; |
33 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
34 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
32 | import org.thingsboard.server.common.data.security.Authority; | 35 | import org.thingsboard.server.common.data.security.Authority; |
33 | import org.thingsboard.server.dao.model.ModelConstants; | 36 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | import org.junit.After; | 37 | import org.junit.After; |
@@ -82,8 +85,6 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -82,8 +85,6 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
82 | Assert.assertNotNull(savedDashboard.getId()); | 85 | Assert.assertNotNull(savedDashboard.getId()); |
83 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); | 86 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); |
84 | Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); | 87 | Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); |
85 | - Assert.assertNotNull(savedDashboard.getCustomerId()); | ||
86 | - Assert.assertEquals(NULL_UUID, savedDashboard.getCustomerId().getId()); | ||
87 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); | 88 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); |
88 | 89 | ||
89 | savedDashboard.setTitle("My new dashboard"); | 90 | savedDashboard.setTitle("My new dashboard"); |
@@ -136,17 +137,20 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -136,17 +137,20 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
136 | 137 | ||
137 | Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() | 138 | Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() |
138 | + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | 139 | + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
139 | - Assert.assertEquals(savedCustomer.getId(), assignedDashboard.getCustomerId()); | ||
140 | - | 140 | + |
141 | + Assert.assertTrue(assignedDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); | ||
142 | + | ||
141 | Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | 143 | Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
142 | - Assert.assertEquals(savedCustomer.getId(), foundDashboard.getCustomerId()); | 144 | + Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); |
143 | 145 | ||
144 | Dashboard unassignedDashboard = | 146 | Dashboard unassignedDashboard = |
145 | - doDelete("/api/customer/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | ||
146 | - Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDashboard.getCustomerId().getId()); | ||
147 | - | 147 | + doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
148 | + | ||
149 | + Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); | ||
150 | + | ||
148 | foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); | 151 | foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); |
149 | - Assert.assertEquals(ModelConstants.NULL_UUID, foundDashboard.getCustomerId().getId()); | 152 | + |
153 | + Assert.assertTrue(foundDashboard.getAssignedCustomers() == null || foundDashboard.getAssignedCustomers().isEmpty()); | ||
150 | } | 154 | } |
151 | 155 | ||
152 | @Test | 156 | @Test |
@@ -320,11 +324,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -320,11 +324,11 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
320 | } | 324 | } |
321 | 325 | ||
322 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); | 326 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); |
323 | - TextPageLink pageLink = new TextPageLink(21); | ||
324 | - TextPageData<DashboardInfo> pageData = null; | 327 | + TimePageLink pageLink = new TimePageLink(21); |
328 | + TimePageData<DashboardInfo> pageData = null; | ||
325 | do { | 329 | do { |
326 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
327 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | 330 | + pageData = doGetTypedWithTimePageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", |
331 | + new TypeReference<TimePageData<DashboardInfo>>(){}, pageLink); | ||
328 | loadedDashboards.addAll(pageData.getData()); | 332 | loadedDashboards.addAll(pageData.getData()); |
329 | if (pageData.hasNext()) { | 333 | if (pageData.hasNext()) { |
330 | pageLink = pageData.getNextPageLink(); | 334 | pageLink = pageData.getNextPageLink(); |
@@ -336,93 +340,5 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | @@ -336,93 +340,5 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest | ||
336 | 340 | ||
337 | Assert.assertEquals(dashboards, loadedDashboards); | 341 | Assert.assertEquals(dashboards, loadedDashboards); |
338 | } | 342 | } |
339 | - | ||
340 | - @Test | ||
341 | - public void testFindCustomerDashboardsByTitle() throws Exception { | ||
342 | - Customer customer = new Customer(); | ||
343 | - customer.setTitle("Test customer"); | ||
344 | - customer = doPost("/api/customer", customer, Customer.class); | ||
345 | - CustomerId customerId = customer.getId(); | ||
346 | - | ||
347 | - String title1 = "Dashboard title 1"; | ||
348 | - List<DashboardInfo> dashboardsTitle1 = new ArrayList<>(); | ||
349 | - for (int i=0;i<125;i++) { | ||
350 | - Dashboard dashboard = new Dashboard(); | ||
351 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
352 | - String title = title1+suffix; | ||
353 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
354 | - dashboard.setTitle(title); | ||
355 | - dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); | ||
356 | - dashboardsTitle1.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() | ||
357 | - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); | ||
358 | - } | ||
359 | - String title2 = "Dashboard title 2"; | ||
360 | - List<DashboardInfo> dashboardsTitle2 = new ArrayList<>(); | ||
361 | - for (int i=0;i<143;i++) { | ||
362 | - Dashboard dashboard = new Dashboard(); | ||
363 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
364 | - String title = title2+suffix; | ||
365 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
366 | - dashboard.setTitle(title); | ||
367 | - dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); | ||
368 | - dashboardsTitle2.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() | ||
369 | - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); | ||
370 | - } | ||
371 | - | ||
372 | - List<DashboardInfo> loadedDashboardsTitle1 = new ArrayList<>(); | ||
373 | - TextPageLink pageLink = new TextPageLink(18, title1); | ||
374 | - TextPageData<DashboardInfo> pageData = null; | ||
375 | - do { | ||
376 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
377 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
378 | - loadedDashboardsTitle1.addAll(pageData.getData()); | ||
379 | - if (pageData.hasNext()) { | ||
380 | - pageLink = pageData.getNextPageLink(); | ||
381 | - } | ||
382 | - } while (pageData.hasNext()); | ||
383 | - | ||
384 | - Collections.sort(dashboardsTitle1, idComparator); | ||
385 | - Collections.sort(loadedDashboardsTitle1, idComparator); | ||
386 | - | ||
387 | - Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); | ||
388 | - | ||
389 | - List<DashboardInfo> loadedDashboardsTitle2 = new ArrayList<>(); | ||
390 | - pageLink = new TextPageLink(7, title2); | ||
391 | - do { | ||
392 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
393 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
394 | - loadedDashboardsTitle2.addAll(pageData.getData()); | ||
395 | - if (pageData.hasNext()) { | ||
396 | - pageLink = pageData.getNextPageLink(); | ||
397 | - } | ||
398 | - } while (pageData.hasNext()); | ||
399 | - | ||
400 | - Collections.sort(dashboardsTitle2, idComparator); | ||
401 | - Collections.sort(loadedDashboardsTitle2, idComparator); | ||
402 | - | ||
403 | - Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); | ||
404 | - | ||
405 | - for (DashboardInfo dashboard : loadedDashboardsTitle1) { | ||
406 | - doDelete("/api/customer/dashboard/" + dashboard.getId().getId().toString()) | ||
407 | - .andExpect(status().isOk()); | ||
408 | - } | ||
409 | - | ||
410 | - pageLink = new TextPageLink(5, title1); | ||
411 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
412 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
413 | - Assert.assertFalse(pageData.hasNext()); | ||
414 | - Assert.assertEquals(0, pageData.getData().size()); | ||
415 | - | ||
416 | - for (DashboardInfo dashboard : loadedDashboardsTitle2) { | ||
417 | - doDelete("/api/customer/dashboard/" + dashboard.getId().getId().toString()) | ||
418 | - .andExpect(status().isOk()); | ||
419 | - } | ||
420 | - | ||
421 | - pageLink = new TextPageLink(9, title2); | ||
422 | - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", | ||
423 | - new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink); | ||
424 | - Assert.assertFalse(pageData.hasNext()); | ||
425 | - Assert.assertEquals(0, pageData.getData().size()); | ||
426 | - } | ||
427 | 343 | ||
428 | } | 344 | } |
@@ -69,6 +69,11 @@ public class Customer extends ContactBased<CustomerId> implements HasName { | @@ -69,6 +69,11 @@ public class Customer extends ContactBased<CustomerId> implements HasName { | ||
69 | return false; | 69 | return false; |
70 | } | 70 | } |
71 | 71 | ||
72 | + @JsonIgnore | ||
73 | + public ShortCustomerInfo toShortCustomerInfo() { | ||
74 | + return new ShortCustomerInfo(id, title, isPublic()); | ||
75 | + } | ||
76 | + | ||
72 | @Override | 77 | @Override |
73 | @JsonProperty(access = Access.READ_ONLY) | 78 | @JsonProperty(access = Access.READ_ONLY) |
74 | public String getName() { | 79 | public String getName() { |
@@ -79,8 +79,6 @@ public class Dashboard extends DashboardInfo { | @@ -79,8 +79,6 @@ public class Dashboard extends DashboardInfo { | ||
79 | StringBuilder builder = new StringBuilder(); | 79 | StringBuilder builder = new StringBuilder(); |
80 | builder.append("Dashboard [tenantId="); | 80 | builder.append("Dashboard [tenantId="); |
81 | builder.append(getTenantId()); | 81 | builder.append(getTenantId()); |
82 | - builder.append(", customerId="); | ||
83 | - builder.append(getCustomerId()); | ||
84 | builder.append(", title="); | 82 | builder.append(", title="); |
85 | builder.append(getTitle()); | 83 | builder.append(getTitle()); |
86 | builder.append(", configuration="); | 84 | builder.append(", configuration="); |
@@ -20,11 +20,13 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -20,11 +20,13 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
20 | import org.thingsboard.server.common.data.id.DashboardId; | 20 | import org.thingsboard.server.common.data.id.DashboardId; |
21 | import org.thingsboard.server.common.data.id.TenantId; | 21 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 22 | ||
23 | +import java.util.*; | ||
24 | + | ||
23 | public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { | 25 | public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName { |
24 | 26 | ||
25 | private TenantId tenantId; | 27 | private TenantId tenantId; |
26 | - private CustomerId customerId; | ||
27 | private String title; | 28 | private String title; |
29 | + private Set<ShortCustomerInfo> assignedCustomers; | ||
28 | 30 | ||
29 | public DashboardInfo() { | 31 | public DashboardInfo() { |
30 | super(); | 32 | super(); |
@@ -37,8 +39,8 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -37,8 +39,8 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
37 | public DashboardInfo(DashboardInfo dashboardInfo) { | 39 | public DashboardInfo(DashboardInfo dashboardInfo) { |
38 | super(dashboardInfo); | 40 | super(dashboardInfo); |
39 | this.tenantId = dashboardInfo.getTenantId(); | 41 | this.tenantId = dashboardInfo.getTenantId(); |
40 | - this.customerId = dashboardInfo.getCustomerId(); | ||
41 | this.title = dashboardInfo.getTitle(); | 42 | this.title = dashboardInfo.getTitle(); |
43 | + this.assignedCustomers = dashboardInfo.getAssignedCustomers(); | ||
42 | } | 44 | } |
43 | 45 | ||
44 | public TenantId getTenantId() { | 46 | public TenantId getTenantId() { |
@@ -49,14 +51,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -49,14 +51,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
49 | this.tenantId = tenantId; | 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 | public String getTitle() { | 54 | public String getTitle() { |
61 | return title; | 55 | return title; |
62 | } | 56 | } |
@@ -65,6 +59,62 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -65,6 +59,62 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
65 | this.title = title; | 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 | @Override | 118 | @Override |
69 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) | 119 | @JsonProperty(access = JsonProperty.Access.READ_ONLY) |
70 | public String getName() { | 120 | public String getName() { |
@@ -80,7 +130,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -80,7 +130,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
80 | public int hashCode() { | 130 | public int hashCode() { |
81 | final int prime = 31; | 131 | final int prime = 31; |
82 | int result = super.hashCode(); | 132 | int result = super.hashCode(); |
83 | - result = prime * result + ((customerId == null) ? 0 : customerId.hashCode()); | ||
84 | result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); | 133 | result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); |
85 | result = prime * result + ((title == null) ? 0 : title.hashCode()); | 134 | result = prime * result + ((title == null) ? 0 : title.hashCode()); |
86 | return result; | 135 | return result; |
@@ -95,11 +144,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -95,11 +144,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
95 | if (getClass() != obj.getClass()) | 144 | if (getClass() != obj.getClass()) |
96 | return false; | 145 | return false; |
97 | DashboardInfo other = (DashboardInfo) obj; | 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 | if (tenantId == null) { | 147 | if (tenantId == null) { |
104 | if (other.tenantId != null) | 148 | if (other.tenantId != null) |
105 | return false; | 149 | return false; |
@@ -118,8 +162,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | @@ -118,8 +162,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa | ||
118 | StringBuilder builder = new StringBuilder(); | 162 | StringBuilder builder = new StringBuilder(); |
119 | builder.append("DashboardInfo [tenantId="); | 163 | builder.append("DashboardInfo [tenantId="); |
120 | builder.append(tenantId); | 164 | builder.append(tenantId); |
121 | - builder.append(", customerId="); | ||
122 | - builder.append(customerId); | ||
123 | builder.append(", title="); | 165 | builder.append(", title="); |
124 | builder.append(title); | 166 | builder.append(title); |
125 | builder.append("]"); | 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,7 +97,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom | ||
97 | public Customer saveCustomer(Customer customer) { | 97 | public Customer saveCustomer(Customer customer) { |
98 | log.trace("Executing saveCustomer [{}]", customer); | 98 | log.trace("Executing saveCustomer [{}]", customer); |
99 | customerValidator.validate(customer); | 99 | customerValidator.validate(customer); |
100 | - return customerDao.save(customer); | 100 | + Customer savedCustomer = customerDao.save(customer); |
101 | + dashboardService.updateCustomerDashboards(savedCustomer.getId()); | ||
102 | + return savedCustomer; | ||
101 | } | 103 | } |
102 | 104 | ||
103 | @Override | 105 | @Override |
@@ -108,7 +110,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom | @@ -108,7 +110,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom | ||
108 | if (customer == null) { | 110 | if (customer == null) { |
109 | throw new IncorrectParameterException("Unable to delete non-existent customer."); | 111 | throw new IncorrectParameterException("Unable to delete non-existent customer."); |
110 | } | 112 | } |
111 | - dashboardService.unassignCustomerDashboards(customer.getTenantId(), customerId); | 113 | + dashboardService.unassignCustomerDashboards(customerId); |
112 | assetService.unassignCustomerAssets(customer.getTenantId(), customerId); | 114 | assetService.unassignCustomerAssets(customer.getTenantId(), customerId); |
113 | deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); | 115 | deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); |
114 | userService.deleteCustomerUsers(customer.getTenantId(), customerId); | 116 | userService.deleteCustomerUsers(customer.getTenantId(), customerId); |
@@ -15,16 +15,26 @@ | @@ -15,16 +15,26 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.dashboard; | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.AsyncFunction; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
20 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
22 | +import org.springframework.beans.factory.annotation.Autowired; | ||
19 | import org.springframework.stereotype.Component; | 23 | import org.springframework.stereotype.Component; |
20 | import org.thingsboard.server.common.data.DashboardInfo; | 24 | import org.thingsboard.server.common.data.DashboardInfo; |
25 | +import org.thingsboard.server.common.data.EntityType; | ||
26 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
21 | import org.thingsboard.server.common.data.page.TextPageLink; | 27 | import org.thingsboard.server.common.data.page.TextPageLink; |
28 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
29 | +import org.thingsboard.server.common.data.relation.EntityRelation; | ||
30 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | ||
22 | import org.thingsboard.server.dao.DaoUtil; | 31 | import org.thingsboard.server.dao.DaoUtil; |
23 | import org.thingsboard.server.dao.model.nosql.DashboardInfoEntity; | 32 | import org.thingsboard.server.dao.model.nosql.DashboardInfoEntity; |
24 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; | 33 | import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; |
34 | +import org.thingsboard.server.dao.relation.RelationDao; | ||
25 | import org.thingsboard.server.dao.util.NoSqlDao; | 35 | import org.thingsboard.server.dao.util.NoSqlDao; |
26 | 36 | ||
27 | -import java.util.Arrays; | 37 | +import java.util.ArrayList; |
28 | import java.util.Collections; | 38 | import java.util.Collections; |
29 | import java.util.List; | 39 | import java.util.List; |
30 | import java.util.UUID; | 40 | import java.util.UUID; |
@@ -37,6 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | @@ -37,6 +47,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | ||
37 | @NoSqlDao | 47 | @NoSqlDao |
38 | public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { | 48 | public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { |
39 | 49 | ||
50 | + @Autowired | ||
51 | + private RelationDao relationDao; | ||
52 | + | ||
40 | @Override | 53 | @Override |
41 | protected Class<DashboardInfoEntity> getColumnFamilyClass() { | 54 | protected Class<DashboardInfoEntity> getColumnFamilyClass() { |
42 | return DashboardInfoEntity.class; | 55 | return DashboardInfoEntity.class; |
@@ -59,15 +72,18 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da | @@ -59,15 +72,18 @@ public class CassandraDashboardInfoDao extends CassandraAbstractSearchTextDao<Da | ||
59 | } | 72 | } |
60 | 73 | ||
61 | @Override | 74 | @Override |
62 | - public List<DashboardInfo> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { | 75 | + public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { |
63 | log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); | 76 | log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); |
64 | - List<DashboardInfoEntity> dashboardEntities = findPageWithTextSearch(DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | ||
65 | - Arrays.asList(eq(DASHBOARD_CUSTOMER_ID_PROPERTY, customerId), | ||
66 | - eq(DASHBOARD_TENANT_ID_PROPERTY, tenantId)), | ||
67 | - pageLink); | ||
68 | 77 | ||
69 | - log.trace("Found dashboards [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", dashboardEntities, tenantId, customerId, pageLink); | ||
70 | - return DaoUtil.convertDataList(dashboardEntities); | 78 | + ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); |
79 | + | ||
80 | + return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<DashboardInfo>>) input -> { | ||
81 | + List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); | ||
82 | + for (EntityRelation relation : input) { | ||
83 | + dashboardFutures.add(findByIdAsync(relation.getTo().getId())); | ||
84 | + } | ||
85 | + return Futures.successfulAsList(dashboardFutures); | ||
86 | + }); | ||
71 | } | 87 | } |
72 | 88 | ||
73 | } | 89 | } |
@@ -15,8 +15,10 @@ | @@ -15,8 +15,10 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.dashboard; | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
18 | import org.thingsboard.server.common.data.DashboardInfo; | 19 | import org.thingsboard.server.common.data.DashboardInfo; |
19 | import org.thingsboard.server.common.data.page.TextPageLink; | 20 | import org.thingsboard.server.common.data.page.TextPageLink; |
21 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
20 | import org.thingsboard.server.dao.Dao; | 22 | import org.thingsboard.server.dao.Dao; |
21 | 23 | ||
22 | import java.util.List; | 24 | import java.util.List; |
@@ -44,6 +46,6 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> { | @@ -44,6 +46,6 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> { | ||
44 | * @param pageLink the page link | 46 | * @param pageLink the page link |
45 | * @return the list of dashboard objects | 47 | * @return the list of dashboard objects |
46 | */ | 48 | */ |
47 | - List<DashboardInfo> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink); | 49 | + ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink); |
48 | 50 | ||
49 | } | 51 | } |
@@ -23,6 +23,10 @@ import org.thingsboard.server.common.data.id.DashboardId; | @@ -23,6 +23,10 @@ import org.thingsboard.server.common.data.id.DashboardId; | ||
23 | import org.thingsboard.server.common.data.id.TenantId; | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | import org.thingsboard.server.common.data.page.TextPageData; | 24 | import org.thingsboard.server.common.data.page.TextPageData; |
25 | import org.thingsboard.server.common.data.page.TextPageLink; | 25 | import org.thingsboard.server.common.data.page.TextPageLink; |
26 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
27 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
28 | + | ||
29 | +import java.util.Set; | ||
26 | 30 | ||
27 | public interface DashboardService { | 31 | public interface DashboardService { |
28 | 32 | ||
@@ -38,7 +42,7 @@ public interface DashboardService { | @@ -38,7 +42,7 @@ public interface DashboardService { | ||
38 | 42 | ||
39 | Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); | 43 | Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId); |
40 | 44 | ||
41 | - Dashboard unassignDashboardFromCustomer(DashboardId dashboardId); | 45 | + Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId); |
42 | 46 | ||
43 | void deleteDashboard(DashboardId dashboardId); | 47 | void deleteDashboard(DashboardId dashboardId); |
44 | 48 | ||
@@ -46,8 +50,10 @@ public interface DashboardService { | @@ -46,8 +50,10 @@ public interface DashboardService { | ||
46 | 50 | ||
47 | void deleteDashboardsByTenantId(TenantId tenantId); | 51 | void deleteDashboardsByTenantId(TenantId tenantId); |
48 | 52 | ||
49 | - TextPageData<DashboardInfo> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); | 53 | + ListenableFuture<TimePageData<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TimePageLink pageLink); |
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,30 +15,42 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.dashboard; | 16 | package org.thingsboard.server.dao.dashboard; |
17 | 17 | ||
18 | +import com.google.common.base.Function; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 20 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
20 | import org.apache.commons.lang3.StringUtils; | 22 | import org.apache.commons.lang3.StringUtils; |
21 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
22 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
23 | -import org.thingsboard.server.common.data.Customer; | ||
24 | -import org.thingsboard.server.common.data.Dashboard; | ||
25 | -import org.thingsboard.server.common.data.DashboardInfo; | ||
26 | -import org.thingsboard.server.common.data.Tenant; | 25 | +import org.thingsboard.server.common.data.*; |
26 | +import org.thingsboard.server.common.data.alarm.AlarmInfo; | ||
27 | import org.thingsboard.server.common.data.id.CustomerId; | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
28 | import org.thingsboard.server.common.data.id.DashboardId; | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
29 | import org.thingsboard.server.common.data.id.TenantId; | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | import org.thingsboard.server.common.data.page.TextPageData; | 30 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | import org.thingsboard.server.common.data.page.TextPageLink; | 31 | import org.thingsboard.server.common.data.page.TextPageLink; |
32 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
33 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
34 | +import org.thingsboard.server.common.data.relation.EntityRelation; | ||
35 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | ||
32 | import org.thingsboard.server.dao.customer.CustomerDao; | 36 | import org.thingsboard.server.dao.customer.CustomerDao; |
33 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 37 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
34 | import org.thingsboard.server.dao.exception.DataValidationException; | 38 | import org.thingsboard.server.dao.exception.DataValidationException; |
35 | import org.thingsboard.server.dao.model.ModelConstants; | 39 | import org.thingsboard.server.dao.model.ModelConstants; |
40 | +import org.thingsboard.server.dao.relation.RelationDao; | ||
36 | import org.thingsboard.server.dao.service.DataValidator; | 41 | import org.thingsboard.server.dao.service.DataValidator; |
37 | import org.thingsboard.server.dao.service.PaginatedRemover; | 42 | import org.thingsboard.server.dao.service.PaginatedRemover; |
43 | +import org.thingsboard.server.dao.service.TimePaginatedRemover; | ||
38 | import org.thingsboard.server.dao.service.Validator; | 44 | import org.thingsboard.server.dao.service.Validator; |
39 | import org.thingsboard.server.dao.tenant.TenantDao; | 45 | import org.thingsboard.server.dao.tenant.TenantDao; |
40 | 46 | ||
47 | +import javax.annotation.Nullable; | ||
48 | +import java.sql.Time; | ||
49 | +import java.util.ArrayList; | ||
50 | +import java.util.HashSet; | ||
41 | import java.util.List; | 51 | import java.util.List; |
52 | +import java.util.Set; | ||
53 | +import java.util.concurrent.ExecutionException; | ||
42 | 54 | ||
43 | import static org.thingsboard.server.dao.service.Validator.validateId; | 55 | import static org.thingsboard.server.dao.service.Validator.validateId; |
44 | 56 | ||
@@ -59,7 +71,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -59,7 +71,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
59 | 71 | ||
60 | @Autowired | 72 | @Autowired |
61 | private CustomerDao customerDao; | 73 | private CustomerDao customerDao; |
62 | - | 74 | + |
63 | @Override | 75 | @Override |
64 | public Dashboard findDashboardById(DashboardId dashboardId) { | 76 | public Dashboard findDashboardById(DashboardId dashboardId) { |
65 | log.trace("Executing findDashboardById [{}]", dashboardId); | 77 | log.trace("Executing findDashboardById [{}]", dashboardId); |
@@ -98,15 +110,63 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -98,15 +110,63 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
98 | @Override | 110 | @Override |
99 | public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { | 111 | public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId) { |
100 | Dashboard dashboard = findDashboardById(dashboardId); | 112 | Dashboard dashboard = findDashboardById(dashboardId); |
101 | - dashboard.setCustomerId(customerId); | ||
102 | - return saveDashboard(dashboard); | 113 | + Customer customer = customerDao.findById(customerId.getId()); |
114 | + if (customer == null) { | ||
115 | + throw new DataValidationException("Can't assign dashboard to non-existent customer!"); | ||
116 | + } | ||
117 | + if (!customer.getTenantId().getId().equals(dashboard.getTenantId().getId())) { | ||
118 | + throw new DataValidationException("Can't assign dashboard to customer from different tenant!"); | ||
119 | + } | ||
120 | + if (dashboard.addAssignedCustomer(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 | @Override | 133 | @Override |
106 | - public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId) { | 134 | + public Dashboard unassignDashboardFromCustomer(DashboardId dashboardId, CustomerId customerId) { |
107 | Dashboard dashboard = findDashboardById(dashboardId); | 135 | Dashboard dashboard = findDashboardById(dashboardId); |
108 | - dashboard.setCustomerId(null); | ||
109 | - return saveDashboard(dashboard); | 136 | + 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 | @Override | 172 | @Override |
@@ -134,23 +194,44 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -134,23 +194,44 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
134 | } | 194 | } |
135 | 195 | ||
136 | @Override | 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 | log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); | 198 | log.trace("Executing findDashboardsByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink); |
139 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | 199 | Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
140 | Validator.validateId(customerId, "Incorrect customerId " + customerId); | 200 | Validator.validateId(customerId, "Incorrect customerId " + customerId); |
141 | Validator.validatePageLink(pageLink, "Incorrect page link " + pageLink); | 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 | @Override | 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 | Validator.validateId(customerId, "Incorrect customerId " + customerId); | 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 | private DataValidator<Dashboard> dashboardValidator = | 235 | private DataValidator<Dashboard> dashboardValidator = |
155 | new DataValidator<Dashboard>() { | 236 | new DataValidator<Dashboard>() { |
156 | @Override | 237 | @Override |
@@ -166,17 +247,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | @@ -166,17 +247,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb | ||
166 | throw new DataValidationException("Dashboard is referencing to non-existent tenant!"); | 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,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 | @Override | 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 | @Override | 285 | @Override |
211 | protected void removeEntity(DashboardInfo entity) { | 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,13 +266,11 @@ public class ModelConstants { | ||
266 | */ | 266 | */ |
267 | public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; | 267 | public static final String DASHBOARD_COLUMN_FAMILY_NAME = "dashboard"; |
268 | public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; | 268 | public static final String DASHBOARD_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; |
269 | - public static final String DASHBOARD_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; | ||
270 | public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; | 269 | public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY; |
271 | public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; | 270 | public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration"; |
271 | + public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers"; | ||
272 | 272 | ||
273 | public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; | 273 | public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text"; |
274 | - public static final String DASHBOARD_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_customer_and_search_text"; | ||
275 | - | ||
276 | 274 | ||
277 | /** | 275 | /** |
278 | * Cassandra plugin metadata constants. | 276 | * Cassandra plugin metadata constants. |
@@ -19,16 +19,23 @@ import com.datastax.driver.core.utils.UUIDs; | @@ -19,16 +19,23 @@ import com.datastax.driver.core.utils.UUIDs; | ||
19 | import com.datastax.driver.mapping.annotations.Column; | 19 | import com.datastax.driver.mapping.annotations.Column; |
20 | import com.datastax.driver.mapping.annotations.PartitionKey; | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | import com.datastax.driver.mapping.annotations.Table; | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
23 | +import com.fasterxml.jackson.databind.JavaType; | ||
22 | import com.fasterxml.jackson.databind.JsonNode; | 24 | import com.fasterxml.jackson.databind.JsonNode; |
25 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
23 | import lombok.EqualsAndHashCode; | 26 | import lombok.EqualsAndHashCode; |
24 | import lombok.ToString; | 27 | import lombok.ToString; |
28 | +import lombok.extern.slf4j.Slf4j; | ||
29 | +import org.springframework.util.StringUtils; | ||
25 | import org.thingsboard.server.common.data.Dashboard; | 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 | import org.thingsboard.server.common.data.id.DashboardId; | 32 | import org.thingsboard.server.common.data.id.DashboardId; |
28 | import org.thingsboard.server.common.data.id.TenantId; | 33 | import org.thingsboard.server.common.data.id.TenantId; |
29 | import org.thingsboard.server.dao.model.SearchTextEntity; | 34 | import org.thingsboard.server.dao.model.SearchTextEntity; |
30 | import org.thingsboard.server.dao.model.type.JsonCodec; | 35 | import org.thingsboard.server.dao.model.type.JsonCodec; |
31 | 36 | ||
37 | +import java.io.IOException; | ||
38 | +import java.util.HashSet; | ||
32 | import java.util.UUID; | 39 | import java.util.UUID; |
33 | 40 | ||
34 | import static org.thingsboard.server.dao.model.ModelConstants.*; | 41 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
@@ -36,8 +43,13 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | @@ -36,8 +43,13 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | ||
36 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) | 43 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) |
37 | @EqualsAndHashCode | 44 | @EqualsAndHashCode |
38 | @ToString | 45 | @ToString |
46 | +@Slf4j | ||
39 | public final class DashboardEntity implements SearchTextEntity<Dashboard> { | 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 | @PartitionKey(value = 0) | 53 | @PartitionKey(value = 0) |
42 | @Column(name = ID_PROPERTY) | 54 | @Column(name = ID_PROPERTY) |
43 | private UUID id; | 55 | private UUID id; |
@@ -46,16 +58,15 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -46,16 +58,15 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
46 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) | 58 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) |
47 | private UUID tenantId; | 59 | private UUID tenantId; |
48 | 60 | ||
49 | - @PartitionKey(value = 2) | ||
50 | - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
51 | - private UUID customerId; | ||
52 | - | ||
53 | @Column(name = DASHBOARD_TITLE_PROPERTY) | 61 | @Column(name = DASHBOARD_TITLE_PROPERTY) |
54 | private String title; | 62 | private String title; |
55 | 63 | ||
56 | @Column(name = SEARCH_TEXT_PROPERTY) | 64 | @Column(name = SEARCH_TEXT_PROPERTY) |
57 | private String searchText; | 65 | private String searchText; |
58 | - | 66 | + |
67 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | ||
68 | + private String assignedCustomers; | ||
69 | + | ||
59 | @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) | 70 | @Column(name = DASHBOARD_CONFIGURATION_PROPERTY, codec = JsonCodec.class) |
60 | private JsonNode configuration; | 71 | private JsonNode configuration; |
61 | 72 | ||
@@ -70,10 +81,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -70,10 +81,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
70 | if (dashboard.getTenantId() != null) { | 81 | if (dashboard.getTenantId() != null) { |
71 | this.tenantId = dashboard.getTenantId().getId(); | 82 | this.tenantId = dashboard.getTenantId().getId(); |
72 | } | 83 | } |
73 | - if (dashboard.getCustomerId() != null) { | ||
74 | - this.customerId = dashboard.getCustomerId().getId(); | ||
75 | - } | ||
76 | this.title = dashboard.getTitle(); | 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 | this.configuration = dashboard.getConfiguration(); | 92 | this.configuration = dashboard.getConfiguration(); |
78 | } | 93 | } |
79 | 94 | ||
@@ -93,14 +108,6 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -93,14 +108,6 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
93 | this.tenantId = tenantId; | 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 | public String getTitle() { | 111 | public String getTitle() { |
105 | return title; | 112 | return title; |
106 | } | 113 | } |
@@ -109,6 +116,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -109,6 +116,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
109 | this.title = title; | 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 | public JsonNode getConfiguration() { | 127 | public JsonNode getConfiguration() { |
113 | return configuration; | 128 | return configuration; |
114 | } | 129 | } |
@@ -138,10 +153,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | @@ -138,10 +153,14 @@ public final class DashboardEntity implements SearchTextEntity<Dashboard> { | ||
138 | if (tenantId != null) { | 153 | if (tenantId != null) { |
139 | dashboard.setTenantId(new TenantId(tenantId)); | 154 | dashboard.setTenantId(new TenantId(tenantId)); |
140 | } | 155 | } |
141 | - if (customerId != null) { | ||
142 | - dashboard.setCustomerId(new CustomerId(customerId)); | ||
143 | - } | ||
144 | dashboard.setTitle(title); | 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 | dashboard.setConfiguration(configuration); | 164 | dashboard.setConfiguration(configuration); |
146 | return dashboard; | 165 | return dashboard; |
147 | } | 166 | } |
@@ -19,14 +19,21 @@ import com.datastax.driver.core.utils.UUIDs; | @@ -19,14 +19,21 @@ import com.datastax.driver.core.utils.UUIDs; | ||
19 | import com.datastax.driver.mapping.annotations.Column; | 19 | import com.datastax.driver.mapping.annotations.Column; |
20 | import com.datastax.driver.mapping.annotations.PartitionKey; | 20 | import com.datastax.driver.mapping.annotations.PartitionKey; |
21 | import com.datastax.driver.mapping.annotations.Table; | 21 | import com.datastax.driver.mapping.annotations.Table; |
22 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
23 | +import com.fasterxml.jackson.databind.JavaType; | ||
24 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
22 | import lombok.EqualsAndHashCode; | 25 | import lombok.EqualsAndHashCode; |
23 | import lombok.ToString; | 26 | import lombok.ToString; |
27 | +import lombok.extern.slf4j.Slf4j; | ||
28 | +import org.springframework.util.StringUtils; | ||
24 | import org.thingsboard.server.common.data.DashboardInfo; | 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 | import org.thingsboard.server.common.data.id.DashboardId; | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
27 | import org.thingsboard.server.common.data.id.TenantId; | 32 | import org.thingsboard.server.common.data.id.TenantId; |
28 | import org.thingsboard.server.dao.model.SearchTextEntity; | 33 | import org.thingsboard.server.dao.model.SearchTextEntity; |
29 | 34 | ||
35 | +import java.io.IOException; | ||
36 | +import java.util.HashSet; | ||
30 | import java.util.UUID; | 37 | import java.util.UUID; |
31 | 38 | ||
32 | import static org.thingsboard.server.dao.model.ModelConstants.*; | 39 | import static org.thingsboard.server.dao.model.ModelConstants.*; |
@@ -34,8 +41,13 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | @@ -34,8 +41,13 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; | ||
34 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) | 41 | @Table(name = DASHBOARD_COLUMN_FAMILY_NAME) |
35 | @EqualsAndHashCode | 42 | @EqualsAndHashCode |
36 | @ToString | 43 | @ToString |
44 | +@Slf4j | ||
37 | public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | 45 | public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { |
38 | 46 | ||
47 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
48 | + private static final JavaType assignedCustomersType = | ||
49 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | ||
50 | + | ||
39 | @PartitionKey(value = 0) | 51 | @PartitionKey(value = 0) |
40 | @Column(name = ID_PROPERTY) | 52 | @Column(name = ID_PROPERTY) |
41 | private UUID id; | 53 | private UUID id; |
@@ -44,16 +56,15 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -44,16 +56,15 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
44 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) | 56 | @Column(name = DASHBOARD_TENANT_ID_PROPERTY) |
45 | private UUID tenantId; | 57 | private UUID tenantId; |
46 | 58 | ||
47 | - @PartitionKey(value = 2) | ||
48 | - @Column(name = DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
49 | - private UUID customerId; | ||
50 | - | ||
51 | @Column(name = DASHBOARD_TITLE_PROPERTY) | 59 | @Column(name = DASHBOARD_TITLE_PROPERTY) |
52 | private String title; | 60 | private String title; |
53 | 61 | ||
54 | @Column(name = SEARCH_TEXT_PROPERTY) | 62 | @Column(name = SEARCH_TEXT_PROPERTY) |
55 | private String searchText; | 63 | private String searchText; |
56 | 64 | ||
65 | + @Column(name = DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | ||
66 | + private String assignedCustomers; | ||
67 | + | ||
57 | public DashboardInfoEntity() { | 68 | public DashboardInfoEntity() { |
58 | super(); | 69 | super(); |
59 | } | 70 | } |
@@ -65,10 +76,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -65,10 +76,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
65 | if (dashboardInfo.getTenantId() != null) { | 76 | if (dashboardInfo.getTenantId() != null) { |
66 | this.tenantId = dashboardInfo.getTenantId().getId(); | 77 | this.tenantId = dashboardInfo.getTenantId().getId(); |
67 | } | 78 | } |
68 | - if (dashboardInfo.getCustomerId() != null) { | ||
69 | - this.customerId = dashboardInfo.getCustomerId().getId(); | ||
70 | - } | ||
71 | this.title = dashboardInfo.getTitle(); | 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 | public UUID getId() { | 89 | public UUID getId() { |
@@ -87,14 +102,6 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -87,14 +102,6 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
87 | this.tenantId = tenantId; | 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 | public String getTitle() { | 105 | public String getTitle() { |
99 | return title; | 106 | return title; |
100 | } | 107 | } |
@@ -103,6 +110,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -103,6 +110,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
103 | this.title = title; | 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 | @Override | 121 | @Override |
107 | public String getSearchTextSource() { | 122 | public String getSearchTextSource() { |
108 | return getTitle(); | 123 | return getTitle(); |
@@ -124,10 +139,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | @@ -124,10 +139,14 @@ public class DashboardInfoEntity implements SearchTextEntity<DashboardInfo> { | ||
124 | if (tenantId != null) { | 139 | if (tenantId != null) { |
125 | dashboardInfo.setTenantId(new TenantId(tenantId)); | 140 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
126 | } | 141 | } |
127 | - if (customerId != null) { | ||
128 | - dashboardInfo.setCustomerId(new CustomerId(customerId)); | ||
129 | - } | ||
130 | dashboardInfo.setTitle(title); | 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 | return dashboardInfo; | 150 | return dashboardInfo; |
132 | } | 151 | } |
133 | 152 |
@@ -16,13 +16,18 @@ | @@ -16,13 +16,18 @@ | ||
16 | package org.thingsboard.server.dao.model.sql; | 16 | package org.thingsboard.server.dao.model.sql; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
20 | +import com.fasterxml.jackson.databind.JavaType; | ||
19 | import com.fasterxml.jackson.databind.JsonNode; | 21 | import com.fasterxml.jackson.databind.JsonNode; |
22 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
20 | import lombok.Data; | 23 | import lombok.Data; |
21 | import lombok.EqualsAndHashCode; | 24 | import lombok.EqualsAndHashCode; |
25 | +import lombok.extern.slf4j.Slf4j; | ||
22 | import org.hibernate.annotations.Type; | 26 | import org.hibernate.annotations.Type; |
23 | import org.hibernate.annotations.TypeDef; | 27 | import org.hibernate.annotations.TypeDef; |
28 | +import org.springframework.util.StringUtils; | ||
24 | import org.thingsboard.server.common.data.Dashboard; | 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 | import org.thingsboard.server.common.data.id.DashboardId; | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
27 | import org.thingsboard.server.common.data.id.TenantId; | 32 | import org.thingsboard.server.common.data.id.TenantId; |
28 | import org.thingsboard.server.dao.model.BaseSqlEntity; | 33 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
@@ -33,26 +38,33 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; | @@ -33,26 +38,33 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; | ||
33 | import javax.persistence.Column; | 38 | import javax.persistence.Column; |
34 | import javax.persistence.Entity; | 39 | import javax.persistence.Entity; |
35 | import javax.persistence.Table; | 40 | import javax.persistence.Table; |
41 | +import java.io.IOException; | ||
42 | +import java.util.HashSet; | ||
36 | 43 | ||
37 | @Data | 44 | @Data |
45 | +@Slf4j | ||
38 | @EqualsAndHashCode(callSuper = true) | 46 | @EqualsAndHashCode(callSuper = true) |
39 | @Entity | 47 | @Entity |
40 | @TypeDef(name = "json", typeClass = JsonStringType.class) | 48 | @TypeDef(name = "json", typeClass = JsonStringType.class) |
41 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) | 49 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) |
42 | public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements SearchTextEntity<Dashboard> { | 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 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) | 56 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
45 | private String tenantId; | 57 | private String tenantId; |
46 | 58 | ||
47 | - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
48 | - private String customerId; | ||
49 | - | ||
50 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) | 59 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) |
51 | private String title; | 60 | private String title; |
52 | 61 | ||
53 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) | 62 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
54 | private String searchText; | 63 | private String searchText; |
55 | 64 | ||
65 | + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | ||
66 | + private String assignedCustomers; | ||
67 | + | ||
56 | @Type(type = "json") | 68 | @Type(type = "json") |
57 | @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) | 69 | @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) |
58 | private JsonNode configuration; | 70 | private JsonNode configuration; |
@@ -68,10 +80,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | @@ -68,10 +80,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | ||
68 | if (dashboard.getTenantId() != null) { | 80 | if (dashboard.getTenantId() != null) { |
69 | this.tenantId = toString(dashboard.getTenantId().getId()); | 81 | this.tenantId = toString(dashboard.getTenantId().getId()); |
70 | } | 82 | } |
71 | - if (dashboard.getCustomerId() != null) { | ||
72 | - this.customerId = toString(dashboard.getCustomerId().getId()); | ||
73 | - } | ||
74 | this.title = dashboard.getTitle(); | 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 | this.configuration = dashboard.getConfiguration(); | 91 | this.configuration = dashboard.getConfiguration(); |
76 | } | 92 | } |
77 | 93 | ||
@@ -92,10 +108,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | @@ -92,10 +108,14 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S | ||
92 | if (tenantId != null) { | 108 | if (tenantId != null) { |
93 | dashboard.setTenantId(new TenantId(toUUID(tenantId))); | 109 | dashboard.setTenantId(new TenantId(toUUID(tenantId))); |
94 | } | 110 | } |
95 | - if (customerId != null) { | ||
96 | - dashboard.setCustomerId(new CustomerId(toUUID(customerId))); | ||
97 | - } | ||
98 | dashboard.setTitle(title); | 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 | dashboard.setConfiguration(configuration); | 119 | dashboard.setConfiguration(configuration); |
100 | return dashboard; | 120 | return dashboard; |
101 | } | 121 | } |
@@ -16,10 +16,15 @@ | @@ -16,10 +16,15 @@ | ||
16 | package org.thingsboard.server.dao.model.sql; | 16 | package org.thingsboard.server.dao.model.sql; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
20 | +import com.fasterxml.jackson.databind.JavaType; | ||
21 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
19 | import lombok.Data; | 22 | import lombok.Data; |
20 | import lombok.EqualsAndHashCode; | 23 | import lombok.EqualsAndHashCode; |
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.springframework.util.StringUtils; | ||
21 | import org.thingsboard.server.common.data.DashboardInfo; | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
22 | -import org.thingsboard.server.common.data.id.CustomerId; | 27 | +import org.thingsboard.server.common.data.ShortCustomerInfo; |
23 | import org.thingsboard.server.common.data.id.DashboardId; | 28 | import org.thingsboard.server.common.data.id.DashboardId; |
24 | import org.thingsboard.server.common.data.id.TenantId; | 29 | import org.thingsboard.server.common.data.id.TenantId; |
25 | import org.thingsboard.server.dao.model.BaseSqlEntity; | 30 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
@@ -29,25 +34,32 @@ import org.thingsboard.server.dao.model.SearchTextEntity; | @@ -29,25 +34,32 @@ import org.thingsboard.server.dao.model.SearchTextEntity; | ||
29 | import javax.persistence.Column; | 34 | import javax.persistence.Column; |
30 | import javax.persistence.Entity; | 35 | import javax.persistence.Entity; |
31 | import javax.persistence.Table; | 36 | import javax.persistence.Table; |
37 | +import java.io.IOException; | ||
38 | +import java.util.HashSet; | ||
32 | 39 | ||
33 | @Data | 40 | @Data |
41 | +@Slf4j | ||
34 | @EqualsAndHashCode(callSuper = true) | 42 | @EqualsAndHashCode(callSuper = true) |
35 | @Entity | 43 | @Entity |
36 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) | 44 | @Table(name = ModelConstants.DASHBOARD_COLUMN_FAMILY_NAME) |
37 | public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { | 45 | public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements SearchTextEntity<DashboardInfo> { |
38 | 46 | ||
47 | + private static final ObjectMapper objectMapper = new ObjectMapper(); | ||
48 | + private static final JavaType assignedCustomersType = | ||
49 | + objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class); | ||
50 | + | ||
39 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) | 51 | @Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY) |
40 | private String tenantId; | 52 | private String tenantId; |
41 | 53 | ||
42 | - @Column(name = ModelConstants.DASHBOARD_CUSTOMER_ID_PROPERTY) | ||
43 | - private String customerId; | ||
44 | - | ||
45 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) | 54 | @Column(name = ModelConstants.DASHBOARD_TITLE_PROPERTY) |
46 | private String title; | 55 | private String title; |
47 | 56 | ||
48 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) | 57 | @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
49 | private String searchText; | 58 | private String searchText; |
50 | 59 | ||
60 | + @Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY) | ||
61 | + private String assignedCustomers; | ||
62 | + | ||
51 | public DashboardInfoEntity() { | 63 | public DashboardInfoEntity() { |
52 | super(); | 64 | super(); |
53 | } | 65 | } |
@@ -59,10 +71,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | @@ -59,10 +71,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | ||
59 | if (dashboardInfo.getTenantId() != null) { | 71 | if (dashboardInfo.getTenantId() != null) { |
60 | this.tenantId = toString(dashboardInfo.getTenantId().getId()); | 72 | this.tenantId = toString(dashboardInfo.getTenantId().getId()); |
61 | } | 73 | } |
62 | - if (dashboardInfo.getCustomerId() != null) { | ||
63 | - this.customerId = toString(dashboardInfo.getCustomerId().getId()); | ||
64 | - } | ||
65 | this.title = dashboardInfo.getTitle(); | 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 | @Override | 84 | @Override |
@@ -86,10 +102,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | @@ -86,10 +102,14 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements | ||
86 | if (tenantId != null) { | 102 | if (tenantId != null) { |
87 | dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); | 103 | dashboardInfo.setTenantId(new TenantId(toUUID(tenantId))); |
88 | } | 104 | } |
89 | - if (customerId != null) { | ||
90 | - dashboardInfo.setCustomerId(new CustomerId(toUUID(customerId))); | ||
91 | - } | ||
92 | dashboardInfo.setTitle(title); | 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 | return dashboardInfo; | 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,6 +17,7 @@ package org.thingsboard.server.dao.service; | ||
17 | 17 | ||
18 | import org.thingsboard.server.common.data.id.EntityId; | 18 | import org.thingsboard.server.common.data.id.EntityId; |
19 | import org.thingsboard.server.common.data.id.UUIDBased; | 19 | import org.thingsboard.server.common.data.id.UUIDBased; |
20 | +import org.thingsboard.server.common.data.page.BasePageLink; | ||
20 | import org.thingsboard.server.common.data.page.TextPageLink; | 21 | import org.thingsboard.server.common.data.page.TextPageLink; |
21 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 22 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
22 | 23 | ||
@@ -116,7 +117,7 @@ public class Validator { | @@ -116,7 +117,7 @@ public class Validator { | ||
116 | * @param pageLink the page link | 117 | * @param pageLink the page link |
117 | * @param errorMessage the error message for exception | 118 | * @param errorMessage the error message for exception |
118 | */ | 119 | */ |
119 | - public static void validatePageLink(TextPageLink pageLink, String errorMessage) { | 120 | + public static void validatePageLink(BasePageLink pageLink, String errorMessage) { |
120 | if (pageLink == null || pageLink.getLimit() < 1 || (pageLink.getIdOffset() != null && pageLink.getIdOffset().version() != 1)) { | 121 | if (pageLink == null || pageLink.getLimit() < 1 || (pageLink.getIdOffset() != null && pageLink.getIdOffset().version() != 1)) { |
121 | throw new IncorrectParameterException(errorMessage); | 122 | throw new IncorrectParameterException(errorMessage); |
122 | } | 123 | } |
@@ -39,12 +39,4 @@ public interface DashboardInfoRepository extends CrudRepository<DashboardInfoEnt | @@ -39,12 +39,4 @@ public interface DashboardInfoRepository extends CrudRepository<DashboardInfoEnt | ||
39 | @Param("idOffset") String idOffset, | 39 | @Param("idOffset") String idOffset, |
40 | Pageable pageable); | 40 | Pageable pageable); |
41 | 41 | ||
42 | - @Query("SELECT di FROM DashboardInfoEntity di WHERE di.tenantId = :tenantId " + | ||
43 | - "AND di.customerId = :customerId AND LOWER(di.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " + | ||
44 | - "AND di.id > :idOffset ORDER BY di.id") | ||
45 | - List<DashboardInfoEntity> findByTenantIdAndCustomerId(@Param("tenantId") String tenantId, | ||
46 | - @Param("customerId") String customerId, | ||
47 | - @Param("searchText") String searchText, | ||
48 | - @Param("idOffset") String idOffset, | ||
49 | - Pageable pageable); | ||
50 | } | 42 | } |
@@ -15,19 +15,31 @@ | @@ -15,19 +15,31 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.sql.dashboard; | 16 | package org.thingsboard.server.dao.sql.dashboard; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.AsyncFunction; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
20 | +import com.google.common.util.concurrent.ListenableFuture; | ||
21 | +import lombok.extern.slf4j.Slf4j; | ||
18 | import org.springframework.beans.factory.annotation.Autowired; | 22 | import org.springframework.beans.factory.annotation.Autowired; |
19 | import org.springframework.data.domain.PageRequest; | 23 | import org.springframework.data.domain.PageRequest; |
20 | import org.springframework.data.repository.CrudRepository; | 24 | import org.springframework.data.repository.CrudRepository; |
21 | import org.springframework.stereotype.Component; | 25 | import org.springframework.stereotype.Component; |
22 | import org.thingsboard.server.common.data.DashboardInfo; | 26 | import org.thingsboard.server.common.data.DashboardInfo; |
27 | +import org.thingsboard.server.common.data.EntityType; | ||
23 | import org.thingsboard.server.common.data.UUIDConverter; | 28 | import org.thingsboard.server.common.data.UUIDConverter; |
29 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
24 | import org.thingsboard.server.common.data.page.TextPageLink; | 30 | import org.thingsboard.server.common.data.page.TextPageLink; |
31 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
32 | +import org.thingsboard.server.common.data.relation.EntityRelation; | ||
33 | +import org.thingsboard.server.common.data.relation.RelationTypeGroup; | ||
25 | import org.thingsboard.server.dao.DaoUtil; | 34 | import org.thingsboard.server.dao.DaoUtil; |
26 | import org.thingsboard.server.dao.dashboard.DashboardInfoDao; | 35 | import org.thingsboard.server.dao.dashboard.DashboardInfoDao; |
27 | import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; | 36 | import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; |
37 | +import org.thingsboard.server.dao.relation.RelationDao; | ||
28 | import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; | 38 | import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
29 | import org.thingsboard.server.dao.util.SqlDao; | 39 | import org.thingsboard.server.dao.util.SqlDao; |
30 | 40 | ||
41 | +import java.sql.Time; | ||
42 | +import java.util.ArrayList; | ||
31 | import java.util.List; | 43 | import java.util.List; |
32 | import java.util.Objects; | 44 | import java.util.Objects; |
33 | import java.util.UUID; | 45 | import java.util.UUID; |
@@ -37,11 +49,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; | @@ -37,11 +49,15 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; | ||
37 | /** | 49 | /** |
38 | * Created by Valerii Sosliuk on 5/6/2017. | 50 | * Created by Valerii Sosliuk on 5/6/2017. |
39 | */ | 51 | */ |
52 | +@Slf4j | ||
40 | @Component | 53 | @Component |
41 | @SqlDao | 54 | @SqlDao |
42 | public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { | 55 | public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { |
43 | 56 | ||
44 | @Autowired | 57 | @Autowired |
58 | + private RelationDao relationDao; | ||
59 | + | ||
60 | + @Autowired | ||
45 | private DashboardInfoRepository dashboardInfoRepository; | 61 | private DashboardInfoRepository dashboardInfoRepository; |
46 | 62 | ||
47 | @Override | 63 | @Override |
@@ -65,13 +81,17 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE | @@ -65,13 +81,17 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoE | ||
65 | } | 81 | } |
66 | 82 | ||
67 | @Override | 83 | @Override |
68 | - public List<DashboardInfo> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { | ||
69 | - return DaoUtil.convertDataList(dashboardInfoRepository | ||
70 | - .findByTenantIdAndCustomerId( | ||
71 | - UUIDConverter.fromTimeUUID(tenantId), | ||
72 | - UUIDConverter.fromTimeUUID(customerId), | ||
73 | - Objects.toString(pageLink.getTextSearch(), ""), | ||
74 | - pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()), | ||
75 | - new PageRequest(0, pageLink.getLimit()))); | 84 | + public ListenableFuture<List<DashboardInfo>> findDashboardsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TimePageLink pageLink) { |
85 | + log.debug("Try to find dashboards by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); | ||
86 | + | ||
87 | + ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(new CustomerId(customerId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.DASHBOARD, EntityType.DASHBOARD, pageLink); | ||
88 | + | ||
89 | + return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<DashboardInfo>>) input -> { | ||
90 | + List<ListenableFuture<DashboardInfo>> dashboardFutures = new ArrayList<>(input.size()); | ||
91 | + for (EntityRelation relation : input) { | ||
92 | + dashboardFutures.add(findByIdAsync(relation.getTo().getId())); | ||
93 | + } | ||
94 | + return Futures.successfulAsList(dashboardFutures); | ||
95 | + }); | ||
76 | } | 96 | } |
77 | } | 97 | } |
@@ -364,26 +364,19 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.widget_type_by_tenant_and_ali | @@ -364,26 +364,19 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.widget_type_by_tenant_and_ali | ||
364 | CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( | 364 | CREATE TABLE IF NOT EXISTS thingsboard.dashboard ( |
365 | id timeuuid, | 365 | id timeuuid, |
366 | tenant_id timeuuid, | 366 | tenant_id timeuuid, |
367 | - customer_id timeuuid, | ||
368 | title text, | 367 | title text, |
369 | search_text text, | 368 | search_text text, |
369 | + assigned_customers text, | ||
370 | configuration text, | 370 | configuration text, |
371 | - PRIMARY KEY (id, tenant_id, customer_id) | 371 | + PRIMARY KEY (id, tenant_id) |
372 | ); | 372 | ); |
373 | 373 | ||
374 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS | 374 | CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_tenant_and_search_text AS |
375 | SELECT * | 375 | SELECT * |
376 | from thingsboard.dashboard | 376 | from thingsboard.dashboard |
377 | - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
378 | - PRIMARY KEY ( tenant_id, search_text, id, customer_id ) | ||
379 | - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC ); | ||
380 | - | ||
381 | -CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.dashboard_by_customer_and_search_text AS | ||
382 | - SELECT * | ||
383 | - from thingsboard.dashboard | ||
384 | - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
385 | - PRIMARY KEY ( customer_id, tenant_id, search_text, id ) | ||
386 | - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); | 377 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL |
378 | + PRIMARY KEY ( tenant_id, search_text, id ) | ||
379 | + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); | ||
387 | 380 | ||
388 | CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( | 381 | CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( |
389 | entity_type text, // (DEVICE, CUSTOMER, TENANT) | 382 | entity_type text, // (DEVICE, CUSTOMER, TENANT) |
@@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS customer ( | @@ -105,7 +105,7 @@ CREATE TABLE IF NOT EXISTS customer ( | ||
105 | CREATE TABLE IF NOT EXISTS dashboard ( | 105 | CREATE TABLE IF NOT EXISTS dashboard ( |
106 | id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, | 106 | id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, |
107 | configuration varchar(10000000), | 107 | configuration varchar(10000000), |
108 | - customer_id varchar(31), | 108 | + assigned_customers varchar(1000000), |
109 | search_text varchar(255), | 109 | search_text varchar(255), |
110 | tenant_id varchar(31), | 110 | tenant_id varchar(31), |
111 | title varchar(255) | 111 | title varchar(255) |
@@ -29,13 +29,17 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -29,13 +29,17 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
29 | import org.thingsboard.server.common.data.id.TenantId; | 29 | import org.thingsboard.server.common.data.id.TenantId; |
30 | import org.thingsboard.server.common.data.page.TextPageData; | 30 | import org.thingsboard.server.common.data.page.TextPageData; |
31 | import org.thingsboard.server.common.data.page.TextPageLink; | 31 | import org.thingsboard.server.common.data.page.TextPageLink; |
32 | +import org.thingsboard.server.common.data.page.TimePageData; | ||
33 | +import org.thingsboard.server.common.data.page.TimePageLink; | ||
32 | import org.thingsboard.server.dao.exception.DataValidationException; | 34 | import org.thingsboard.server.dao.exception.DataValidationException; |
33 | import org.thingsboard.server.dao.model.ModelConstants; | 35 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | 36 | ||
35 | import java.io.IOException; | 37 | import java.io.IOException; |
38 | +import java.sql.Time; | ||
36 | import java.util.ArrayList; | 39 | import java.util.ArrayList; |
37 | import java.util.Collections; | 40 | import java.util.Collections; |
38 | import java.util.List; | 41 | import java.util.List; |
42 | +import java.util.concurrent.ExecutionException; | ||
39 | 43 | ||
40 | public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | 44 | public abstract class BaseDashboardServiceTest extends AbstractServiceTest { |
41 | 45 | ||
@@ -68,8 +72,6 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -68,8 +72,6 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
68 | Assert.assertNotNull(savedDashboard.getId()); | 72 | Assert.assertNotNull(savedDashboard.getId()); |
69 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); | 73 | Assert.assertTrue(savedDashboard.getCreatedTime() > 0); |
70 | Assert.assertEquals(dashboard.getTenantId(), savedDashboard.getTenantId()); | 74 | Assert.assertEquals(dashboard.getTenantId(), savedDashboard.getTenantId()); |
71 | - Assert.assertNotNull(savedDashboard.getCustomerId()); | ||
72 | - Assert.assertEquals(ModelConstants.NULL_UUID, savedDashboard.getCustomerId().getId()); | ||
73 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); | 75 | Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); |
74 | 76 | ||
75 | savedDashboard.setTitle("My new dashboard"); | 77 | savedDashboard.setTitle("My new dashboard"); |
@@ -280,7 +282,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -280,7 +282,7 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
280 | } | 282 | } |
281 | 283 | ||
282 | @Test | 284 | @Test |
283 | - public void testFindDashboardsByTenantIdAndCustomerId() { | 285 | + public void testFindDashboardsByTenantIdAndCustomerId() throws ExecutionException, InterruptedException { |
284 | Tenant tenant = new Tenant(); | 286 | Tenant tenant = new Tenant(); |
285 | tenant.setTitle("Test tenant"); | 287 | tenant.setTitle("Test tenant"); |
286 | tenant = tenantService.saveTenant(tenant); | 288 | tenant = tenantService.saveTenant(tenant); |
@@ -303,10 +305,10 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -303,10 +305,10 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
303 | } | 305 | } |
304 | 306 | ||
305 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); | 307 | List<DashboardInfo> loadedDashboards = new ArrayList<>(); |
306 | - TextPageLink pageLink = new TextPageLink(23); | ||
307 | - TextPageData<DashboardInfo> pageData = null; | 308 | + TimePageLink pageLink = new TimePageLink(23); |
309 | + TimePageData<DashboardInfo> pageData = null; | ||
308 | do { | 310 | do { |
309 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | 311 | + pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get(); |
310 | loadedDashboards.addAll(pageData.getData()); | 312 | loadedDashboards.addAll(pageData.getData()); |
311 | if (pageData.hasNext()) { | 313 | if (pageData.hasNext()) { |
312 | pageLink = pageData.getNextPageLink(); | 314 | pageLink = pageData.getNextPageLink(); |
@@ -318,98 +320,14 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | @@ -318,98 +320,14 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { | ||
318 | 320 | ||
319 | Assert.assertEquals(dashboards, loadedDashboards); | 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 | Assert.assertFalse(pageData.hasNext()); | 327 | Assert.assertFalse(pageData.hasNext()); |
326 | Assert.assertTrue(pageData.getData().isEmpty()); | 328 | Assert.assertTrue(pageData.getData().isEmpty()); |
327 | 329 | ||
328 | tenantService.deleteTenant(tenantId); | 330 | tenantService.deleteTenant(tenantId); |
329 | } | 331 | } |
330 | - | ||
331 | - @Test | ||
332 | - public void testFindDashboardsByTenantIdCustomerIdAndTitle() { | ||
333 | - | ||
334 | - Customer customer = new Customer(); | ||
335 | - customer.setTitle("Test customer"); | ||
336 | - customer.setTenantId(tenantId); | ||
337 | - customer = customerService.saveCustomer(customer); | ||
338 | - CustomerId customerId = customer.getId(); | ||
339 | - | ||
340 | - String title1 = "Dashboard title 1"; | ||
341 | - List<DashboardInfo> dashboardsTitle1 = new ArrayList<>(); | ||
342 | - for (int i=0;i<124;i++) { | ||
343 | - Dashboard dashboard = new Dashboard(); | ||
344 | - dashboard.setTenantId(tenantId); | ||
345 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
346 | - String title = title1+suffix; | ||
347 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
348 | - dashboard.setTitle(title); | ||
349 | - dashboard = dashboardService.saveDashboard(dashboard); | ||
350 | - dashboardsTitle1.add(new DashboardInfo(dashboardService.assignDashboardToCustomer(dashboard.getId(), customerId))); | ||
351 | - } | ||
352 | - String title2 = "Dashboard title 2"; | ||
353 | - List<DashboardInfo> dashboardsTitle2 = new ArrayList<>(); | ||
354 | - for (int i=0;i<151;i++) { | ||
355 | - Dashboard dashboard = new Dashboard(); | ||
356 | - dashboard.setTenantId(tenantId); | ||
357 | - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); | ||
358 | - String title = title2+suffix; | ||
359 | - title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); | ||
360 | - dashboard.setTitle(title); | ||
361 | - dashboard = dashboardService.saveDashboard(dashboard); | ||
362 | - dashboardsTitle2.add(new DashboardInfo(dashboardService.assignDashboardToCustomer(dashboard.getId(), customerId))); | ||
363 | - } | ||
364 | - | ||
365 | - List<DashboardInfo> loadedDashboardsTitle1 = new ArrayList<>(); | ||
366 | - TextPageLink pageLink = new TextPageLink(24, title1); | ||
367 | - TextPageData<DashboardInfo> pageData = null; | ||
368 | - do { | ||
369 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
370 | - loadedDashboardsTitle1.addAll(pageData.getData()); | ||
371 | - if (pageData.hasNext()) { | ||
372 | - pageLink = pageData.getNextPageLink(); | ||
373 | - } | ||
374 | - } while (pageData.hasNext()); | ||
375 | - | ||
376 | - Collections.sort(dashboardsTitle1, idComparator); | ||
377 | - Collections.sort(loadedDashboardsTitle1, idComparator); | ||
378 | - | ||
379 | - Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); | ||
380 | - | ||
381 | - List<DashboardInfo> loadedDashboardsTitle2 = new ArrayList<>(); | ||
382 | - pageLink = new TextPageLink(4, title2); | ||
383 | - do { | ||
384 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
385 | - loadedDashboardsTitle2.addAll(pageData.getData()); | ||
386 | - if (pageData.hasNext()) { | ||
387 | - pageLink = pageData.getNextPageLink(); | ||
388 | - } | ||
389 | - } while (pageData.hasNext()); | ||
390 | 332 | ||
391 | - Collections.sort(dashboardsTitle2, idComparator); | ||
392 | - Collections.sort(loadedDashboardsTitle2, idComparator); | ||
393 | - | ||
394 | - Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); | ||
395 | - | ||
396 | - for (DashboardInfo dashboard : loadedDashboardsTitle1) { | ||
397 | - dashboardService.deleteDashboard(dashboard.getId()); | ||
398 | - } | ||
399 | - | ||
400 | - pageLink = new TextPageLink(4, title1); | ||
401 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
402 | - Assert.assertFalse(pageData.hasNext()); | ||
403 | - Assert.assertEquals(0, pageData.getData().size()); | ||
404 | - | ||
405 | - for (DashboardInfo dashboard : loadedDashboardsTitle2) { | ||
406 | - dashboardService.deleteDashboard(dashboard.getId()); | ||
407 | - } | ||
408 | - | ||
409 | - pageLink = new TextPageLink(4, title2); | ||
410 | - pageData = dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink); | ||
411 | - Assert.assertFalse(pageData.hasNext()); | ||
412 | - Assert.assertEquals(0, pageData.getData().size()); | ||
413 | - customerService.deleteCustomer(customerId); | ||
414 | - } | ||
415 | } | 333 | } |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | package org.thingsboard.server.dao.sql.dashboard; | 16 | package org.thingsboard.server.dao.sql.dashboard; |
17 | 17 | ||
18 | import com.datastax.driver.core.utils.UUIDs; | 18 | import com.datastax.driver.core.utils.UUIDs; |
19 | +import org.junit.Assert; | ||
19 | import org.junit.Test; | 20 | import org.junit.Test; |
20 | import org.springframework.beans.factory.annotation.Autowired; | 21 | import org.springframework.beans.factory.annotation.Autowired; |
21 | import org.thingsboard.server.common.data.DashboardInfo; | 22 | import org.thingsboard.server.common.data.DashboardInfo; |
@@ -40,53 +41,26 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { | @@ -40,53 +41,26 @@ public class JpaDashboardInfoDaoTest extends AbstractJpaDaoTest { | ||
40 | @Test | 41 | @Test |
41 | public void testFindDashboardsByTenantId() { | 42 | public void testFindDashboardsByTenantId() { |
42 | UUID tenantId1 = UUIDs.timeBased(); | 43 | UUID tenantId1 = UUIDs.timeBased(); |
43 | - UUID customerId1 = UUIDs.timeBased(); | ||
44 | UUID tenantId2 = UUIDs.timeBased(); | 44 | UUID tenantId2 = UUIDs.timeBased(); |
45 | - UUID customerId2 = UUIDs.timeBased(); | ||
46 | 45 | ||
47 | for (int i = 0; i < 20; i++) { | 46 | for (int i = 0; i < 20; i++) { |
48 | - createDashboard(tenantId1, customerId1, i); | ||
49 | - createDashboard(tenantId2, customerId2, i * 2); | 47 | + createDashboard(tenantId1, i); |
48 | + createDashboard(tenantId2, i * 2); | ||
50 | } | 49 | } |
51 | 50 | ||
52 | TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); | 51 | TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); |
53 | List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink1); | 52 | List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink1); |
54 | - assertEquals(15, dashboardInfos1.size()); | 53 | + Assert.assertEquals(15, dashboardInfos1.size()); |
55 | 54 | ||
56 | TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); | 55 | TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); |
57 | List<DashboardInfo> dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink2); | 56 | List<DashboardInfo> dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantId(tenantId1, pageLink2); |
58 | - assertEquals(5, dashboardInfos2.size()); | 57 | + Assert.assertEquals(5, dashboardInfos2.size()); |
59 | } | 58 | } |
60 | 59 | ||
61 | - @Test | ||
62 | - public void testFindDashboardsByTenantAndCustomerId() { | ||
63 | - UUID tenantId1 = UUIDs.timeBased(); | ||
64 | - UUID customerId1 = UUIDs.timeBased(); | ||
65 | - UUID tenantId2 = UUIDs.timeBased(); | ||
66 | - UUID customerId2 = UUIDs.timeBased(); | ||
67 | - | ||
68 | - for (int i = 0; i < 20; i++) { | ||
69 | - createDashboard(tenantId1, customerId1, i); | ||
70 | - createDashboard(tenantId2, customerId2, i * 2); | ||
71 | - } | ||
72 | - | ||
73 | - TextPageLink pageLink1 = new TextPageLink(15, "DASHBOARD"); | ||
74 | - List<DashboardInfo> dashboardInfos1 = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId1, customerId1, pageLink1); | ||
75 | - assertEquals(15, dashboardInfos1.size()); | ||
76 | - | ||
77 | - TextPageLink pageLink2 = new TextPageLink(15, "DASHBOARD", dashboardInfos1.get(14).getId().getId(), null); | ||
78 | - List<DashboardInfo> dashboardInfos2 = dashboardInfoDao.findDashboardsByTenantIdAndCustomerId(tenantId1, customerId1, pageLink2); | ||
79 | - assertEquals(5, dashboardInfos2.size()); | ||
80 | - } | ||
81 | - | ||
82 | - private void assertEquals(int i, int size) { | ||
83 | - } | ||
84 | - | ||
85 | - private void createDashboard(UUID tenantId, UUID customerId, int index) { | 60 | + private void createDashboard(UUID tenantId, int index) { |
86 | DashboardInfo dashboardInfo = new DashboardInfo(); | 61 | DashboardInfo dashboardInfo = new DashboardInfo(); |
87 | dashboardInfo.setId(new DashboardId(UUIDs.timeBased())); | 62 | dashboardInfo.setId(new DashboardId(UUIDs.timeBased())); |
88 | dashboardInfo.setTenantId(new TenantId(tenantId)); | 63 | dashboardInfo.setTenantId(new TenantId(tenantId)); |
89 | - dashboardInfo.setCustomerId(new CustomerId(customerId)); | ||
90 | dashboardInfo.setTitle("DASHBOARD_" + index); | 64 | dashboardInfo.setTitle("DASHBOARD_" + index); |
91 | dashboardInfoDao.save(dashboardInfo); | 65 | dashboardInfoDao.save(dashboardInfo); |
92 | } | 66 | } |
@@ -17,7 +17,7 @@ export default angular.module('thingsboard.api.dashboard', []) | @@ -17,7 +17,7 @@ export default angular.module('thingsboard.api.dashboard', []) | ||
17 | .factory('dashboardService', DashboardService).name; | 17 | .factory('dashboardService', DashboardService).name; |
18 | 18 | ||
19 | /*@ngInject*/ | 19 | /*@ngInject*/ |
20 | -function DashboardService($rootScope, $http, $q, $location, customerService) { | 20 | +function DashboardService($rootScope, $http, $q, $location, $filter) { |
21 | 21 | ||
22 | var stDiffPromise; | 22 | var stDiffPromise; |
23 | 23 | ||
@@ -37,7 +37,11 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -37,7 +37,11 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
37 | deleteDashboard: deleteDashboard, | 37 | deleteDashboard: deleteDashboard, |
38 | saveDashboard: saveDashboard, | 38 | saveDashboard: saveDashboard, |
39 | unassignDashboardFromCustomer: unassignDashboardFromCustomer, | 39 | unassignDashboardFromCustomer: unassignDashboardFromCustomer, |
40 | + updateDashboardCustomers: updateDashboardCustomers, | ||
41 | + addDashboardCustomers: addDashboardCustomers, | ||
42 | + removeDashboardCustomers: removeDashboardCustomers, | ||
40 | makeDashboardPublic: makeDashboardPublic, | 43 | makeDashboardPublic: makeDashboardPublic, |
44 | + makeDashboardPrivate: makeDashboardPrivate, | ||
41 | getPublicDashboardLink: getPublicDashboardLink | 45 | getPublicDashboardLink: getPublicDashboardLink |
42 | } | 46 | } |
43 | 47 | ||
@@ -56,14 +60,14 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -56,14 +60,14 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
56 | url += '&textOffset=' + pageLink.textOffset; | 60 | url += '&textOffset=' + pageLink.textOffset; |
57 | } | 61 | } |
58 | $http.get(url, config).then(function success(response) { | 62 | $http.get(url, config).then(function success(response) { |
59 | - deferred.resolve(response.data); | 63 | + deferred.resolve(prepareDashboards(response.data)); |
60 | }, function fail() { | 64 | }, function fail() { |
61 | deferred.reject(); | 65 | deferred.reject(); |
62 | }); | 66 | }); |
63 | return deferred.promise; | 67 | return deferred.promise; |
64 | } | 68 | } |
65 | 69 | ||
66 | - function getTenantDashboards(pageLink, applyCustomersInfo, config) { | 70 | + function getTenantDashboards(pageLink, config) { |
67 | var deferred = $q.defer(); | 71 | var deferred = $q.defer(); |
68 | var url = '/api/tenant/dashboards?limit=' + pageLink.limit; | 72 | var url = '/api/tenant/dashboards?limit=' + pageLink.limit; |
69 | if (angular.isDefined(pageLink.textSearch)) { | 73 | if (angular.isDefined(pageLink.textSearch)) { |
@@ -76,51 +80,25 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -76,51 +80,25 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
76 | url += '&textOffset=' + pageLink.textOffset; | 80 | url += '&textOffset=' + pageLink.textOffset; |
77 | } | 81 | } |
78 | $http.get(url, config).then(function success(response) { | 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 | }, function fail() { | 84 | }, function fail() { |
93 | deferred.reject(); | 85 | deferred.reject(); |
94 | }); | 86 | }); |
95 | return deferred.promise; | 87 | return deferred.promise; |
96 | } | 88 | } |
97 | 89 | ||
98 | - function getCustomerDashboards(customerId, pageLink, applyCustomersInfo, config) { | 90 | + function getCustomerDashboards(customerId, pageLink, config) { |
99 | var deferred = $q.defer(); | 91 | var deferred = $q.defer(); |
100 | var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; | 92 | var url = '/api/customer/' + customerId + '/dashboards?limit=' + pageLink.limit; |
101 | - if (angular.isDefined(pageLink.textSearch)) { | ||
102 | - url += '&textSearch=' + pageLink.textSearch; | ||
103 | - } | ||
104 | if (angular.isDefined(pageLink.idOffset)) { | 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 | $http.get(url, config).then(function success(response) { | 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 | }, function fail() { | 102 | }, function fail() { |
125 | deferred.reject(); | 103 | deferred.reject(); |
126 | }); | 104 | }); |
@@ -151,7 +129,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -151,7 +129,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
151 | var deferred = $q.defer(); | 129 | var deferred = $q.defer(); |
152 | var url = '/api/dashboard/' + dashboardId; | 130 | var url = '/api/dashboard/' + dashboardId; |
153 | $http.get(url, null).then(function success(response) { | 131 | $http.get(url, null).then(function success(response) { |
154 | - deferred.resolve(response.data); | 132 | + deferred.resolve(prepareDashboard(response.data)); |
155 | }, function fail() { | 133 | }, function fail() { |
156 | deferred.reject(); | 134 | deferred.reject(); |
157 | }); | 135 | }); |
@@ -162,7 +140,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -162,7 +140,7 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
162 | var deferred = $q.defer(); | 140 | var deferred = $q.defer(); |
163 | var url = '/api/dashboard/info/' + dashboardId; | 141 | var url = '/api/dashboard/info/' + dashboardId; |
164 | $http.get(url, config).then(function success(response) { | 142 | $http.get(url, config).then(function success(response) { |
165 | - deferred.resolve(response.data); | 143 | + deferred.resolve(prepareDashboard(response.data)); |
166 | }, function fail() { | 144 | }, function fail() { |
167 | deferred.reject(); | 145 | deferred.reject(); |
168 | }); | 146 | }); |
@@ -172,8 +150,8 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -172,8 +150,8 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
172 | function saveDashboard(dashboard) { | 150 | function saveDashboard(dashboard) { |
173 | var deferred = $q.defer(); | 151 | var deferred = $q.defer(); |
174 | var url = '/api/dashboard'; | 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 | }, function fail() { | 155 | }, function fail() { |
178 | deferred.reject(); | 156 | deferred.reject(); |
179 | }); | 157 | }); |
@@ -195,18 +173,51 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -195,18 +173,51 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
195 | var deferred = $q.defer(); | 173 | var deferred = $q.defer(); |
196 | var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; | 174 | var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; |
197 | $http.post(url, null).then(function success(response) { | 175 | $http.post(url, null).then(function success(response) { |
198 | - deferred.resolve(response.data); | 176 | + deferred.resolve(prepareDashboard(response.data)); |
199 | }, function fail() { | 177 | }, function fail() { |
200 | deferred.reject(); | 178 | deferred.reject(); |
201 | }); | 179 | }); |
202 | return deferred.promise; | 180 | return deferred.promise; |
203 | } | 181 | } |
204 | 182 | ||
205 | - function unassignDashboardFromCustomer(dashboardId) { | 183 | + function unassignDashboardFromCustomer(customerId, dashboardId) { |
206 | var deferred = $q.defer(); | 184 | var deferred = $q.defer(); |
207 | - var url = '/api/customer/dashboard/' + dashboardId; | 185 | + var url = '/api/customer/' + customerId + '/dashboard/' + dashboardId; |
208 | $http.delete(url).then(function success(response) { | 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 | }, function fail() { | 221 | }, function fail() { |
211 | deferred.reject(); | 222 | deferred.reject(); |
212 | }); | 223 | }); |
@@ -217,7 +228,18 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -217,7 +228,18 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
217 | var deferred = $q.defer(); | 228 | var deferred = $q.defer(); |
218 | var url = '/api/customer/public/dashboard/' + dashboardId; | 229 | var url = '/api/customer/public/dashboard/' + dashboardId; |
219 | $http.post(url, null).then(function success(response) { | 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 | }, function fail() { | 243 | }, function fail() { |
222 | deferred.reject(); | 244 | deferred.reject(); |
223 | }); | 245 | }); |
@@ -230,8 +252,44 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | @@ -230,8 +252,44 @@ function DashboardService($rootScope, $http, $q, $location, customerService) { | ||
230 | if (port != 80 && port != 443) { | 252 | if (port != 80 && port != 443) { |
231 | url += ":" + port; | 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 | return url; | 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,9 +273,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
273 | break; | 273 | break; |
274 | case types.entityType.dashboard: | 274 | case types.entityType.dashboard: |
275 | if (user.authority === 'CUSTOMER_USER') { | 275 | if (user.authority === 'CUSTOMER_USER') { |
276 | - promise = dashboardService.getCustomerDashboards(customerId, pageLink, false, config); | 276 | + promise = dashboardService.getCustomerDashboards(customerId, pageLink, config); |
277 | } else { | 277 | } else { |
278 | - promise = dashboardService.getTenantDashboards(pageLink, false, config); | 278 | + promise = dashboardService.getTenantDashboards(pageLink, config); |
279 | } | 279 | } |
280 | break; | 280 | break; |
281 | case types.entityType.user: | 281 | case types.entityType.user: |
@@ -403,6 +403,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -403,6 +403,21 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
403 | return deferred.promise; | 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 | function getStateEntityId(filter, stateParams) { | 421 | function getStateEntityId(filter, stateParams) { |
407 | var entityId = null; | 422 | var entityId = null; |
408 | if (stateParams) { | 423 | if (stateParams) { |
@@ -417,6 +432,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -417,6 +432,9 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
417 | if (!entityId) { | 432 | if (!entityId) { |
418 | entityId = filter.defaultStateEntity; | 433 | entityId = filter.defaultStateEntity; |
419 | } | 434 | } |
435 | + if (entityId) { | ||
436 | + entityId = resolveAliasEntityId(entityId.entityType, entityId.id); | ||
437 | + } | ||
420 | return entityId; | 438 | return entityId; |
421 | } | 439 | } |
422 | 440 | ||
@@ -432,7 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -432,7 +450,8 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
432 | var stateEntityId = getStateEntityId(filter, stateParams); | 450 | var stateEntityId = getStateEntityId(filter, stateParams); |
433 | switch (filter.type) { | 451 | switch (filter.type) { |
434 | case types.aliasFilterType.singleEntity.value: | 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 | function success(entity) { | 455 | function success(entity) { |
437 | result.entities = entitiesToEntitiesInfo([entity]); | 456 | result.entities = entitiesToEntitiesInfo([entity]); |
438 | deferred.resolve(result); | 457 | deferred.resolve(result); |
@@ -530,10 +549,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -530,10 +549,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
530 | rootEntityId = filter.rootEntity.id; | 549 | rootEntityId = filter.rootEntity.id; |
531 | } | 550 | } |
532 | if (rootEntityType && rootEntityId) { | 551 | if (rootEntityType && rootEntityId) { |
552 | + var relationQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId); | ||
533 | var searchQuery = { | 553 | var searchQuery = { |
534 | parameters: { | 554 | parameters: { |
535 | - rootId: rootEntityId, | ||
536 | - rootType: rootEntityType, | 555 | + rootId: relationQueryRootEntityId.id, |
556 | + rootType: relationQueryRootEntityId.entityType, | ||
537 | direction: filter.direction | 557 | direction: filter.direction |
538 | }, | 558 | }, |
539 | filters: filter.filters | 559 | filters: filter.filters |
@@ -571,10 +591,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -571,10 +591,11 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
571 | rootEntityId = filter.rootEntity.id; | 591 | rootEntityId = filter.rootEntity.id; |
572 | } | 592 | } |
573 | if (rootEntityType && rootEntityId) { | 593 | if (rootEntityType && rootEntityId) { |
594 | + var searchQueryRootEntityId = resolveAliasEntityId(rootEntityType, rootEntityId); | ||
574 | searchQuery = { | 595 | searchQuery = { |
575 | parameters: { | 596 | parameters: { |
576 | - rootId: rootEntityId, | ||
577 | - rootType: rootEntityType, | 597 | + rootId: searchQueryRootEntityId.id, |
598 | + rootType: searchQueryRootEntityId.entityType, | ||
578 | direction: filter.direction | 599 | direction: filter.direction |
579 | }, | 600 | }, |
580 | relationType: filter.relationType | 601 | relationType: filter.relationType |
@@ -709,7 +730,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -709,7 +730,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
709 | return result; | 730 | return result; |
710 | } | 731 | } |
711 | 732 | ||
712 | - function prepareAllowedEntityTypesList(allowedEntityTypes) { | 733 | + function prepareAllowedEntityTypesList(allowedEntityTypes, useAliasEntityTypes) { |
713 | var authority = userService.getAuthority(); | 734 | var authority = userService.getAuthority(); |
714 | var entityTypes = {}; | 735 | var entityTypes = {}; |
715 | switch(authority) { | 736 | switch(authority) { |
@@ -726,12 +747,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | @@ -726,12 +747,18 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device | ||
726 | entityTypes.rule = types.entityType.rule; | 747 | entityTypes.rule = types.entityType.rule; |
727 | entityTypes.plugin = types.entityType.plugin; | 748 | entityTypes.plugin = types.entityType.plugin; |
728 | entityTypes.dashboard = types.entityType.dashboard; | 749 | entityTypes.dashboard = types.entityType.dashboard; |
750 | + if (useAliasEntityTypes) { | ||
751 | + entityTypes.current_customer = types.aliasEntityType.current_customer; | ||
752 | + } | ||
729 | break; | 753 | break; |
730 | case 'CUSTOMER_USER': | 754 | case 'CUSTOMER_USER': |
731 | entityTypes.device = types.entityType.device; | 755 | entityTypes.device = types.entityType.device; |
732 | entityTypes.asset = types.entityType.asset; | 756 | entityTypes.asset = types.entityType.asset; |
733 | entityTypes.customer = types.entityType.customer; | 757 | entityTypes.customer = types.entityType.customer; |
734 | entityTypes.dashboard = types.entityType.dashboard; | 758 | entityTypes.dashboard = types.entityType.dashboard; |
759 | + if (useAliasEntityTypes) { | ||
760 | + entityTypes.current_customer = types.aliasEntityType.current_customer; | ||
761 | + } | ||
735 | break; | 762 | break; |
736 | } | 763 | } |
737 | 764 |
@@ -266,9 +266,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi | @@ -266,9 +266,9 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi | ||
266 | var pageLink = {limit: 100}; | 266 | var pageLink = {limit: 100}; |
267 | var fetchDashboardsPromise; | 267 | var fetchDashboardsPromise; |
268 | if (currentUser.authority === 'TENANT_ADMIN') { | 268 | if (currentUser.authority === 'TENANT_ADMIN') { |
269 | - fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink, false); | 269 | + fetchDashboardsPromise = dashboardService.getTenantDashboards(pageLink); |
270 | } else { | 270 | } else { |
271 | - fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink, false); | 271 | + fetchDashboardsPromise = dashboardService.getCustomerDashboards(currentUser.customerId, pageLink); |
272 | } | 272 | } |
273 | fetchDashboardsPromise.then( | 273 | fetchDashboardsPromise.then( |
274 | function success(result) { | 274 | function success(result) { |
@@ -296,6 +296,9 @@ export default angular.module('thingsboard.types', []) | @@ -296,6 +296,9 @@ export default angular.module('thingsboard.types', []) | ||
296 | dashboard: "DASHBOARD", | 296 | dashboard: "DASHBOARD", |
297 | alarm: "ALARM" | 297 | alarm: "ALARM" |
298 | }, | 298 | }, |
299 | + aliasEntityType: { | ||
300 | + current_customer: "CURRENT_CUSTOMER" | ||
301 | + }, | ||
299 | entityTypeTranslations: { | 302 | entityTypeTranslations: { |
300 | "DEVICE": { | 303 | "DEVICE": { |
301 | type: 'entity.type-device', | 304 | type: 'entity.type-device', |
@@ -350,6 +353,10 @@ export default angular.module('thingsboard.types', []) | @@ -350,6 +353,10 @@ export default angular.module('thingsboard.types', []) | ||
350 | typePlural: 'entity.type-alarms', | 353 | typePlural: 'entity.type-alarms', |
351 | list: 'entity.list-of-alarms', | 354 | list: 'entity.list-of-alarms', |
352 | nameStartsWith: 'entity.alarm-name-starts-with' | 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 | entitySearchDirection: { | 362 | entitySearchDirection: { |
@@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u | @@ -48,7 +48,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u | ||
48 | var promise; | 48 | var promise; |
49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { | 49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
50 | if (scope.customerId) { | 50 | if (scope.customerId) { |
51 | - promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, false, {ignoreLoading: true}); | 51 | + promise = dashboardService.getCustomerDashboards(scope.customerId, pageLink, {ignoreLoading: true}); |
52 | } else { | 52 | } else { |
53 | promise = $q.when({data: []}); | 53 | promise = $q.when({data: []}); |
54 | } | 54 | } |
@@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u | @@ -60,7 +60,7 @@ function DashboardAutocomplete($compile, $templateCache, $q, dashboardService, u | ||
60 | promise = $q.when({data: []}); | 60 | promise = $q.when({data: []}); |
61 | } | 61 | } |
62 | } else { | 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,12 +48,12 @@ function DashboardSelect($compile, $templateCache, $q, $mdMedia, $mdPanel, $docu | ||
48 | var promise; | 48 | var promise; |
49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { | 49 | if (scope.dashboardsScope === 'customer' || userService.getAuthority() === 'CUSTOMER_USER') { |
50 | if (scope.customerId && scope.customerId != types.id.nullUid) { | 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 | } else { | 52 | } else { |
53 | promise = $q.when({data: []}); | 53 | promise = $q.when({data: []}); |
54 | } | 54 | } |
55 | } else { | 55 | } else { |
56 | - promise = dashboardService.getTenantDashboards(pageLink, false, {ignoreLoading: true}); | 56 | + promise = dashboardService.getTenantDashboards(pageLink, {ignoreLoading: true}); |
57 | } | 57 | } |
58 | 58 | ||
59 | promise.then(function success(result) { | 59 | promise.then(function success(result) { |
@@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD | @@ -52,7 +52,7 @@ export default function AddDashboardsToCustomerController(dashboardService, $mdD | ||
52 | fetchMoreItems_: function () { | 52 | fetchMoreItems_: function () { |
53 | if (vm.dashboards.hasNext && !vm.dashboards.pending) { | 53 | if (vm.dashboards.hasNext && !vm.dashboards.pending) { |
54 | vm.dashboards.pending = true; | 54 | vm.dashboards.pending = true; |
55 | - dashboardService.getTenantDashboards(vm.dashboards.nextPageLink, false).then( | 55 | + dashboardService.getTenantDashboards(vm.dashboards.nextPageLink).then( |
56 | function success(dashboards) { | 56 | function success(dashboards) { |
57 | vm.dashboards.data = vm.dashboards.data.concat(dashboards.data); | 57 | vm.dashboards.data = vm.dashboards.data.concat(dashboards.data); |
58 | vm.dashboards.nextPageLink = dashboards.nextPageLink; | 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,6 +15,6 @@ | ||
15 | limitations under the License. | 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,24 +19,29 @@ | ||
19 | ng-show="!isEdit && dashboardScope === 'tenant'" | 19 | ng-show="!isEdit && dashboardScope === 'tenant'" |
20 | class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button> | 20 | class="md-raised md-primary">{{ 'dashboard.export' | translate }}</md-button> |
21 | <md-button ng-click="onMakePublic({event: $event})" | 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 | class="md-raised md-primary">{{ 'dashboard.make-public' | translate }}</md-button> | 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 | <md-button ng-click="onDeleteDashboard({event: $event})" | 34 | <md-button ng-click="onDeleteDashboard({event: $event})" |
31 | ng-show="!isEdit && dashboardScope === 'tenant'" | 35 | ng-show="!isEdit && dashboardScope === 'tenant'" |
32 | class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button> | 36 | class="md-raised md-primary">{{ 'dashboard.delete' | translate }}</md-button> |
33 | <md-content class="md-padding" layout="column"> | 37 | <md-content class="md-padding" layout="column"> |
34 | <md-input-container class="md-block" | 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 | </md-input-container> | 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 | <tb-social-share-panel style="padding-bottom: 10px;" | 45 | <tb-social-share-panel style="padding-bottom: 10px;" |
41 | share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}" | 46 | share-title="{{ 'dashboard.socialshare-title' | translate:{dashboardTitle: dashboard.title} }}" |
42 | share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}" | 47 | share-text="{{ 'dashboard.socialshare-text' | translate:{dashboardTitle: dashboard.title} }}" |
@@ -20,36 +20,17 @@ import dashboardFieldsetTemplate from './dashboard-fieldset.tpl.html'; | @@ -20,36 +20,17 @@ import dashboardFieldsetTemplate from './dashboard-fieldset.tpl.html'; | ||
20 | /* eslint-enable import/no-unresolved, import/default */ | 20 | /* eslint-enable import/no-unresolved, import/default */ |
21 | 21 | ||
22 | /*@ngInject*/ | 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 | var linker = function (scope, element) { | 24 | var linker = function (scope, element) { |
25 | var template = $templateCache.get(dashboardFieldsetTemplate); | 25 | var template = $templateCache.get(dashboardFieldsetTemplate); |
26 | element.html(template); | 26 | element.html(template); |
27 | - | ||
28 | - scope.isAssignedToCustomer = false; | ||
29 | - scope.isPublic = false; | ||
30 | - scope.assignedCustomer = null; | ||
31 | scope.publicLink = null; | 27 | scope.publicLink = null; |
32 | - | ||
33 | scope.$watch('dashboard', function(newVal) { | 28 | scope.$watch('dashboard', function(newVal) { |
34 | if (newVal) { | 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 | } else { | 32 | } else { |
49 | - scope.isAssignedToCustomer = false; | ||
50 | - scope.isPublic = false; | ||
51 | scope.publicLink = null; | 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,10 +47,12 @@ export default function DashboardDirective($compile, $templateCache, $translate, | ||
66 | scope: { | 47 | scope: { |
67 | dashboard: '=', | 48 | dashboard: '=', |
68 | isEdit: '=', | 49 | isEdit: '=', |
50 | + customerId: '=', | ||
69 | dashboardScope: '=', | 51 | dashboardScope: '=', |
70 | theForm: '=', | 52 | theForm: '=', |
71 | - onAssignToCustomer: '&', | ||
72 | onMakePublic: '&', | 53 | onMakePublic: '&', |
54 | + onMakePrivate: '&', | ||
55 | + onManageAssignedCustomers: '&', | ||
73 | onUnassignFromCustomer: '&', | 56 | onUnassignFromCustomer: '&', |
74 | onExportDashboard: '&', | 57 | onExportDashboard: '&', |
75 | onDeleteDashboard: '&' | 58 | onDeleteDashboard: '&' |
@@ -17,12 +17,14 @@ | @@ -17,12 +17,14 @@ | ||
17 | 17 | ||
18 | import addDashboardTemplate from './add-dashboard.tpl.html'; | 18 | import addDashboardTemplate from './add-dashboard.tpl.html'; |
19 | import dashboardCard from './dashboard-card.tpl.html'; | 19 | import dashboardCard from './dashboard-card.tpl.html'; |
20 | -import assignToCustomerTemplate from './assign-to-customer.tpl.html'; | ||
21 | import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html'; | 20 | import addDashboardsToCustomerTemplate from './add-dashboards-to-customer.tpl.html'; |
22 | import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html'; | 21 | import makeDashboardPublicDialogTemplate from './make-dashboard-public-dialog.tpl.html'; |
22 | +import manageAssignedCustomersTemplate from './manage-assigned-customers.tpl.html'; | ||
23 | 23 | ||
24 | /* eslint-enable import/no-unresolved, import/default */ | 24 | /* eslint-enable import/no-unresolved, import/default */ |
25 | 25 | ||
26 | +import './dashboard-card.scss'; | ||
27 | + | ||
26 | /*@ngInject*/ | 28 | /*@ngInject*/ |
27 | export function MakeDashboardPublicDialogController($mdDialog, $translate, toast, dashboardService, dashboard) { | 29 | export function MakeDashboardPublicDialogController($mdDialog, $translate, toast, dashboardService, dashboard) { |
28 | 30 | ||
@@ -48,23 +50,8 @@ export function MakeDashboardPublicDialogController($mdDialog, $translate, toast | @@ -48,23 +50,8 @@ export function MakeDashboardPublicDialogController($mdDialog, $translate, toast | ||
48 | export function DashboardCardController(types) { | 50 | export function DashboardCardController(types) { |
49 | 51 | ||
50 | var vm = this; | 52 | var vm = this; |
51 | - | ||
52 | vm.types = types; | 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 | /*@ngInject*/ | 57 | /*@ngInject*/ |
@@ -135,8 +122,9 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -135,8 +122,9 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
135 | 122 | ||
136 | vm.dashboardsScope = $state.$current.data.dashboardsType; | 123 | vm.dashboardsScope = $state.$current.data.dashboardsType; |
137 | 124 | ||
138 | - vm.assignToCustomer = assignToCustomer; | ||
139 | vm.makePublic = makePublic; | 125 | vm.makePublic = makePublic; |
126 | + vm.makePrivate = makePrivate; | ||
127 | + vm.manageAssignedCustomers = manageAssignedCustomers; | ||
140 | vm.unassignFromCustomer = unassignFromCustomer; | 128 | vm.unassignFromCustomer = unassignFromCustomer; |
141 | vm.exportDashboard = exportDashboard; | 129 | vm.exportDashboard = exportDashboard; |
142 | 130 | ||
@@ -155,6 +143,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -155,6 +143,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
155 | } | 143 | } |
156 | 144 | ||
157 | if (customerId) { | 145 | if (customerId) { |
146 | + vm.customerId = customerId; | ||
158 | vm.customerDashboardsTitle = $translate.instant('customer.dashboards'); | 147 | vm.customerDashboardsTitle = $translate.instant('customer.dashboards'); |
159 | customerService.getShortCustomerInfo(customerId).then( | 148 | customerService.getShortCustomerInfo(customerId).then( |
160 | function success(info) { | 149 | function success(info) { |
@@ -167,7 +156,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -167,7 +156,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
167 | 156 | ||
168 | if (vm.dashboardsScope === 'tenant') { | 157 | if (vm.dashboardsScope === 'tenant') { |
169 | fetchDashboardsFunction = function (pageLink) { | 158 | fetchDashboardsFunction = function (pageLink) { |
170 | - return dashboardService.getTenantDashboards(pageLink, true); | 159 | + return dashboardService.getTenantDashboards(pageLink); |
171 | }; | 160 | }; |
172 | deleteDashboardFunction = function (dashboardId) { | 161 | deleteDashboardFunction = function (dashboardId) { |
173 | return dashboardService.deleteDashboard(dashboardId); | 162 | return dashboardService.deleteDashboard(dashboardId); |
@@ -194,11 +183,33 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -194,11 +183,33 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
194 | details: function() { return $translate.instant('dashboard.make-public') }, | 183 | details: function() { return $translate.instant('dashboard.make-public') }, |
195 | icon: "share", | 184 | icon: "share", |
196 | isEnabled: function(dashboard) { | 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 | dashboardActionsList.push({ | 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 | onAction: function ($event, item) { | 213 | onAction: function ($event, item) { |
203 | assignToCustomer($event, [ item.id.id ]); | 214 | assignToCustomer($event, [ item.id.id ]); |
204 | }, | 215 | }, |
@@ -208,8 +219,8 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -208,8 +219,8 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
208 | isEnabled: function(dashboard) { | 219 | isEnabled: function(dashboard) { |
209 | return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); | 220 | return dashboard && (!dashboard.customerId || dashboard.customerId.id === types.id.nullUid); |
210 | } | 221 | } |
211 | - }); | ||
212 | - dashboardActionsList.push({ | 222 | + });*/ |
223 | + /*dashboardActionsList.push({ | ||
213 | onAction: function ($event, item) { | 224 | onAction: function ($event, item) { |
214 | unassignFromCustomer($event, item, false); | 225 | unassignFromCustomer($event, item, false); |
215 | }, | 226 | }, |
@@ -219,18 +230,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -219,18 +230,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
219 | isEnabled: function(dashboard) { | 230 | isEnabled: function(dashboard) { |
220 | return dashboard && dashboard.customerId && dashboard.customerId.id !== types.id.nullUid && !dashboard.assignedCustomer.isPublic; | 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 | dashboardActionsList.push( | 235 | dashboardActionsList.push( |
236 | { | 236 | { |
@@ -246,7 +246,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -246,7 +246,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
246 | dashboardGroupActionsList.push( | 246 | dashboardGroupActionsList.push( |
247 | { | 247 | { |
248 | onAction: function ($event, items) { | 248 | onAction: function ($event, items) { |
249 | - assignDashboardsToCustomer($event, items); | 249 | + assignDashboardsToCustomers($event, items); |
250 | }, | 250 | }, |
251 | name: function() { return $translate.instant('dashboard.assign-dashboards') }, | 251 | name: function() { return $translate.instant('dashboard.assign-dashboards') }, |
252 | details: function(selectedCount) { | 252 | details: function(selectedCount) { |
@@ -255,6 +255,17 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -255,6 +255,17 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
255 | icon: "assignment_ind" | 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 | dashboardGroupActionsList.push( | 270 | dashboardGroupActionsList.push( |
260 | { | 271 | { |
@@ -290,10 +301,10 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -290,10 +301,10 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
290 | }); | 301 | }); |
291 | } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') { | 302 | } else if (vm.dashboardsScope === 'customer' || vm.dashboardsScope === 'customer_user') { |
292 | fetchDashboardsFunction = function (pageLink) { | 303 | fetchDashboardsFunction = function (pageLink) { |
293 | - return dashboardService.getCustomerDashboards(customerId, pageLink, true); | 304 | + return dashboardService.getCustomerDashboards(customerId, pageLink); |
294 | }; | 305 | }; |
295 | deleteDashboardFunction = function (dashboardId) { | 306 | deleteDashboardFunction = function (dashboardId) { |
296 | - return dashboardService.unassignDashboardFromCustomer(dashboardId); | 307 | + return dashboardService.unassignDashboardFromCustomer(customerId, dashboardId); |
297 | }; | 308 | }; |
298 | refreshDashboardsParamsFunction = function () { | 309 | refreshDashboardsParamsFunction = function () { |
299 | return {"customerId": customerId, "topIndex": vm.topIndex}; | 310 | return {"customerId": customerId, "topIndex": vm.topIndex}; |
@@ -314,26 +325,27 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -314,26 +325,27 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
314 | dashboardActionsList.push( | 325 | dashboardActionsList.push( |
315 | { | 326 | { |
316 | onAction: function ($event, item) { | 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 | isEnabled: function(dashboard) { | 333 | isEnabled: function(dashboard) { |
323 | - return dashboard && !dashboard.assignedCustomer.isPublic; | 334 | + return dashboard && customerId == dashboard.publicCustomerId; |
324 | } | 335 | } |
325 | } | 336 | } |
326 | ); | 337 | ); |
338 | + | ||
327 | dashboardActionsList.push( | 339 | dashboardActionsList.push( |
328 | { | 340 | { |
329 | onAction: function ($event, item) { | 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 | isEnabled: function(dashboard) { | 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,7 +353,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
341 | dashboardGroupActionsList.push( | 353 | dashboardGroupActionsList.push( |
342 | { | 354 | { |
343 | onAction: function ($event, items) { | 355 | onAction: function ($event, items) { |
344 | - unassignDashboardsFromCustomer($event, items); | 356 | + unassignDashboardsFromCustomer($event, items, customerId); |
345 | }, | 357 | }, |
346 | name: function() { return $translate.instant('dashboard.unassign-dashboards') }, | 358 | name: function() { return $translate.instant('dashboard.unassign-dashboards') }, |
347 | details: function(selectedCount) { | 359 | details: function(selectedCount) { |
@@ -351,7 +363,6 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -351,7 +363,6 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
351 | } | 363 | } |
352 | ); | 364 | ); |
353 | 365 | ||
354 | - | ||
355 | vm.dashboardGridConfig.addItemAction = { | 366 | vm.dashboardGridConfig.addItemAction = { |
356 | onAction: function ($event) { | 367 | onAction: function ($event) { |
357 | addDashboardsToCustomer($event); | 368 | addDashboardsToCustomer($event); |
@@ -428,39 +439,42 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -428,39 +439,42 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
428 | return deferred.promise; | 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 | if ($event) { | 463 | if ($event) { |
433 | $event.stopPropagation(); | 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 | function addDashboardsToCustomer($event) { | 480 | function addDashboardsToCustomer($event) { |
@@ -468,7 +482,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -468,7 +482,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
468 | $event.stopPropagation(); | 482 | $event.stopPropagation(); |
469 | } | 483 | } |
470 | var pageSize = 10; | 484 | var pageSize = 10; |
471 | - dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}, false).then( | 485 | + dashboardService.getTenantDashboards({limit: pageSize, textSearch: ''}).then( |
472 | function success(_dashboards) { | 486 | function success(_dashboards) { |
473 | var dashboards = { | 487 | var dashboards = { |
474 | pageSize: pageSize, | 488 | pageSize: pageSize, |
@@ -499,30 +513,13 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -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 | if ($event) { | 517 | if ($event) { |
512 | $event.stopPropagation(); | 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 | var confirm = $mdDialog.confirm() | 523 | var confirm = $mdDialog.confirm() |
527 | .targetEvent($event) | 524 | .targetEvent($event) |
528 | .title(title) | 525 | .title(title) |
@@ -531,7 +528,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -531,7 +528,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
531 | .cancel($translate.instant('action.no')) | 528 | .cancel($translate.instant('action.no')) |
532 | .ok($translate.instant('action.yes')); | 529 | .ok($translate.instant('action.yes')); |
533 | $mdDialog.show(confirm).then(function () { | 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 | vm.grid.refreshList(); | 532 | vm.grid.refreshList(); |
536 | }); | 533 | }); |
537 | }); | 534 | }); |
@@ -556,12 +553,33 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -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 | function exportDashboard($event, dashboard) { | 577 | function exportDashboard($event, dashboard) { |
560 | $event.stopPropagation(); | 578 | $event.stopPropagation(); |
561 | importExport.exportDashboard(dashboard.id.id); | 579 | importExport.exportDashboard(dashboard.id.id); |
562 | } | 580 | } |
563 | 581 | ||
564 | - function unassignDashboardsFromCustomer($event, items) { | 582 | + function unassignDashboardsFromCustomer($event, items, customerId) { |
565 | var confirm = $mdDialog.confirm() | 583 | var confirm = $mdDialog.confirm() |
566 | .targetEvent($event) | 584 | .targetEvent($event) |
567 | .title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat')) | 585 | .title($translate.instant('dashboard.unassign-dashboards-title', {count: items.selectedCount}, 'messageformat')) |
@@ -572,7 +590,7 @@ export function DashboardsController(userService, dashboardService, customerServ | @@ -572,7 +590,7 @@ export function DashboardsController(userService, dashboardService, customerServ | ||
572 | $mdDialog.show(confirm).then(function () { | 590 | $mdDialog.show(confirm).then(function () { |
573 | var tasks = []; | 591 | var tasks = []; |
574 | for (var id in items.selections) { | 592 | for (var id in items.selections) { |
575 | - tasks.push(dashboardService.unassignDashboardFromCustomer(id)); | 593 | + tasks.push(dashboardService.unassignDashboardFromCustomer(customerId, id)); |
576 | } | 594 | } |
577 | $q.all(tasks).then(function () { | 595 | $q.all(tasks).then(function () { |
578 | vm.grid.refreshList(); | 596 | vm.grid.refreshList(); |
@@ -25,10 +25,12 @@ | @@ -25,10 +25,12 @@ | ||
25 | <tb-dashboard-details dashboard="vm.grid.operatingItem()" | 25 | <tb-dashboard-details dashboard="vm.grid.operatingItem()" |
26 | is-edit="vm.grid.detailsConfig.isDetailsEditMode" | 26 | is-edit="vm.grid.detailsConfig.isDetailsEditMode" |
27 | dashboard-scope="vm.dashboardsScope" | 27 | dashboard-scope="vm.dashboardsScope" |
28 | + customer-id="vm.customerId" | ||
28 | the-form="vm.grid.detailsForm" | 29 | the-form="vm.grid.detailsForm" |
29 | - on-assign-to-customer="vm.assignToCustomer(event, [ vm.grid.detailsConfig.currentItem.id.id ])" | ||
30 | on-make-public="vm.makePublic(event, vm.grid.detailsConfig.currentItem)" | 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 | on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)" | 34 | on-export-dashboard="vm.exportDashboard(event, vm.grid.detailsConfig.currentItem)" |
33 | on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details> | 35 | on-delete-dashboard="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-dashboard-details> |
34 | </md-tab> | 36 | </md-tab> |
@@ -40,8 +40,8 @@ import DashboardRoutes from './dashboard.routes'; | @@ -40,8 +40,8 @@ import DashboardRoutes from './dashboard.routes'; | ||
40 | import {DashboardsController, DashboardCardController, MakeDashboardPublicDialogController} from './dashboards.controller'; | 40 | import {DashboardsController, DashboardCardController, MakeDashboardPublicDialogController} from './dashboards.controller'; |
41 | import DashboardController from './dashboard.controller'; | 41 | import DashboardController from './dashboard.controller'; |
42 | import DashboardSettingsController from './dashboard-settings.controller'; | 42 | import DashboardSettingsController from './dashboard-settings.controller'; |
43 | -import AssignDashboardToCustomerController from './assign-to-customer.controller'; | ||
44 | import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller'; | 43 | import AddDashboardsToCustomerController from './add-dashboards-to-customer.controller'; |
44 | +import ManageAssignedCustomersController from './manage-assigned-customers.controller'; | ||
45 | import AddWidgetController from './add-widget.controller'; | 45 | import AddWidgetController from './add-widget.controller'; |
46 | import DashboardDirective from './dashboard.directive'; | 46 | import DashboardDirective from './dashboard.directive'; |
47 | import EditWidgetDirective from './edit-widget.directive'; | 47 | import EditWidgetDirective from './edit-widget.directive'; |
@@ -74,8 +74,8 @@ export default angular.module('thingsboard.dashboard', [ | @@ -74,8 +74,8 @@ export default angular.module('thingsboard.dashboard', [ | ||
74 | .controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController) | 74 | .controller('MakeDashboardPublicDialogController', MakeDashboardPublicDialogController) |
75 | .controller('DashboardController', DashboardController) | 75 | .controller('DashboardController', DashboardController) |
76 | .controller('DashboardSettingsController', DashboardSettingsController) | 76 | .controller('DashboardSettingsController', DashboardSettingsController) |
77 | - .controller('AssignDashboardToCustomerController', AssignDashboardToCustomerController) | ||
78 | .controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController) | 77 | .controller('AddDashboardsToCustomerController', AddDashboardsToCustomerController) |
78 | + .controller('ManageAssignedCustomersController', ManageAssignedCustomersController) | ||
79 | .controller('AddWidgetController', AddWidgetController) | 79 | .controller('AddWidgetController', AddWidgetController) |
80 | .directive('tbDashboardDetails', DashboardDirective) | 80 | .directive('tbDashboardDetails', DashboardDirective) |
81 | .directive('tbEditWidget', EditWidgetDirective) | 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,11 +15,11 @@ | ||
15 | limitations under the License. | 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 | <md-toolbar> | 20 | <md-toolbar> |
21 | <div class="md-toolbar-tools"> | 21 | <div class="md-toolbar-tools"> |
22 | - <h2 translate>dashboard.assign-dashboard-to-customer</h2> | 22 | + <h2 translate>{{vm.titleText}}</h2> |
23 | <span flex></span> | 23 | <span flex></span> |
24 | <md-button class="md-icon-button" ng-click="vm.cancel()"> | 24 | <md-button class="md-icon-button" ng-click="vm.cancel()"> |
25 | <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | 25 | <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> |
@@ -31,42 +31,17 @@ | @@ -31,42 +31,17 @@ | ||
31 | <md-dialog-content> | 31 | <md-dialog-content> |
32 | <div class="md-dialog-content"> | 32 | <div class="md-dialog-content"> |
33 | <fieldset> | 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 | </fieldset> | 38 | </fieldset> |
64 | </div> | 39 | </div> |
65 | </md-dialog-content> | 40 | </md-dialog-content> |
66 | <md-dialog-actions layout="row"> | 41 | <md-dialog-actions layout="row"> |
67 | <span flex></span> | 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 | </md-button> | 45 | </md-button> |
71 | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | | 46 | <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | |
72 | translate }} | 47 | translate }} |
@@ -38,7 +38,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | @@ -38,7 +38,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | ||
38 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { | 38 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { |
39 | limit += scope.excludeEntityIds.length; | 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 | if (result) { | 47 | if (result) { |
43 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { | 48 | if (scope.excludeEntityIds && scope.excludeEntityIds.length) { |
44 | var entities = []; | 49 | var entities = []; |
@@ -71,7 +76,11 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | @@ -71,7 +76,11 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | ||
71 | 76 | ||
72 | ngModelCtrl.$render = function () { | 77 | ngModelCtrl.$render = function () { |
73 | if (ngModelCtrl.$viewValue) { | 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 | function success(entity) { | 84 | function success(entity) { |
76 | scope.entity = entity; | 85 | scope.entity = entity; |
77 | }, | 86 | }, |
@@ -114,55 +123,61 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | @@ -114,55 +123,61 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | ||
114 | scope.selectEntityText = 'asset.select-asset'; | 123 | scope.selectEntityText = 'asset.select-asset'; |
115 | scope.entityText = 'asset.asset'; | 124 | scope.entityText = 'asset.asset'; |
116 | scope.noEntitiesMatchingText = 'asset.no-assets-matching'; | 125 | scope.noEntitiesMatchingText = 'asset.no-assets-matching'; |
117 | - scope.entityRequiredText = 'asset.asset-required' | 126 | + scope.entityRequiredText = 'asset.asset-required'; |
118 | break; | 127 | break; |
119 | case types.entityType.device: | 128 | case types.entityType.device: |
120 | scope.selectEntityText = 'device.select-device'; | 129 | scope.selectEntityText = 'device.select-device'; |
121 | scope.entityText = 'device.device'; | 130 | scope.entityText = 'device.device'; |
122 | scope.noEntitiesMatchingText = 'device.no-devices-matching'; | 131 | scope.noEntitiesMatchingText = 'device.no-devices-matching'; |
123 | - scope.entityRequiredText = 'device.device-required' | 132 | + scope.entityRequiredText = 'device.device-required'; |
124 | break; | 133 | break; |
125 | case types.entityType.rule: | 134 | case types.entityType.rule: |
126 | scope.selectEntityText = 'rule.select-rule'; | 135 | scope.selectEntityText = 'rule.select-rule'; |
127 | scope.entityText = 'rule.rule'; | 136 | scope.entityText = 'rule.rule'; |
128 | scope.noEntitiesMatchingText = 'rule.no-rules-matching'; | 137 | scope.noEntitiesMatchingText = 'rule.no-rules-matching'; |
129 | - scope.entityRequiredText = 'rule.rule-required' | 138 | + scope.entityRequiredText = 'rule.rule-required'; |
130 | break; | 139 | break; |
131 | case types.entityType.plugin: | 140 | case types.entityType.plugin: |
132 | scope.selectEntityText = 'plugin.select-plugin'; | 141 | scope.selectEntityText = 'plugin.select-plugin'; |
133 | scope.entityText = 'plugin.plugin'; | 142 | scope.entityText = 'plugin.plugin'; |
134 | scope.noEntitiesMatchingText = 'plugin.no-plugins-matching'; | 143 | scope.noEntitiesMatchingText = 'plugin.no-plugins-matching'; |
135 | - scope.entityRequiredText = 'plugin.plugin-required' | 144 | + scope.entityRequiredText = 'plugin.plugin-required'; |
136 | break; | 145 | break; |
137 | case types.entityType.tenant: | 146 | case types.entityType.tenant: |
138 | scope.selectEntityText = 'tenant.select-tenant'; | 147 | scope.selectEntityText = 'tenant.select-tenant'; |
139 | scope.entityText = 'tenant.tenant'; | 148 | scope.entityText = 'tenant.tenant'; |
140 | scope.noEntitiesMatchingText = 'tenant.no-tenants-matching'; | 149 | scope.noEntitiesMatchingText = 'tenant.no-tenants-matching'; |
141 | - scope.entityRequiredText = 'tenant.tenant-required' | 150 | + scope.entityRequiredText = 'tenant.tenant-required'; |
142 | break; | 151 | break; |
143 | case types.entityType.customer: | 152 | case types.entityType.customer: |
144 | scope.selectEntityText = 'customer.select-customer'; | 153 | scope.selectEntityText = 'customer.select-customer'; |
145 | scope.entityText = 'customer.customer'; | 154 | scope.entityText = 'customer.customer'; |
146 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; | 155 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; |
147 | - scope.entityRequiredText = 'customer.customer-required' | 156 | + scope.entityRequiredText = 'customer.customer-required'; |
148 | break; | 157 | break; |
149 | case types.entityType.user: | 158 | case types.entityType.user: |
150 | scope.selectEntityText = 'user.select-user'; | 159 | scope.selectEntityText = 'user.select-user'; |
151 | scope.entityText = 'user.user'; | 160 | scope.entityText = 'user.user'; |
152 | scope.noEntitiesMatchingText = 'user.no-users-matching'; | 161 | scope.noEntitiesMatchingText = 'user.no-users-matching'; |
153 | - scope.entityRequiredText = 'user.user-required' | 162 | + scope.entityRequiredText = 'user.user-required'; |
154 | break; | 163 | break; |
155 | case types.entityType.dashboard: | 164 | case types.entityType.dashboard: |
156 | scope.selectEntityText = 'dashboard.select-dashboard'; | 165 | scope.selectEntityText = 'dashboard.select-dashboard'; |
157 | scope.entityText = 'dashboard.dashboard'; | 166 | scope.entityText = 'dashboard.dashboard'; |
158 | scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; | 167 | scope.noEntitiesMatchingText = 'dashboard.no-dashboards-matching'; |
159 | - scope.entityRequiredText = 'dashboard.dashboard-required' | 168 | + scope.entityRequiredText = 'dashboard.dashboard-required'; |
160 | break; | 169 | break; |
161 | case types.entityType.alarm: | 170 | case types.entityType.alarm: |
162 | scope.selectEntityText = 'alarm.select-alarm'; | 171 | scope.selectEntityText = 'alarm.select-alarm'; |
163 | scope.entityText = 'alarm.alarm'; | 172 | scope.entityText = 'alarm.alarm'; |
164 | scope.noEntitiesMatchingText = 'alarm.no-alarms-matching'; | 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 | break; | 181 | break; |
167 | } | 182 | } |
168 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { | 183 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { |
@@ -32,6 +32,7 @@ | @@ -32,6 +32,7 @@ | ||
32 | <tb-entity-select flex | 32 | <tb-entity-select flex |
33 | the-form="theForm" | 33 | the-form="theForm" |
34 | tb-required="true" | 34 | tb-required="true" |
35 | + use-alias-entity-types="true" | ||
35 | ng-model="filter.singleEntity"> | 36 | ng-model="filter.singleEntity"> |
36 | </tb-entity-select> | 37 | </tb-entity-select> |
37 | </section> | 38 | </section> |
@@ -78,6 +79,7 @@ | @@ -78,6 +79,7 @@ | ||
78 | <tb-entity-select flex | 79 | <tb-entity-select flex |
79 | the-form="theForm" | 80 | the-form="theForm" |
80 | tb-required="false" | 81 | tb-required="false" |
82 | + use-alias-entity-types="true" | ||
81 | ng-model="filter.defaultStateEntity"> | 83 | ng-model="filter.defaultStateEntity"> |
82 | </tb-entity-select> | 84 | </tb-entity-select> |
83 | </div> | 85 | </div> |
@@ -123,6 +125,7 @@ | @@ -123,6 +125,7 @@ | ||
123 | the-form="theForm" | 125 | the-form="theForm" |
124 | tb-required="!filter.rootStateEntity" | 126 | tb-required="!filter.rootStateEntity" |
125 | ng-disabled="filter.rootStateEntity" | 127 | ng-disabled="filter.rootStateEntity" |
128 | + use-alias-entity-types="true" | ||
126 | ng-model="filter.rootEntity"> | 129 | ng-model="filter.rootEntity"> |
127 | </tb-entity-select> | 130 | </tb-entity-select> |
128 | </div> | 131 | </div> |
@@ -139,6 +142,7 @@ | @@ -139,6 +142,7 @@ | ||
139 | <tb-entity-select flex | 142 | <tb-entity-select flex |
140 | the-form="theForm" | 143 | the-form="theForm" |
141 | tb-required="false" | 144 | tb-required="false" |
145 | + use-alias-entity-types="true" | ||
142 | ng-model="filter.defaultStateEntity"> | 146 | ng-model="filter.defaultStateEntity"> |
143 | </tb-entity-select> | 147 | </tb-entity-select> |
144 | </div> | 148 | </div> |
@@ -182,6 +186,7 @@ | @@ -182,6 +186,7 @@ | ||
182 | the-form="theForm" | 186 | the-form="theForm" |
183 | tb-required="!filter.rootStateEntity" | 187 | tb-required="!filter.rootStateEntity" |
184 | ng-disabled="filter.rootStateEntity" | 188 | ng-disabled="filter.rootStateEntity" |
189 | + use-alias-entity-types="true" | ||
185 | ng-model="filter.rootEntity"> | 190 | ng-model="filter.rootEntity"> |
186 | </tb-entity-select> | 191 | </tb-entity-select> |
187 | </div> | 192 | </div> |
@@ -198,6 +203,7 @@ | @@ -198,6 +203,7 @@ | ||
198 | <tb-entity-select flex | 203 | <tb-entity-select flex |
199 | the-form="theForm" | 204 | the-form="theForm" |
200 | tb-required="false" | 205 | tb-required="false" |
206 | + use-alias-entity-types="true" | ||
201 | ng-model="filter.defaultStateEntity"> | 207 | ng-model="filter.defaultStateEntity"> |
202 | </tb-entity-select> | 208 | </tb-entity-select> |
203 | </div> | 209 | </div> |
@@ -249,6 +255,7 @@ | @@ -249,6 +255,7 @@ | ||
249 | the-form="theForm" | 255 | the-form="theForm" |
250 | tb-required="!filter.rootStateEntity" | 256 | tb-required="!filter.rootStateEntity" |
251 | ng-disabled="filter.rootStateEntity" | 257 | ng-disabled="filter.rootStateEntity" |
258 | + use-alias-entity-types="true" | ||
252 | ng-model="filter.rootEntity"> | 259 | ng-model="filter.rootEntity"> |
253 | </tb-entity-select> | 260 | </tb-entity-select> |
254 | </div> | 261 | </div> |
@@ -265,6 +272,7 @@ | @@ -265,6 +272,7 @@ | ||
265 | <tb-entity-select flex | 272 | <tb-entity-select flex |
266 | the-form="theForm" | 273 | the-form="theForm" |
267 | tb-required="false" | 274 | tb-required="false" |
275 | + use-alias-entity-types="true" | ||
268 | ng-model="filter.defaultStateEntity"> | 276 | ng-model="filter.defaultStateEntity"> |
269 | </tb-entity-select> | 277 | </tb-entity-select> |
270 | </div> | 278 | </div> |
@@ -105,7 +105,8 @@ export default function EntitySelect($compile, $templateCache) { | @@ -105,7 +105,8 @@ export default function EntitySelect($compile, $templateCache) { | ||
105 | scope: { | 105 | scope: { |
106 | theForm: '=?', | 106 | theForm: '=?', |
107 | tbRequired: '=?', | 107 | tbRequired: '=?', |
108 | - disabled:'=ngDisabled' | 108 | + disabled:'=ngDisabled', |
109 | + useAliasEntityTypes: "=?" | ||
109 | } | 110 | } |
110 | }; | 111 | }; |
111 | } | 112 | } |
@@ -20,6 +20,7 @@ | @@ -20,6 +20,7 @@ | ||
20 | the-form="theForm" | 20 | the-form="theForm" |
21 | ng-disabled="disabled" | 21 | ng-disabled="disabled" |
22 | tb-required="tbRequired" | 22 | tb-required="tbRequired" |
23 | + use-alias-entity-types="useAliasEntityTypes" | ||
23 | ng-model="model.entityType"> | 24 | ng-model="model.entityType"> |
24 | </tb-entity-type-select> | 25 | </tb-entity-type-select> |
25 | <tb-entity-autocomplete flex ng-if="model.entityType" | 26 | <tb-entity-autocomplete flex ng-if="model.entityType" |
@@ -39,7 +39,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity | @@ -39,7 +39,7 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity | ||
39 | 39 | ||
40 | scope.ngModelCtrl = ngModelCtrl; | 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 | scope.typeName = function(type) { | 44 | scope.typeName = function(type) { |
45 | return type ? types.entityTypeTranslations[type].type : ''; | 45 | return type ? types.entityTypeTranslations[type].type : ''; |
@@ -79,7 +79,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity | @@ -79,7 +79,8 @@ export default function EntityTypeSelect($compile, $templateCache, utils, entity | ||
79 | theForm: '=?', | 79 | theForm: '=?', |
80 | tbRequired: '=?', | 80 | tbRequired: '=?', |
81 | disabled:'=ngDisabled', | 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,7 +540,7 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | ||
540 | function success(dashboard) { | 540 | function success(dashboard) { |
541 | var name = dashboard.title; | 541 | var name = dashboard.title; |
542 | name = name.toLowerCase().replace(/\W/g,"_"); | 542 | name = name.toLowerCase().replace(/\W/g,"_"); |
543 | - exportToPc(prepareExport(dashboard), name + '.json'); | 543 | + exportToPc(prepareDashboardExport(dashboard), name + '.json'); |
544 | }, | 544 | }, |
545 | function fail(rejection) { | 545 | function fail(rejection) { |
546 | var message = rejection; | 546 | var message = rejection; |
@@ -552,6 +552,15 @@ export default function ImportExport($log, $translate, $q, $mdDialog, $document, | @@ -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 | function importDashboard($event) { | 564 | function importDashboard($event) { |
556 | var deferred = $q.defer(); | 565 | var deferred = $q.defer(); |
557 | openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then( | 566 | openImportDialog($event, 'dashboard.import', 'dashboard.dashboard-file').then( |
@@ -383,7 +383,10 @@ export default angular.module('thingsboard.locale', []) | @@ -383,7 +383,10 @@ export default angular.module('thingsboard.locale', []) | ||
383 | "idCopiedMessage": "Customer Id has been copied to clipboard", | 383 | "idCopiedMessage": "Customer Id has been copied to clipboard", |
384 | "select-customer": "Select customer", | 384 | "select-customer": "Select customer", |
385 | "no-customers-matching": "No customers matching '{{entity}}' were found.", | 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 | "datetime": { | 391 | "datetime": { |
389 | "date-from": "Date from", | 392 | "date-from": "Date from", |
@@ -404,6 +407,12 @@ export default angular.module('thingsboard.locale', []) | @@ -404,6 +407,12 @@ export default angular.module('thingsboard.locale', []) | ||
404 | "unassign-from-customer": "Unassign from customer", | 407 | "unassign-from-customer": "Unassign from customer", |
405 | "make-public": "Make dashboard public", | 408 | "make-public": "Make dashboard public", |
406 | "make-private": "Make dashboard private", | 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 | "no-dashboards-text": "No dashboards found", | 416 | "no-dashboards-text": "No dashboards found", |
408 | "no-widgets": "No widgets configured", | 417 | "no-widgets": "No widgets configured", |
409 | "add-widget": "Add new widget", | 418 | "add-widget": "Add new widget", |
@@ -418,7 +427,8 @@ export default angular.module('thingsboard.locale', []) | @@ -418,7 +427,8 @@ export default angular.module('thingsboard.locale', []) | ||
418 | "add-dashboard-text": "Add new dashboard", | 427 | "add-dashboard-text": "Add new dashboard", |
419 | "assign-dashboards": "Assign dashboards", | 428 | "assign-dashboards": "Assign dashboards", |
420 | "assign-new-dashboard": "Assign new dashboard", | 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 | "delete-dashboards": "Delete dashboards", | 432 | "delete-dashboards": "Delete dashboards", |
423 | "unassign-dashboards": "Unassign dashboards", | 433 | "unassign-dashboards": "Unassign dashboards", |
424 | "unassign-dashboards-action-title": "Unassign { count, select, 1 {1 dashboard} other {# dashboards} } from customer", | 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,6 +510,7 @@ export default angular.module('thingsboard.locale', []) | ||
500 | "Please contact your administrator in order to resolve this issue.", | 510 | "Please contact your administrator in order to resolve this issue.", |
501 | "select-devices": "Select devices", | 511 | "select-devices": "Select devices", |
502 | "assignedToCustomer": "Assigned to customer", | 512 | "assignedToCustomer": "Assigned to customer", |
513 | + "assignedToCustomers": "Assigned to customers", | ||
503 | "public": "Public", | 514 | "public": "Public", |
504 | "public-link": "Public link", | 515 | "public-link": "Public link", |
505 | "copy-public-link": "Copy public link", | 516 | "copy-public-link": "Copy public link", |
@@ -735,6 +746,7 @@ export default angular.module('thingsboard.locale', []) | @@ -735,6 +746,7 @@ export default angular.module('thingsboard.locale', []) | ||
735 | "type-alarms": "Alarms", | 746 | "type-alarms": "Alarms", |
736 | "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", | 747 | "list-of-alarms": "{ count, select, 1 {One alarms} other {List of # alarms} }", |
737 | "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", | 748 | "alarm-name-starts-with": "Alarms whose names start with '{{prefix}}'", |
749 | + "type-current-customer": "Current Customer", | ||
738 | "search": "Search entities", | 750 | "search": "Search entities", |
739 | "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", | 751 | "selected-entities": "{ count, select, 1 {1 entity} other {# entities} } selected", |
740 | "entity-name": "Entity name", | 752 | "entity-name": "Entity name", |