Showing
44 changed files
with
1983 additions
and
7 deletions
1 | +-- | ||
2 | +-- Copyright © 2016-2019 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 | +CREATE TABLE IF NOT EXISTS thingsboard.edge ( | ||
18 | + id timeuuid, | ||
19 | + tenant_id timeuuid, | ||
20 | + name text, | ||
21 | + search_text text, | ||
22 | + configuration text, | ||
23 | + additional_info text, | ||
24 | + PRIMARY KEY (id, tenant_id) | ||
25 | +); | ||
26 | + | ||
27 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_search_text AS | ||
28 | + SELECT * | ||
29 | + from thingsboard.edge | ||
30 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
31 | + PRIMARY KEY ( tenant_id, search_text, id ) | ||
32 | + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); |
1 | +-- | ||
2 | +-- Copyright © 2016-2019 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 | +CREATE TABLE IF NOT EXISTS edge ( | ||
18 | + id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, | ||
19 | + additional_info varchar, | ||
20 | + configuration varchar(10000000), | ||
21 | + name varchar(255), | ||
22 | + search_text varchar(255), | ||
23 | + tenant_id varchar(31) | ||
24 | +); |
@@ -43,12 +43,14 @@ import org.thingsboard.server.common.data.alarm.AlarmId; | @@ -43,12 +43,14 @@ import org.thingsboard.server.common.data.alarm.AlarmId; | ||
43 | import org.thingsboard.server.common.data.alarm.AlarmInfo; | 43 | import org.thingsboard.server.common.data.alarm.AlarmInfo; |
44 | import org.thingsboard.server.common.data.asset.Asset; | 44 | import org.thingsboard.server.common.data.asset.Asset; |
45 | import org.thingsboard.server.common.data.audit.ActionType; | 45 | import org.thingsboard.server.common.data.audit.ActionType; |
46 | +import org.thingsboard.server.common.data.edge.Edge; | ||
46 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | 47 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
47 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 48 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
48 | import org.thingsboard.server.common.data.id.AssetId; | 49 | import org.thingsboard.server.common.data.id.AssetId; |
49 | import org.thingsboard.server.common.data.id.CustomerId; | 50 | import org.thingsboard.server.common.data.id.CustomerId; |
50 | import org.thingsboard.server.common.data.id.DashboardId; | 51 | import org.thingsboard.server.common.data.id.DashboardId; |
51 | import org.thingsboard.server.common.data.id.DeviceId; | 52 | import org.thingsboard.server.common.data.id.DeviceId; |
53 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
52 | import org.thingsboard.server.common.data.id.EntityId; | 54 | import org.thingsboard.server.common.data.id.EntityId; |
53 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 55 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
54 | import org.thingsboard.server.common.data.id.EntityViewId; | 56 | import org.thingsboard.server.common.data.id.EntityViewId; |
@@ -82,6 +84,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; | @@ -82,6 +84,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; | ||
82 | import org.thingsboard.server.dao.device.ClaimDevicesService; | 84 | import org.thingsboard.server.dao.device.ClaimDevicesService; |
83 | import org.thingsboard.server.dao.device.DeviceCredentialsService; | 85 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
84 | import org.thingsboard.server.dao.device.DeviceService; | 86 | import org.thingsboard.server.dao.device.DeviceService; |
87 | +import org.thingsboard.server.dao.edge.EdgeService; | ||
85 | import org.thingsboard.server.dao.entityview.EntityViewService; | 88 | import org.thingsboard.server.dao.entityview.EntityViewService; |
86 | import org.thingsboard.server.dao.exception.DataValidationException; | 89 | import org.thingsboard.server.dao.exception.DataValidationException; |
87 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 90 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
@@ -185,6 +188,9 @@ public abstract class BaseController { | @@ -185,6 +188,9 @@ public abstract class BaseController { | ||
185 | @Autowired | 188 | @Autowired |
186 | protected ClaimDevicesService claimDevicesService; | 189 | protected ClaimDevicesService claimDevicesService; |
187 | 190 | ||
191 | + @Autowired | ||
192 | + protected EdgeService edgeService; | ||
193 | + | ||
188 | @Value("${server.log_controller_error_stack_trace}") | 194 | @Value("${server.log_controller_error_stack_trace}") |
189 | @Getter | 195 | @Getter |
190 | private boolean logControllerErrorStackTrace; | 196 | private boolean logControllerErrorStackTrace; |
@@ -458,6 +464,18 @@ public abstract class BaseController { | @@ -458,6 +464,18 @@ public abstract class BaseController { | ||
458 | } | 464 | } |
459 | } | 465 | } |
460 | 466 | ||
467 | + Edge checkEdgeId(EdgeId edgeId, Operation operation) throws ThingsboardException { | ||
468 | + try { | ||
469 | + validateId(edgeId, "Incorrect edgeId " + edgeId); | ||
470 | + Edge edge = edgeService.findEdgeById(getTenantId(), edgeId); | ||
471 | + checkNotNull(edge); | ||
472 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge); | ||
473 | + return edge; | ||
474 | + } catch (Exception e) { | ||
475 | + throw handleException(e, false); | ||
476 | + } | ||
477 | + } | ||
478 | + | ||
461 | DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException { | 479 | DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException { |
462 | try { | 480 | try { |
463 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); | 481 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
@@ -513,7 +531,6 @@ public abstract class BaseController { | @@ -513,7 +531,6 @@ public abstract class BaseController { | ||
513 | return ruleNode; | 531 | return ruleNode; |
514 | } | 532 | } |
515 | 533 | ||
516 | - | ||
517 | protected String constructBaseUrl(HttpServletRequest request) { | 534 | protected String constructBaseUrl(HttpServletRequest request) { |
518 | String scheme = request.getScheme(); | 535 | String scheme = request.getScheme(); |
519 | if (request.getHeader("x-forwarded-proto") != null) { | 536 | if (request.getHeader("x-forwarded-proto") != null) { |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.controller; | ||
17 | + | ||
18 | +import org.springframework.http.HttpStatus; | ||
19 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
20 | +import org.springframework.web.bind.annotation.PathVariable; | ||
21 | +import org.springframework.web.bind.annotation.RequestBody; | ||
22 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
23 | +import org.springframework.web.bind.annotation.RequestMethod; | ||
24 | +import org.springframework.web.bind.annotation.RequestParam; | ||
25 | +import org.springframework.web.bind.annotation.ResponseBody; | ||
26 | +import org.springframework.web.bind.annotation.ResponseStatus; | ||
27 | +import org.springframework.web.bind.annotation.RestController; | ||
28 | +import org.thingsboard.server.common.data.EntityType; | ||
29 | +import org.thingsboard.server.common.data.audit.ActionType; | ||
30 | +import org.thingsboard.server.common.data.edge.Edge; | ||
31 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
32 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
33 | +import org.thingsboard.server.common.data.id.TenantId; | ||
34 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
35 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
36 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
37 | +import org.thingsboard.server.service.security.permission.Operation; | ||
38 | +import org.thingsboard.server.service.security.permission.Resource; | ||
39 | + | ||
40 | +import java.util.ArrayList; | ||
41 | +import java.util.List; | ||
42 | +import java.util.stream.Collectors; | ||
43 | + | ||
44 | +@RestController | ||
45 | +@RequestMapping("/api") | ||
46 | +public class EdgeController extends BaseController { | ||
47 | + | ||
48 | + private static final String EDGE_ID = "edgeId"; | ||
49 | + | ||
50 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
51 | + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET) | ||
52 | + @ResponseBody | ||
53 | + public Edge getEdgeById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | ||
54 | + checkParameter(EDGE_ID, strEdgeId); | ||
55 | + try { | ||
56 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | ||
57 | + return checkEdgeId(edgeId, Operation.READ); | ||
58 | + } catch (Exception e) { | ||
59 | + throw handleException(e); | ||
60 | + } | ||
61 | + } | ||
62 | + | ||
63 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
64 | + @RequestMapping(value = "/edge", method = RequestMethod.POST) | ||
65 | + @ResponseBody | ||
66 | + public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { | ||
67 | + try { | ||
68 | + edge.setTenantId(getCurrentUser().getTenantId()); | ||
69 | + boolean created = edge.getId() == null; | ||
70 | + | ||
71 | + Operation operation = created ? Operation.CREATE : Operation.WRITE; | ||
72 | + | ||
73 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, | ||
74 | + edge.getId(), edge); | ||
75 | + | ||
76 | + Edge result = checkNotNull(edgeService.saveEdge(edge)); | ||
77 | + | ||
78 | + logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null); | ||
79 | + return result; | ||
80 | + } catch (Exception e) { | ||
81 | + logEntityAction(emptyId(EntityType.EDGE), edge, | ||
82 | + null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); | ||
83 | + throw handleException(e); | ||
84 | + } | ||
85 | + } | ||
86 | + | ||
87 | + | ||
88 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
89 | + @RequestMapping(value = "/edges", params = {"limit"}, method = RequestMethod.GET) | ||
90 | + @ResponseBody | ||
91 | + public TextPageData<Edge> getEdges( | ||
92 | + @RequestParam int limit, | ||
93 | + @RequestParam(required = false) String textSearch, | ||
94 | + @RequestParam(required = false) String idOffset, | ||
95 | + @RequestParam(required = false) String textOffset) throws ThingsboardException { | ||
96 | + try { | ||
97 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.READ); | ||
98 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
99 | + TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); | ||
100 | + return checkNotNull(edgeService.findTenantEdges(tenantId, pageLink)); | ||
101 | + } catch (Exception e) { | ||
102 | + throw handleException(e); | ||
103 | + } | ||
104 | + } | ||
105 | + | ||
106 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
107 | + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE) | ||
108 | + @ResponseStatus(value = HttpStatus.OK) | ||
109 | + public void deleteEdge(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { | ||
110 | + checkParameter(EDGE_ID, strEdgeId); | ||
111 | + try { | ||
112 | + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); | ||
113 | + Edge edge = checkEdgeId(edgeId, Operation.DELETE); | ||
114 | + edgeService.deleteEdge(getTenantId(), edgeId); | ||
115 | + | ||
116 | + logEntityAction(edgeId, edge, | ||
117 | + null, | ||
118 | + ActionType.DELETED, null, strEdgeId); | ||
119 | + | ||
120 | + } catch (Exception e) { | ||
121 | + | ||
122 | + logEntityAction(emptyId(EntityType.EDGE), | ||
123 | + null, | ||
124 | + null, | ||
125 | + ActionType.DELETED, e, strEdgeId); | ||
126 | + | ||
127 | + throw handleException(e); | ||
128 | + } | ||
129 | + } | ||
130 | + | ||
131 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
132 | + @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET) | ||
133 | + @ResponseBody | ||
134 | + public List<Edge> getEdgesByIds( | ||
135 | + @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException { | ||
136 | + checkArrayParameter("edgeIds", strEdgeIds); | ||
137 | + try { | ||
138 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.READ); | ||
139 | + SecurityUser user = getCurrentUser(); | ||
140 | + TenantId tenantId = user.getTenantId(); | ||
141 | + List<EdgeId> edgeIds = new ArrayList<>(); | ||
142 | + for (String strEdgeId : strEdgeIds) { | ||
143 | + edgeIds.add(new EdgeId(toUUID(strEdgeId))); | ||
144 | + } | ||
145 | + List<Edge> edges = checkNotNull(edgeService.findEdgesByIdsAsync(tenantId, edgeIds).get()); | ||
146 | + return filterEdgesByReadPermission(edges); | ||
147 | + } catch (Exception e) { | ||
148 | + throw handleException(e); | ||
149 | + } | ||
150 | + } | ||
151 | + | ||
152 | + private List<Edge> filterEdgesByReadPermission(List<Edge> edges) { | ||
153 | + return edges.stream().filter(edge -> { | ||
154 | + try { | ||
155 | + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.READ, edge.getId(), edge); | ||
156 | + return true; | ||
157 | + } catch (ThingsboardException e) { | ||
158 | + return false; | ||
159 | + } | ||
160 | + }).collect(Collectors.toList()); | ||
161 | + } | ||
162 | + | ||
163 | +} |
@@ -119,6 +119,11 @@ public class ThingsboardInstallService { | @@ -119,6 +119,11 @@ public class ThingsboardInstallService { | ||
119 | case "2.4.0": | 119 | case "2.4.0": |
120 | log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ..."); | 120 | log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ..."); |
121 | 121 | ||
122 | + case "2.4.2": | ||
123 | + log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.x ..."); | ||
124 | + | ||
125 | + databaseUpgradeService.upgradeDatabase("2.4.2"); | ||
126 | + | ||
122 | log.info("Updating system data..."); | 127 | log.info("Updating system data..."); |
123 | 128 | ||
124 | systemDataLoaderService.deleteSystemWidgetBundle("charts"); | 129 | systemDataLoaderService.deleteSystemWidgetBundle("charts"); |
@@ -267,6 +267,14 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -267,6 +267,14 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { | ||
267 | } catch (InvalidQueryException e) {} | 267 | } catch (InvalidQueryException e) {} |
268 | log.info("Schema updated."); | 268 | log.info("Schema updated."); |
269 | break; | 269 | break; |
270 | + case "2.4.2": | ||
271 | + | ||
272 | + log.info("Updating schema ..."); | ||
273 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.x", SCHEMA_UPDATE_CQL); | ||
274 | + loadCql(schemaUpdateFile); | ||
275 | + log.info("Schema updated."); | ||
276 | + | ||
277 | + break; | ||
270 | default: | 278 | default: |
271 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | 279 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
272 | } | 280 | } |
@@ -176,6 +176,14 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | @@ -176,6 +176,14 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { | ||
176 | log.info("Schema updated."); | 176 | log.info("Schema updated."); |
177 | } | 177 | } |
178 | break; | 178 | break; |
179 | + case "2.4.2": | ||
180 | + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | ||
181 | + log.info("Updating schema ..."); | ||
182 | + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.x", SCHEMA_UPDATE_SQL); | ||
183 | + loadSql(schemaUpdateFile, conn); | ||
184 | + log.info("Schema updated."); | ||
185 | + } | ||
186 | + break; | ||
179 | default: | 187 | default: |
180 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 188 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
181 | } | 189 | } |
@@ -31,7 +31,8 @@ public enum Resource { | @@ -31,7 +31,8 @@ public enum Resource { | ||
31 | RULE_CHAIN(EntityType.RULE_CHAIN), | 31 | RULE_CHAIN(EntityType.RULE_CHAIN), |
32 | USER(EntityType.USER), | 32 | USER(EntityType.USER), |
33 | WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), | 33 | WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), |
34 | - WIDGET_TYPE(EntityType.WIDGET_TYPE); | 34 | + WIDGET_TYPE(EntityType.WIDGET_TYPE), |
35 | + EDGE(EntityType.EDGE); | ||
35 | 36 | ||
36 | private final EntityType entityType; | 37 | private final EntityType entityType; |
37 | 38 |
@@ -42,6 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions { | @@ -42,6 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions { | ||
42 | put(Resource.USER, userPermissionChecker); | 42 | put(Resource.USER, userPermissionChecker); |
43 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); | 43 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); |
44 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); | 44 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); |
45 | + put(Resource.EDGE, tenantEntityPermissionChecker); | ||
45 | } | 46 | } |
46 | 47 | ||
47 | public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { | 48 | public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.controller; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.core.type.TypeReference; | ||
19 | +import org.apache.commons.lang3.RandomStringUtils; | ||
20 | +import org.junit.After; | ||
21 | +import org.junit.Assert; | ||
22 | +import org.junit.Before; | ||
23 | +import org.junit.Test; | ||
24 | +import org.thingsboard.server.common.data.Tenant; | ||
25 | +import org.thingsboard.server.common.data.User; | ||
26 | +import org.thingsboard.server.common.data.edge.Edge; | ||
27 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
28 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
29 | +import org.thingsboard.server.common.data.security.Authority; | ||
30 | + | ||
31 | +import java.util.ArrayList; | ||
32 | +import java.util.Collections; | ||
33 | +import java.util.List; | ||
34 | + | ||
35 | +import static org.hamcrest.Matchers.containsString; | ||
36 | +import static org.junit.Assert.assertEquals; | ||
37 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
38 | + | ||
39 | +public abstract class BaseEdgeControllerTest extends AbstractControllerTest { | ||
40 | + | ||
41 | + private IdComparator<Edge> idComparator; | ||
42 | + private Tenant savedTenant; | ||
43 | + private User tenantAdmin; | ||
44 | + | ||
45 | + @Before | ||
46 | + public void beforeTest() throws Exception { | ||
47 | + loginSysAdmin(); | ||
48 | + idComparator = new IdComparator<>(); | ||
49 | + | ||
50 | + savedTenant = doPost("/api/tenant", getNewTenant("My tenant"), Tenant.class); | ||
51 | + Assert.assertNotNull(savedTenant); | ||
52 | + | ||
53 | + tenantAdmin = new User(); | ||
54 | + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); | ||
55 | + tenantAdmin.setTenantId(savedTenant.getId()); | ||
56 | + tenantAdmin.setEmail("tenant2@thingsboard.org"); | ||
57 | + tenantAdmin.setFirstName("Joe"); | ||
58 | + tenantAdmin.setLastName("Downs"); | ||
59 | + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); | ||
60 | + | ||
61 | + } | ||
62 | + | ||
63 | + @After | ||
64 | + public void afterTest() throws Exception { | ||
65 | + loginSysAdmin(); | ||
66 | + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) | ||
67 | + .andExpect(status().isOk()); | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + public void testFindEdgeById() throws Exception { | ||
72 | + Edge savedEdge = getNewSavedEdge("Test edge"); | ||
73 | + Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); | ||
74 | + Assert.assertNotNull(foundEdge); | ||
75 | + assertEquals(savedEdge, foundEdge); | ||
76 | + } | ||
77 | + | ||
78 | + @Test | ||
79 | + public void testSaveEdge() throws Exception { | ||
80 | + Edge savedEdge = getNewSavedEdge("Test edge"); | ||
81 | + | ||
82 | + Assert.assertNotNull(savedEdge); | ||
83 | + Assert.assertNotNull(savedEdge.getId()); | ||
84 | + Assert.assertTrue(savedEdge.getCreatedTime() > 0); | ||
85 | + assertEquals(savedTenant.getId(), savedEdge.getTenantId()); | ||
86 | + | ||
87 | + savedEdge.setName("New test edge"); | ||
88 | + doPost("/api/edge", savedEdge, Edge.class); | ||
89 | + Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); | ||
90 | + | ||
91 | + assertEquals(foundEdge.getName(), savedEdge.getName()); | ||
92 | + } | ||
93 | + | ||
94 | + @Test | ||
95 | + public void testDeleteEdge() throws Exception { | ||
96 | + Edge edge = getNewSavedEdge("Test edge"); | ||
97 | + Edge savedEdge = doPost("/api/edge", edge, Edge.class); | ||
98 | + | ||
99 | + doDelete("/api/edge/" + savedEdge.getId().getId().toString()) | ||
100 | + .andExpect(status().isOk()); | ||
101 | + | ||
102 | + doGet("/api/edge/" + savedEdge.getId().getId().toString()) | ||
103 | + .andExpect(status().isNotFound()); | ||
104 | + } | ||
105 | + | ||
106 | + @Test | ||
107 | + public void testSaveEdgeWithEmptyName() throws Exception { | ||
108 | + Edge edge = new Edge(); | ||
109 | + doPost("/api/edge", edge) | ||
110 | + .andExpect(status().isBadRequest()) | ||
111 | + .andExpect(statusReason(containsString("Edge name should be specified!"))); | ||
112 | + } | ||
113 | + | ||
114 | + @Test | ||
115 | + public void testGetEdges() throws Exception { | ||
116 | + | ||
117 | + List<Edge> edges = new ArrayList<>(); | ||
118 | + for (int i = 0; i < 178; i++) { | ||
119 | + edges.add(getNewSavedEdge("Test edge " + i)); | ||
120 | + } | ||
121 | + List<Edge> loadedEdges = loadListOf(new TextPageLink(23), "/api/edges?"); | ||
122 | + | ||
123 | + Collections.sort(edges, idComparator); | ||
124 | + Collections.sort(loadedEdges, idComparator); | ||
125 | + | ||
126 | + assertEquals(edges, loadedEdges); | ||
127 | + } | ||
128 | + | ||
129 | + @Test | ||
130 | + public void testGetEdgesByName() throws Exception { | ||
131 | + String name1 = "Entity edge1"; | ||
132 | + List<Edge> namesOfEdge1 = fillListOf(143, name1); | ||
133 | + List<Edge> loadedNamesOfEdge1 = loadListOf(new TextPageLink(15, name1), "/api/edges?"); | ||
134 | + Collections.sort(namesOfEdge1, idComparator); | ||
135 | + Collections.sort(loadedNamesOfEdge1, idComparator); | ||
136 | + assertEquals(namesOfEdge1, loadedNamesOfEdge1); | ||
137 | + | ||
138 | + String name2 = "Entity edge2"; | ||
139 | + List<Edge> namesOfEdge2 = fillListOf(75, name2); | ||
140 | + List<Edge> loadedNamesOfEdge2 = loadListOf(new TextPageLink(4, name2), "/api/edges?"); | ||
141 | + Collections.sort(namesOfEdge2, idComparator); | ||
142 | + Collections.sort(loadedNamesOfEdge2, idComparator); | ||
143 | + assertEquals(namesOfEdge2, loadedNamesOfEdge2); | ||
144 | + | ||
145 | + for (Edge edge : loadedNamesOfEdge1) { | ||
146 | + doDelete("/api/edge/" + edge.getId().getId().toString()).andExpect(status().isOk()); | ||
147 | + } | ||
148 | + TextPageData<Edge> pageData = doGetTypedWithPageLink("/api/edges?", | ||
149 | + new TypeReference<TextPageData<Edge>>() { | ||
150 | + }, new TextPageLink(4, name1)); | ||
151 | + Assert.assertFalse(pageData.hasNext()); | ||
152 | + assertEquals(0, pageData.getData().size()); | ||
153 | + | ||
154 | + for (Edge edge : loadedNamesOfEdge2) { | ||
155 | + doDelete("/api/edge/" + edge.getId().getId().toString()).andExpect(status().isOk()); | ||
156 | + } | ||
157 | + pageData = doGetTypedWithPageLink("/api/edges?", new TypeReference<TextPageData<Edge>>() { | ||
158 | + }, new TextPageLink(4, name2)); | ||
159 | + Assert.assertFalse(pageData.hasNext()); | ||
160 | + assertEquals(0, pageData.getData().size()); | ||
161 | + } | ||
162 | + | ||
163 | + private Edge getNewSavedEdge(String name) throws Exception { | ||
164 | + Edge edge = createEdge(name); | ||
165 | + return doPost("/api/edge", edge, Edge.class); | ||
166 | + } | ||
167 | + | ||
168 | + private Edge createEdge(String name) { | ||
169 | + Edge edge = new Edge(); | ||
170 | + edge.setTenantId(savedTenant.getId()); | ||
171 | + edge.setName(name); | ||
172 | + return edge; | ||
173 | + } | ||
174 | + | ||
175 | + private Tenant getNewTenant(String title) { | ||
176 | + Tenant tenant = new Tenant(); | ||
177 | + tenant.setTitle(title); | ||
178 | + return tenant; | ||
179 | + } | ||
180 | + | ||
181 | + private List<Edge> fillListOf(int limit, String partOfName) throws Exception { | ||
182 | + List<Edge> edgeNames = new ArrayList<>(); | ||
183 | + for (int i = 0; i < limit; i++) { | ||
184 | + String fullName = partOfName + ' ' + RandomStringUtils.randomAlphanumeric(15); | ||
185 | + fullName = i % 2 == 0 ? fullName.toLowerCase() : fullName.toUpperCase(); | ||
186 | + Edge edge = getNewSavedEdge(fullName); | ||
187 | + edgeNames.add(doPost("/api/edge", edge, Edge.class)); | ||
188 | + } | ||
189 | + return edgeNames; | ||
190 | + } | ||
191 | + | ||
192 | + private List<Edge> loadListOf(TextPageLink pageLink, String urlTemplate) throws Exception { | ||
193 | + List<Edge> loadedItems = new ArrayList<>(); | ||
194 | + TextPageData<Edge> pageData; | ||
195 | + do { | ||
196 | + pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<TextPageData<Edge>>() { | ||
197 | + }, pageLink); | ||
198 | + loadedItems.addAll(pageData.getData()); | ||
199 | + if (pageData.hasNext()) { | ||
200 | + pageLink = pageData.getNextPageLink(); | ||
201 | + } | ||
202 | + } while (pageData.hasNext()); | ||
203 | + | ||
204 | + return loadedItems; | ||
205 | + } | ||
206 | +} |
application/src/test/java/org/thingsboard/server/controller/nosql/EdgeControllerNoSqlTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.controller.nosql; | ||
17 | + | ||
18 | +import org.thingsboard.server.controller.BaseEdgeControllerTest; | ||
19 | +import org.thingsboard.server.dao.service.DaoNoSqlTest; | ||
20 | + | ||
21 | +@DaoNoSqlTest | ||
22 | +public class EdgeControllerNoSqlTest extends BaseEdgeControllerTest { | ||
23 | +} |
application/src/test/java/org/thingsboard/server/controller/sql/EdgeControllerSqlTest.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.controller.sql; | ||
17 | + | ||
18 | +import org.thingsboard.server.controller.BaseEdgeControllerTest; | ||
19 | +import org.thingsboard.server.dao.service.DaoSqlTest; | ||
20 | + | ||
21 | +@DaoSqlTest | ||
22 | +public class EdgeControllerSqlTest extends BaseEdgeControllerTest { | ||
23 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.edge; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | +import org.thingsboard.server.common.data.edge.Edge; | ||
20 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
21 | +import org.thingsboard.server.common.data.id.TenantId; | ||
22 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
23 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
24 | + | ||
25 | +import java.util.List; | ||
26 | + | ||
27 | +public interface EdgeService { | ||
28 | + | ||
29 | + Edge saveEdge(Edge edge); | ||
30 | + | ||
31 | + Edge findEdgeById(TenantId tenantId, EdgeId edgeId); | ||
32 | + | ||
33 | + ListenableFuture<Edge> findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId); | ||
34 | + | ||
35 | + ListenableFuture<List<Edge>> findEdgesByIdsAsync(TenantId tenantId, List<EdgeId> edgeIds); | ||
36 | + | ||
37 | + List<Edge> findAllEdges(TenantId tenantId); | ||
38 | + | ||
39 | + TextPageData<Edge> findTenantEdges(TenantId tenantId, TextPageLink pageLink); | ||
40 | + | ||
41 | + void deleteEdge(TenantId tenantId, EdgeId edgeId); | ||
42 | + | ||
43 | + void deleteEdgesByTenantId(TenantId tenantId); | ||
44 | + | ||
45 | +} |
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; | @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; | ||
19 | * @author Andrew Shvayka | 19 | * @author Andrew Shvayka |
20 | */ | 20 | */ |
21 | public enum EntityType { | 21 | public enum EntityType { |
22 | - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE | 22 | + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, EDGE |
23 | } | 23 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.edge; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import lombok.EqualsAndHashCode; | ||
20 | +import lombok.Getter; | ||
21 | +import lombok.Setter; | ||
22 | +import lombok.ToString; | ||
23 | +import org.thingsboard.server.common.data.HasCustomerId; | ||
24 | +import org.thingsboard.server.common.data.HasName; | ||
25 | +import org.thingsboard.server.common.data.HasTenantId; | ||
26 | +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; | ||
27 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
28 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
29 | +import org.thingsboard.server.common.data.id.TenantId; | ||
30 | + | ||
31 | +@EqualsAndHashCode(callSuper = true) | ||
32 | +@ToString | ||
33 | +@Getter | ||
34 | +@Setter | ||
35 | +public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements HasName, HasTenantId, HasCustomerId { | ||
36 | + | ||
37 | + private static final long serialVersionUID = 4934987555236873728L; | ||
38 | + | ||
39 | + private TenantId tenantId; | ||
40 | + private CustomerId customerId; | ||
41 | + private String name; | ||
42 | + private transient JsonNode configuration; | ||
43 | + private transient JsonNode additionalInfo; | ||
44 | + | ||
45 | + public Edge() { | ||
46 | + super(); | ||
47 | + } | ||
48 | + | ||
49 | + public Edge(EdgeId id) { | ||
50 | + super(id); | ||
51 | + } | ||
52 | + | ||
53 | + public Edge(Edge edge) { | ||
54 | + super(edge); | ||
55 | + this.tenantId = edge.getTenantId(); | ||
56 | + this.name = edge.getName(); | ||
57 | + this.configuration = edge.getConfiguration(); | ||
58 | + this.additionalInfo = edge.getAdditionalInfo(); | ||
59 | + } | ||
60 | + | ||
61 | + @Override | ||
62 | + public String getSearchText() { | ||
63 | + return getName(); | ||
64 | + } | ||
65 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.id; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonIgnore; | ||
20 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
21 | +import org.thingsboard.server.common.data.EntityType; | ||
22 | + | ||
23 | +import java.util.UUID; | ||
24 | + | ||
25 | +public class EdgeId extends UUIDBased implements EntityId { | ||
26 | + | ||
27 | + private static final long serialVersionUID = 1L; | ||
28 | + | ||
29 | + @JsonCreator | ||
30 | + public EdgeId(@JsonProperty("id") UUID id) { | ||
31 | + super(id); | ||
32 | + } | ||
33 | + | ||
34 | + public static EdgeId fromString(String integrationId) { | ||
35 | + return new EdgeId(UUID.fromString(integrationId)); | ||
36 | + } | ||
37 | + | ||
38 | + @JsonIgnore | ||
39 | + @Override | ||
40 | + public EntityType getEntityType() { | ||
41 | + return EntityType.EDGE; | ||
42 | + } | ||
43 | +} |
@@ -63,6 +63,8 @@ public class EntityIdFactory { | @@ -63,6 +63,8 @@ public class EntityIdFactory { | ||
63 | return new WidgetsBundleId(uuid); | 63 | return new WidgetsBundleId(uuid); |
64 | case WIDGET_TYPE: | 64 | case WIDGET_TYPE: |
65 | return new WidgetTypeId(uuid); | 65 | return new WidgetTypeId(uuid); |
66 | + case EDGE: | ||
67 | + return new EdgeId(uuid); | ||
66 | } | 68 | } |
67 | throw new IllegalArgumentException("EntityType " + type + " is not supported!"); | 69 | throw new IllegalArgumentException("EntityType " + type + " is not supported!"); |
68 | } | 70 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.edge; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | +import org.springframework.beans.factory.annotation.Autowired; | ||
21 | +import org.springframework.stereotype.Service; | ||
22 | +import org.springframework.util.StringUtils; | ||
23 | +import org.thingsboard.server.common.data.Tenant; | ||
24 | +import org.thingsboard.server.common.data.edge.Edge; | ||
25 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
26 | +import org.thingsboard.server.common.data.id.TenantId; | ||
27 | +import org.thingsboard.server.common.data.page.TextPageData; | ||
28 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
29 | +import org.thingsboard.server.dao.entity.AbstractEntityService; | ||
30 | +import org.thingsboard.server.dao.exception.DataValidationException; | ||
31 | +import org.thingsboard.server.dao.service.DataValidator; | ||
32 | +import org.thingsboard.server.dao.service.PaginatedRemover; | ||
33 | +import org.thingsboard.server.dao.tenant.TenantDao; | ||
34 | + | ||
35 | +import java.util.List; | ||
36 | + | ||
37 | +import static org.thingsboard.server.dao.DaoUtil.toUUIDs; | ||
38 | +import static org.thingsboard.server.dao.service.Validator.validateId; | ||
39 | +import static org.thingsboard.server.dao.service.Validator.validateIds; | ||
40 | +import static org.thingsboard.server.dao.service.Validator.validatePageLink; | ||
41 | + | ||
42 | +@Service | ||
43 | +@Slf4j | ||
44 | +public class BaseEdgeService extends AbstractEntityService implements EdgeService { | ||
45 | + | ||
46 | + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; | ||
47 | + public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; | ||
48 | + public static final String INCORRECT_EDGE_ID = "Incorrect edgeId "; | ||
49 | + | ||
50 | + @Autowired | ||
51 | + private EdgeDao edgeDao; | ||
52 | + | ||
53 | + @Autowired | ||
54 | + private TenantDao tenantDao; | ||
55 | + | ||
56 | + @Override | ||
57 | + public Edge saveEdge(Edge edge) { | ||
58 | + log.trace("Executing saveEdge [{}]", edge); | ||
59 | + edgeValidator.validate(edge, Edge::getTenantId); | ||
60 | + return edgeDao.save(edge.getTenantId(), edge); | ||
61 | + } | ||
62 | + | ||
63 | + @Override | ||
64 | + public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) { | ||
65 | + log.trace("Executing findEdgeById [{}]", edgeId); | ||
66 | + validateId(edgeId, INCORRECT_EDGE_ID + edgeId); | ||
67 | + return edgeDao.findById(tenantId, edgeId.getId()); | ||
68 | + } | ||
69 | + | ||
70 | + @Override | ||
71 | + public ListenableFuture<Edge> findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId) { | ||
72 | + log.trace("Executing findEdgeByIdAsync [{}]", edgeId); | ||
73 | + validateId(edgeId, INCORRECT_EDGE_ID + edgeId); | ||
74 | + return edgeDao.findByIdAsync(tenantId, edgeId.getId()); | ||
75 | + } | ||
76 | + | ||
77 | + @Override | ||
78 | + public ListenableFuture<List<Edge>> findEdgesByIdsAsync(TenantId tenantId, List<EdgeId> edgeIds) { | ||
79 | + log.trace("Executing findEdgesByIdsAsync, tenantId [{}], edgeIds [{}]", tenantId, edgeIds); | ||
80 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
81 | + validateIds(edgeIds, "Incorrect edgeIds " + edgeIds); | ||
82 | + return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds)); | ||
83 | + } | ||
84 | + | ||
85 | + @Override | ||
86 | + public List<Edge> findAllEdges(TenantId tenantId) { | ||
87 | + log.trace("Executing findAllEdges"); | ||
88 | + return edgeDao.find(tenantId); | ||
89 | + } | ||
90 | + | ||
91 | + @Override | ||
92 | + public TextPageData<Edge> findTenantEdges(TenantId tenantId, TextPageLink pageLink) { | ||
93 | + log.trace("Executing findTenantEdges, tenantId [{}], pageLink [{}]", tenantId, pageLink); | ||
94 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
95 | + validatePageLink(pageLink, INCORRECT_PAGE_LINK + pageLink); | ||
96 | + List<Edge> edges = edgeDao.findByTenantIdAndPageLink(tenantId.getId(), pageLink); | ||
97 | + return new TextPageData<>(edges, pageLink); | ||
98 | + } | ||
99 | + | ||
100 | + @Override | ||
101 | + public void deleteEdge(TenantId tenantId, EdgeId edgeId) { | ||
102 | + log.trace("Executing deleteEdge [{}]", edgeId); | ||
103 | + validateId(edgeId, INCORRECT_EDGE_ID + edgeId); | ||
104 | + deleteEntityRelations(tenantId, edgeId); | ||
105 | + edgeDao.removeById(tenantId, edgeId.getId()); | ||
106 | + } | ||
107 | + | ||
108 | + @Override | ||
109 | + public void deleteEdgesByTenantId(TenantId tenantId) { | ||
110 | + log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId); | ||
111 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
112 | + tenantEdgesRemover.removeEntities(tenantId, tenantId); | ||
113 | + } | ||
114 | + | ||
115 | + private DataValidator<Edge> edgeValidator = | ||
116 | + new DataValidator<Edge>() { | ||
117 | + | ||
118 | + @Override | ||
119 | + protected void validateCreate(TenantId tenantId, Edge edge) { | ||
120 | + } | ||
121 | + | ||
122 | + @Override | ||
123 | + protected void validateUpdate(TenantId tenantId, Edge edge) { | ||
124 | + } | ||
125 | + | ||
126 | + @Override | ||
127 | + protected void validateDataImpl(TenantId tenantId, Edge edge) { | ||
128 | + if (StringUtils.isEmpty(edge.getName())) { | ||
129 | + throw new DataValidationException("Edge name should be specified!"); | ||
130 | + } | ||
131 | + if (edge.getTenantId() == null || edge.getTenantId().isNullUid()) { | ||
132 | + throw new DataValidationException("Edge should be assigned to tenant!"); | ||
133 | + } else { | ||
134 | + Tenant tenant = tenantDao.findById(tenantId, edge.getTenantId().getId()); | ||
135 | + if (tenant == null) { | ||
136 | + throw new DataValidationException("Edge is referencing to non-existent tenant!"); | ||
137 | + } | ||
138 | + } | ||
139 | + } | ||
140 | + }; | ||
141 | + | ||
142 | + private PaginatedRemover<TenantId, Edge> tenantEdgesRemover = | ||
143 | + new PaginatedRemover<TenantId, Edge>() { | ||
144 | + | ||
145 | + @Override | ||
146 | + protected List<Edge> findEntities(TenantId tenantId, TenantId id, TextPageLink pageLink) { | ||
147 | + return edgeDao.findByTenantIdAndPageLink(id.getId(), pageLink); | ||
148 | + } | ||
149 | + | ||
150 | + @Override | ||
151 | + protected void removeEntity(TenantId tenantId, Edge entity) { | ||
152 | + deleteEdge(tenantId, new EdgeId(entity.getId().getId())); | ||
153 | + } | ||
154 | + }; | ||
155 | + | ||
156 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.edge; | ||
17 | + | ||
18 | +import com.datastax.driver.core.querybuilder.Select; | ||
19 | +import com.google.common.util.concurrent.ListenableFuture; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.springframework.stereotype.Component; | ||
22 | +import org.thingsboard.server.common.data.edge.Edge; | ||
23 | +import org.thingsboard.server.common.data.id.TenantId; | ||
24 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
25 | +import org.thingsboard.server.dao.DaoUtil; | ||
26 | +import org.thingsboard.server.dao.model.nosql.EdgeEntity; | ||
27 | +import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao; | ||
28 | +import org.thingsboard.server.dao.util.NoSqlDao; | ||
29 | + | ||
30 | +import java.util.Collections; | ||
31 | +import java.util.List; | ||
32 | +import java.util.UUID; | ||
33 | + | ||
34 | +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; | ||
35 | +import static com.datastax.driver.core.querybuilder.QueryBuilder.in; | ||
36 | +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; | ||
37 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; | ||
38 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME; | ||
39 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; | ||
40 | +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; | ||
41 | + | ||
42 | +@Component | ||
43 | +@Slf4j | ||
44 | +@NoSqlDao | ||
45 | +public class CassandraEdgeDao extends CassandraAbstractSearchTextDao<EdgeEntity, Edge> implements EdgeDao { | ||
46 | + | ||
47 | + @Override | ||
48 | + protected Class<EdgeEntity> getColumnFamilyClass() { | ||
49 | + return EdgeEntity.class; | ||
50 | + } | ||
51 | + | ||
52 | + @Override | ||
53 | + protected String getColumnFamilyName() { | ||
54 | + return EDGE_COLUMN_FAMILY_NAME; | ||
55 | + } | ||
56 | + | ||
57 | + @Override | ||
58 | + public List<Edge> findByTenantIdAndPageLink(UUID tenantId, TextPageLink pageLink) { | ||
59 | + log.debug("Try to find edges by tenantId [{}] and pageLink [{}]", tenantId, pageLink); | ||
60 | + List<EdgeEntity> edgeEntities = findPageWithTextSearch(new TenantId(tenantId), EDGE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, | ||
61 | + Collections.singletonList(eq(EDGE_TENANT_ID_PROPERTY, tenantId)), pageLink); | ||
62 | + | ||
63 | + log.trace("Found edges [{}] by tenantId [{}] and pageLink [{}]", edgeEntities, tenantId, pageLink); | ||
64 | + return DaoUtil.convertDataList(edgeEntities); | ||
65 | + } | ||
66 | + | ||
67 | + @Override | ||
68 | + public ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(UUID tenantId, List<UUID> edgeIds) { | ||
69 | + log.debug("Try to find edges by tenantId [{}] and edge Ids [{}]", tenantId, edgeIds); | ||
70 | + Select select = select().from(getColumnFamilyName()); | ||
71 | + Select.Where query = select.where(); | ||
72 | + query.and(eq(EDGE_TENANT_ID_PROPERTY, tenantId)); | ||
73 | + query.and(in(ID_PROPERTY, edgeIds)); | ||
74 | + return findListByStatementAsync(new TenantId(tenantId), query); | ||
75 | + } | ||
76 | + | ||
77 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.edge; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | +import org.thingsboard.server.common.data.edge.Edge; | ||
20 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
21 | +import org.thingsboard.server.dao.Dao; | ||
22 | + | ||
23 | +import java.util.List; | ||
24 | +import java.util.UUID; | ||
25 | + | ||
26 | +/** | ||
27 | + * The Interface EdgeDao. | ||
28 | + * | ||
29 | + */ | ||
30 | +public interface EdgeDao extends Dao<Edge> { | ||
31 | + | ||
32 | + /** | ||
33 | + * Find edges by tenantId and page link. | ||
34 | + * | ||
35 | + * @param tenantId the tenantId | ||
36 | + * @param pageLink the page link | ||
37 | + * @return the list of edge objects | ||
38 | + */ | ||
39 | + List<Edge> findByTenantIdAndPageLink(UUID tenantId, TextPageLink pageLink); | ||
40 | + | ||
41 | + /** | ||
42 | + * Find edges by tenantId and edge Ids. | ||
43 | + * | ||
44 | + * @param tenantId the tenantId | ||
45 | + * @param edgeIds the edge Ids | ||
46 | + * @return the list of edge objects | ||
47 | + */ | ||
48 | + ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(UUID tenantId, List<UUID> edgeIds); | ||
49 | + | ||
50 | + | ||
51 | +} |
@@ -348,6 +348,18 @@ public class ModelConstants { | @@ -348,6 +348,18 @@ public class ModelConstants { | ||
348 | public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration"; | 348 | public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration"; |
349 | 349 | ||
350 | /** | 350 | /** |
351 | + * Cassandra edge constants. | ||
352 | + */ | ||
353 | + public static final String EDGE_COLUMN_FAMILY_NAME = "edge"; | ||
354 | + public static final String EDGE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; | ||
355 | + public static final String EDGE_NAME_PROPERTY = "name"; | ||
356 | + public static final String EDGE_CONFIGURATION_PROPERTY = "configuration"; | ||
357 | + public static final String EDGE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; | ||
358 | + | ||
359 | + public static final String EDGE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "edge_by_tenant_and_search_text"; | ||
360 | + | ||
361 | + | ||
362 | + /** | ||
351 | * Cassandra attributes and timeseries constants. | 363 | * Cassandra attributes and timeseries constants. |
352 | */ | 364 | */ |
353 | public static final String ATTRIBUTES_KV_CF = "attributes_kv_cf"; | 365 | public static final String ATTRIBUTES_KV_CF = "attributes_kv_cf"; |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.model.nosql; | ||
17 | + | ||
18 | +import com.datastax.driver.core.utils.UUIDs; | ||
19 | +import com.datastax.driver.mapping.annotations.ClusteringColumn; | ||
20 | +import com.datastax.driver.mapping.annotations.Column; | ||
21 | +import com.datastax.driver.mapping.annotations.PartitionKey; | ||
22 | +import com.datastax.driver.mapping.annotations.Table; | ||
23 | +import com.fasterxml.jackson.databind.JsonNode; | ||
24 | +import lombok.Data; | ||
25 | +import org.thingsboard.server.common.data.edge.Edge; | ||
26 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
27 | +import org.thingsboard.server.common.data.id.TenantId; | ||
28 | +import org.thingsboard.server.dao.model.SearchTextEntity; | ||
29 | +import org.thingsboard.server.dao.model.type.JsonCodec; | ||
30 | + | ||
31 | +import java.util.UUID; | ||
32 | + | ||
33 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_ADDITIONAL_INFO_PROPERTY; | ||
34 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME; | ||
35 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_CONFIGURATION_PROPERTY; | ||
36 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; | ||
37 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; | ||
38 | +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; | ||
39 | +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; | ||
40 | + | ||
41 | +@Data | ||
42 | +@Table(name = EDGE_COLUMN_FAMILY_NAME) | ||
43 | +public class EdgeEntity implements SearchTextEntity<Edge> { | ||
44 | + | ||
45 | + @PartitionKey | ||
46 | + @Column(name = ID_PROPERTY) | ||
47 | + private UUID id; | ||
48 | + | ||
49 | + @ClusteringColumn | ||
50 | + @Column(name = EDGE_TENANT_ID_PROPERTY) | ||
51 | + private UUID tenantId; | ||
52 | + | ||
53 | + @Column(name = EDGE_NAME_PROPERTY) | ||
54 | + private String name; | ||
55 | + | ||
56 | + @Column(name = SEARCH_TEXT_PROPERTY) | ||
57 | + private String searchText; | ||
58 | + | ||
59 | + @Column(name = EDGE_CONFIGURATION_PROPERTY, codec = JsonCodec.class) | ||
60 | + private JsonNode configuration; | ||
61 | + | ||
62 | + @Column(name = EDGE_ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class) | ||
63 | + private JsonNode additionalInfo; | ||
64 | + | ||
65 | + public EdgeEntity() { | ||
66 | + super(); | ||
67 | + } | ||
68 | + | ||
69 | + public EdgeEntity(Edge edge) { | ||
70 | + if (edge.getId() != null) { | ||
71 | + this.id = edge.getId().getId(); | ||
72 | + } | ||
73 | + if (edge.getTenantId() != null) { | ||
74 | + this.tenantId = edge.getTenantId().getId(); | ||
75 | + } | ||
76 | + this.name = edge.getName(); | ||
77 | + this.configuration = edge.getConfiguration(); | ||
78 | + this.additionalInfo = edge.getAdditionalInfo(); | ||
79 | + } | ||
80 | + | ||
81 | + @Override | ||
82 | + public String getSearchTextSource() { | ||
83 | + return getName(); | ||
84 | + } | ||
85 | + | ||
86 | + @Override | ||
87 | + public Edge toData() { | ||
88 | + Edge edge = new Edge(new EdgeId(id)); | ||
89 | + edge.setCreatedTime(UUIDs.unixTimestamp(id)); | ||
90 | + if (tenantId != null) { | ||
91 | + edge.setTenantId(new TenantId(tenantId)); | ||
92 | + } | ||
93 | + edge.setName(name); | ||
94 | + edge.setConfiguration(configuration); | ||
95 | + edge.setAdditionalInfo(additionalInfo); | ||
96 | + return edge; | ||
97 | + } | ||
98 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.model.sql; | ||
17 | + | ||
18 | +import com.datastax.driver.core.utils.UUIDs; | ||
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | +import lombok.Data; | ||
21 | +import lombok.EqualsAndHashCode; | ||
22 | +import org.hibernate.annotations.Type; | ||
23 | +import org.hibernate.annotations.TypeDef; | ||
24 | +import org.thingsboard.server.common.data.UUIDConverter; | ||
25 | +import org.thingsboard.server.common.data.edge.Edge; | ||
26 | +import org.thingsboard.server.common.data.id.EdgeId; | ||
27 | +import org.thingsboard.server.common.data.id.TenantId; | ||
28 | +import org.thingsboard.server.dao.model.BaseSqlEntity; | ||
29 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
30 | +import org.thingsboard.server.dao.model.SearchTextEntity; | ||
31 | +import org.thingsboard.server.dao.util.mapping.JsonStringType; | ||
32 | + | ||
33 | +import javax.persistence.Column; | ||
34 | +import javax.persistence.Entity; | ||
35 | +import javax.persistence.Table; | ||
36 | + | ||
37 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_COLUMN_FAMILY_NAME; | ||
38 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_NAME_PROPERTY; | ||
39 | +import static org.thingsboard.server.dao.model.ModelConstants.EDGE_TENANT_ID_PROPERTY; | ||
40 | +import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; | ||
41 | + | ||
42 | +@Data | ||
43 | +@EqualsAndHashCode(callSuper = true) | ||
44 | +@Entity | ||
45 | +@TypeDef(name = "json", typeClass = JsonStringType.class) | ||
46 | +@Table(name = EDGE_COLUMN_FAMILY_NAME) | ||
47 | +public class EdgeEntity extends BaseSqlEntity<Edge> implements SearchTextEntity<Edge> { | ||
48 | + | ||
49 | + @Column(name = EDGE_TENANT_ID_PROPERTY) | ||
50 | + private String tenantId; | ||
51 | + | ||
52 | + @Column(name = EDGE_NAME_PROPERTY) | ||
53 | + private String name; | ||
54 | + | ||
55 | + @Column(name = SEARCH_TEXT_PROPERTY) | ||
56 | + private String searchText; | ||
57 | + | ||
58 | + @Type(type = "json") | ||
59 | + @Column(name = ModelConstants.EDGE_CONFIGURATION_PROPERTY) | ||
60 | + private JsonNode configuration; | ||
61 | + | ||
62 | + @Type(type = "json") | ||
63 | + @Column(name = ModelConstants.EDGE_ADDITIONAL_INFO_PROPERTY) | ||
64 | + private JsonNode additionalInfo; | ||
65 | + | ||
66 | + public EdgeEntity() { | ||
67 | + super(); | ||
68 | + } | ||
69 | + | ||
70 | + public EdgeEntity(Edge edge) { | ||
71 | + if (edge.getId() != null) { | ||
72 | + this.setId(edge.getId().getId()); | ||
73 | + } | ||
74 | + if (edge.getTenantId() != null) { | ||
75 | + this.tenantId = UUIDConverter.fromTimeUUID(edge.getTenantId().getId()); | ||
76 | + } | ||
77 | + this.name = edge.getName(); | ||
78 | + this.configuration = edge.getConfiguration(); | ||
79 | + this.additionalInfo = edge.getAdditionalInfo(); | ||
80 | + } | ||
81 | + | ||
82 | + public String getSearchText() { | ||
83 | + return searchText; | ||
84 | + } | ||
85 | + | ||
86 | + @Override | ||
87 | + public String getSearchTextSource() { | ||
88 | + return name; | ||
89 | + } | ||
90 | + | ||
91 | + @Override | ||
92 | + public void setSearchText(String searchText) { | ||
93 | + this.searchText = searchText; | ||
94 | + } | ||
95 | + | ||
96 | + @Override | ||
97 | + public Edge toData() { | ||
98 | + Edge edge = new Edge(new EdgeId(UUIDConverter.fromString(id))); | ||
99 | + edge.setCreatedTime(UUIDs.unixTimestamp(UUIDConverter.fromString(id))); | ||
100 | + if (tenantId != null) { | ||
101 | + edge.setTenantId(new TenantId(UUIDConverter.fromString(tenantId))); | ||
102 | + } | ||
103 | + edge.setName(name); | ||
104 | + edge.setConfiguration(configuration); | ||
105 | + edge.setAdditionalInfo(additionalInfo); | ||
106 | + return edge; | ||
107 | + } | ||
108 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.sql.edge; | ||
17 | + | ||
18 | +import org.springframework.data.domain.Pageable; | ||
19 | +import org.springframework.data.jpa.repository.Query; | ||
20 | +import org.springframework.data.repository.CrudRepository; | ||
21 | +import org.springframework.data.repository.query.Param; | ||
22 | +import org.thingsboard.server.dao.model.sql.EdgeEntity; | ||
23 | +import org.thingsboard.server.dao.util.SqlDao; | ||
24 | + | ||
25 | +import java.util.List; | ||
26 | + | ||
27 | +@SqlDao | ||
28 | +public interface EdgeRepository extends CrudRepository<EdgeEntity, String> { | ||
29 | + | ||
30 | + @Query("SELECT a FROM EdgeEntity a WHERE a.tenantId = :tenantId " + | ||
31 | + "AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%')) " + | ||
32 | + "AND a.id > :idOffset ORDER BY a.id") | ||
33 | + List<EdgeEntity> findByTenantIdAndPageLink(@Param("tenantId") String tenantId, | ||
34 | + @Param("textSearch") String textSearch, | ||
35 | + @Param("idOffset") String idOffset, | ||
36 | + Pageable pageable); | ||
37 | + | ||
38 | + List<EdgeEntity> findEdgesByTenantIdAndIdIn(String tenantId, List<String> edgeIds); | ||
39 | + | ||
40 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 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.sql.edge; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | +import org.springframework.beans.factory.annotation.Autowired; | ||
20 | +import org.springframework.data.domain.PageRequest; | ||
21 | +import org.springframework.data.repository.CrudRepository; | ||
22 | +import org.springframework.stereotype.Component; | ||
23 | +import org.thingsboard.server.common.data.UUIDConverter; | ||
24 | +import org.thingsboard.server.common.data.edge.Edge; | ||
25 | +import org.thingsboard.server.common.data.page.TextPageLink; | ||
26 | +import org.thingsboard.server.dao.DaoUtil; | ||
27 | +import org.thingsboard.server.dao.edge.EdgeDao; | ||
28 | +import org.thingsboard.server.dao.model.sql.EdgeEntity; | ||
29 | +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; | ||
30 | +import org.thingsboard.server.dao.util.SqlDao; | ||
31 | + | ||
32 | +import java.util.List; | ||
33 | +import java.util.Objects; | ||
34 | +import java.util.UUID; | ||
35 | + | ||
36 | +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; | ||
37 | +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; | ||
38 | +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; | ||
39 | + | ||
40 | +@Component | ||
41 | +@SqlDao | ||
42 | +public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> implements EdgeDao { | ||
43 | + | ||
44 | + @Autowired | ||
45 | + private EdgeRepository edgeRepository; | ||
46 | + | ||
47 | + @Override | ||
48 | + public List<Edge> findByTenantIdAndPageLink(UUID tenantId, TextPageLink pageLink) { | ||
49 | + return DaoUtil.convertDataList(edgeRepository | ||
50 | + .findByTenantIdAndPageLink( | ||
51 | + fromTimeUUID(tenantId), | ||
52 | + Objects.toString(pageLink.getTextSearch(), ""), | ||
53 | + pageLink.getIdOffset() == null ? NULL_UUID_STR : fromTimeUUID(pageLink.getIdOffset()), | ||
54 | + new PageRequest(0, pageLink.getLimit()))); | ||
55 | + } | ||
56 | + | ||
57 | + @Override | ||
58 | + public ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(UUID tenantId, List<UUID> edgeIds) { | ||
59 | + return service.submit(() -> DaoUtil.convertDataList(edgeRepository.findEdgesByTenantIdAndIdIn(UUIDConverter.fromTimeUUID(tenantId), fromTimeUUIDs(edgeIds)))); | ||
60 | + } | ||
61 | + | ||
62 | + @Override | ||
63 | + protected Class<EdgeEntity> getEntityClass() { | ||
64 | + return EdgeEntity.class; | ||
65 | + } | ||
66 | + | ||
67 | + @Override | ||
68 | + protected CrudRepository<EdgeEntity, String> getCrudRepository() { | ||
69 | + return edgeRepository; | ||
70 | + } | ||
71 | + | ||
72 | +} |
@@ -711,4 +711,21 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_ent | @@ -711,4 +711,21 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_ent | ||
711 | AND search_text IS NOT NULL | 711 | AND search_text IS NOT NULL |
712 | AND id IS NOT NULL | 712 | AND id IS NOT NULL |
713 | PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) | 713 | PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) |
714 | - WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); | ||
714 | + WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); | ||
715 | + | ||
716 | +CREATE TABLE IF NOT EXISTS thingsboard.edge ( | ||
717 | + id timeuuid, | ||
718 | + tenant_id timeuuid, | ||
719 | + name text, | ||
720 | + search_text text, | ||
721 | + configuration text, | ||
722 | + additional_info text, | ||
723 | + PRIMARY KEY (id, tenant_id) | ||
724 | +); | ||
725 | + | ||
726 | +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_search_text AS | ||
727 | + SELECT * | ||
728 | + from thingsboard.edge | ||
729 | + WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL | ||
730 | + PRIMARY KEY ( tenant_id, search_text, id ) | ||
731 | + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); |
@@ -243,3 +243,12 @@ CREATE TABLE IF NOT EXISTS entity_view ( | @@ -243,3 +243,12 @@ CREATE TABLE IF NOT EXISTS entity_view ( | ||
243 | search_text varchar(255), | 243 | search_text varchar(255), |
244 | additional_info varchar | 244 | additional_info varchar |
245 | ); | 245 | ); |
246 | + | ||
247 | +CREATE TABLE IF NOT EXISTS edge ( | ||
248 | + id varchar(31) NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, | ||
249 | + additional_info varchar, | ||
250 | + configuration varchar(10000000), | ||
251 | + name varchar(255), | ||
252 | + search_text varchar(255), | ||
253 | + tenant_id varchar(31) | ||
254 | +); |
@@ -20,3 +20,4 @@ DROP TABLE IF EXISTS widgets_bundle; | @@ -20,3 +20,4 @@ DROP TABLE IF EXISTS widgets_bundle; | ||
20 | DROP TABLE IF EXISTS rule_node; | 20 | DROP TABLE IF EXISTS rule_node; |
21 | DROP TABLE IF EXISTS rule_chain; | 21 | DROP TABLE IF EXISTS rule_chain; |
22 | DROP TABLE IF EXISTS entity_view; | 22 | DROP TABLE IF EXISTS entity_view; |
23 | +DROP TABLE IF EXISTS edge; |
ui/src/app/api/edge.service.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2019 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 | +export default angular.module('thingsboard.api.edge', []) | ||
17 | + .factory('edgeService', EdgeService) | ||
18 | + .name; | ||
19 | + | ||
20 | +/*@ngInject*/ | ||
21 | +function EdgeService($http, $q) { | ||
22 | + | ||
23 | + var service = { | ||
24 | + getEdges: getEdges, | ||
25 | + getEdgesByIds: getEdgesByIds, | ||
26 | + getEdge: getEdge, | ||
27 | + deleteEdge: deleteEdge, | ||
28 | + saveEdge: saveEdge | ||
29 | + }; | ||
30 | + | ||
31 | + return service; | ||
32 | + | ||
33 | + function getEdges(pageLink, config) { | ||
34 | + var deferred = $q.defer(); | ||
35 | + var url = '/api/edges?limit=' + pageLink.limit; | ||
36 | + if (angular.isDefined(pageLink.textSearch)) { | ||
37 | + url += '&textSearch=' + pageLink.textSearch; | ||
38 | + } | ||
39 | + if (angular.isDefined(pageLink.idOffset)) { | ||
40 | + url += '&idOffset=' + pageLink.idOffset; | ||
41 | + } | ||
42 | + if (angular.isDefined(pageLink.textOffset)) { | ||
43 | + url += '&textOffset=' + pageLink.textOffset; | ||
44 | + } | ||
45 | + $http.get(url, config).then(function success(response) { | ||
46 | + deferred.resolve(response.data); | ||
47 | + }, function fail() { | ||
48 | + deferred.reject(); | ||
49 | + }); | ||
50 | + return deferred.promise; | ||
51 | + } | ||
52 | + | ||
53 | + function getEdgesByIds(edgeIds, config) { | ||
54 | + var deferred = $q.defer(); | ||
55 | + var ids = ''; | ||
56 | + for (var i=0;i<edgeIds.length;i++) { | ||
57 | + if (i>0) { | ||
58 | + ids += ','; | ||
59 | + } | ||
60 | + ids += edgeIds[i]; | ||
61 | + } | ||
62 | + var url = '/api/edges?edgeIds=' + ids; | ||
63 | + $http.get(url, config).then(function success(response) { | ||
64 | + var entities = response.data; | ||
65 | + entities.sort(function (entity1, entity2) { | ||
66 | + var id1 = entity1.id.id; | ||
67 | + var id2 = entity2.id.id; | ||
68 | + var index1 = edgeIds.indexOf(id1); | ||
69 | + var index2 = edgeIds.indexOf(id2); | ||
70 | + return index1 - index2; | ||
71 | + }); | ||
72 | + deferred.resolve(entities); | ||
73 | + }, function fail() { | ||
74 | + deferred.reject(); | ||
75 | + }); | ||
76 | + return deferred.promise; | ||
77 | + } | ||
78 | + | ||
79 | + function getEdge(edgeId, config) { | ||
80 | + var deferred = $q.defer(); | ||
81 | + var url = '/api/edge/' + edgeId; | ||
82 | + $http.get(url, config).then(function success(response) { | ||
83 | + deferred.resolve(response.data); | ||
84 | + }, function fail(response) { | ||
85 | + deferred.reject(response.data); | ||
86 | + }); | ||
87 | + return deferred.promise; | ||
88 | + } | ||
89 | + | ||
90 | + function saveEdge(edge) { | ||
91 | + var deferred = $q.defer(); | ||
92 | + var url = '/api/edge'; | ||
93 | + $http.post(url, edge).then(function success(response) { | ||
94 | + deferred.resolve(response.data); | ||
95 | + }, function fail(response) { | ||
96 | + deferred.reject(response.data); | ||
97 | + }); | ||
98 | + return deferred.promise; | ||
99 | + } | ||
100 | + | ||
101 | + function deleteEdge(edgeId) { | ||
102 | + var deferred = $q.defer(); | ||
103 | + var url = '/api/edge/' + edgeId; | ||
104 | + $http.delete(url).then(function success() { | ||
105 | + deferred.resolve(); | ||
106 | + }, function fail(response) { | ||
107 | + deferred.reject(response.data); | ||
108 | + }); | ||
109 | + return deferred.promise; | ||
110 | + } | ||
111 | +} |
@@ -99,6 +99,7 @@ import thingsboardApiAlarm from './api/alarm.service'; | @@ -99,6 +99,7 @@ import thingsboardApiAlarm from './api/alarm.service'; | ||
99 | import thingsboardApiAuditLog from './api/audit-log.service'; | 99 | import thingsboardApiAuditLog from './api/audit-log.service'; |
100 | import thingsboardApiComponentDescriptor from './api/component-descriptor.service'; | 100 | import thingsboardApiComponentDescriptor from './api/component-descriptor.service'; |
101 | import thingsboardApiRuleChain from './api/rule-chain.service'; | 101 | import thingsboardApiRuleChain from './api/rule-chain.service'; |
102 | +import thingsboardApiEdge from './api/edge.service'; | ||
102 | 103 | ||
103 | import AppConfig from './app.config'; | 104 | import AppConfig from './app.config'; |
104 | import GlobalInterceptor from './global-interceptor.service'; | 105 | import GlobalInterceptor from './global-interceptor.service'; |
@@ -157,6 +158,7 @@ angular.module('thingsboard', [ | @@ -157,6 +158,7 @@ angular.module('thingsboard', [ | ||
157 | thingsboardApiAuditLog, | 158 | thingsboardApiAuditLog, |
158 | thingsboardApiComponentDescriptor, | 159 | thingsboardApiComponentDescriptor, |
159 | thingsboardApiRuleChain, | 160 | thingsboardApiRuleChain, |
161 | + thingsboardApiEdge, | ||
160 | uiRouter]) | 162 | uiRouter]) |
161 | .config(AppConfig) | 163 | .config(AppConfig) |
162 | .factory('globalInterceptor', GlobalInterceptor) | 164 | .factory('globalInterceptor', GlobalInterceptor) |
@@ -358,7 +358,8 @@ export default angular.module('thingsboard.types', []) | @@ -358,7 +358,8 @@ export default angular.module('thingsboard.types', []) | ||
358 | alarm: "ALARM", | 358 | alarm: "ALARM", |
359 | rulechain: "RULE_CHAIN", | 359 | rulechain: "RULE_CHAIN", |
360 | rulenode: "RULE_NODE", | 360 | rulenode: "RULE_NODE", |
361 | - entityView: "ENTITY_VIEW" | 361 | + entityView: "ENTITY_VIEW", |
362 | + edge: "EDGE" | ||
362 | }, | 363 | }, |
363 | importEntityColumnType: { | 364 | importEntityColumnType: { |
364 | name: { | 365 | name: { |
@@ -461,7 +462,13 @@ export default angular.module('thingsboard.types', []) | @@ -461,7 +462,13 @@ export default angular.module('thingsboard.types', []) | ||
461 | "CURRENT_CUSTOMER": { | 462 | "CURRENT_CUSTOMER": { |
462 | type: 'entity.type-current-customer', | 463 | type: 'entity.type-current-customer', |
463 | list: 'entity.type-current-customer' | 464 | list: 'entity.type-current-customer' |
464 | - } | 465 | + }, |
466 | + "EDGE": { | ||
467 | + type: 'entity.type-edge', | ||
468 | + typePlural: 'entity.type-edges', | ||
469 | + list: 'entity.list-of-edges', | ||
470 | + nameStartsWith: 'entity.edge-name-starts-with' | ||
471 | + }, | ||
465 | }, | 472 | }, |
466 | entitySearchDirection: { | 473 | entitySearchDirection: { |
467 | from: "FROM", | 474 | from: "FROM", |
ui/src/app/edge/add-edge.tpl.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2019 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<md-dialog aria-label="{{ 'edge.add' | translate }}" tb-help="'edges'" help-container-id="help-container" style="width: 600px;"> | ||
19 | + <form name="theForm" ng-submit="vm.add()"> | ||
20 | + <md-toolbar> | ||
21 | + <div class="md-toolbar-tools"> | ||
22 | + <h2 translate>edge.add</h2> | ||
23 | + <span flex></span> | ||
24 | + <div id="help-container"></div> | ||
25 | + <md-button class="md-icon-button" ng-click="vm.cancel()"> | ||
26 | + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | ||
27 | + </md-button> | ||
28 | + </div> | ||
29 | + </md-toolbar> | ||
30 | + <md-progress-linear class="md-warn" md-mode="indeterminate" ng-disabled="!$root.loading" ng-show="$root.loading"></md-progress-linear> | ||
31 | + <span style="min-height: 5px;" flex="" ng-show="!$root.loading"></span> | ||
32 | + <md-dialog-content> | ||
33 | + <div class="md-dialog-content"> | ||
34 | + <tb-edge edge="vm.item" is-edit="true" is-create="true" the-form="theForm"></tb-edge> | ||
35 | + </div> | ||
36 | + </md-dialog-content> | ||
37 | + <md-dialog-actions layout="row"> | ||
38 | + <span flex></span> | ||
39 | + <md-button ng-disabled="$root.loading || theForm.$invalid || !theForm.$dirty" type="submit" class="md-raised md-primary"> | ||
40 | + {{ 'action.add' | translate }} | ||
41 | + </md-button> | ||
42 | + <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> | ||
43 | + </md-dialog-actions> | ||
44 | + </form> | ||
45 | +</md-dialog> | ||
46 | + |
ui/src/app/edge/edge-card.tpl.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2019 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<div flex layout="column"> | ||
19 | + <div flex style="text-transform: uppercase;">{{ vm.types.edgeType[vm.item.type].name | translate }}</div> | ||
20 | + <div class="tb-card-description">{{vm.item.additionalInfo.description}}</div> | ||
21 | +</div> |
ui/src/app/edge/edge-fieldset.tpl.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2019 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<md-button ng-click="onExportEdge({event: $event})" | ||
19 | + ng-show="!isEdit" | ||
20 | + class="md-raised md-primary">{{ 'edge.export' | translate }}</md-button> | ||
21 | +<md-button ng-click="onDeleteEdge({event: $event})" | ||
22 | + ng-if="'edge' | hasGenericPermission:'delete'" | ||
23 | + ng-show="!isEdit" class="md-raised md-primary">{{ 'edge.delete' | translate }}</md-button> | ||
24 | + | ||
25 | +<div layout="row"> | ||
26 | + <md-button ngclipboard data-clipboard-action="copy" | ||
27 | + ngclipboard-success="onEdgeIdCopied(e)" | ||
28 | + data-clipboard-text="{{edge.id.id}}" ng-show="!isEdit" | ||
29 | + class="md-raised"> | ||
30 | + <md-icon md-svg-icon="mdi:clipboard-arrow-left"></md-icon> | ||
31 | + <span translate>edge.copyId</span> | ||
32 | + </md-button> | ||
33 | +</div> | ||
34 | + | ||
35 | +<md-content class="md-padding" layout="column"> | ||
36 | + <fieldset ng-disabled="$root.loading || !isEdit"> | ||
37 | + <md-input-container class="md-block"> | ||
38 | + <label translate>edge.name</label> | ||
39 | + <input required name="name" ng-model="edge.name"> | ||
40 | + <div ng-messages="theForm.name.$error"> | ||
41 | + <div translate ng-message="required">edge.name-required</div> | ||
42 | + </div> | ||
43 | + </md-input-container> | ||
44 | + <md-input-container class="md-block"> | ||
45 | + <label translate>edge.description</label> | ||
46 | + <textarea ng-model="edge.additionalInfo.description" rows="2"></textarea> | ||
47 | + </md-input-container> | ||
48 | + </fieldset> | ||
49 | +</md-content> |
ui/src/app/edge/edge.controller.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2019 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 | +/* eslint-disable import/no-unresolved, import/default */ | ||
17 | + | ||
18 | +import addEdgeTemplate from './add-edge.tpl.html'; | ||
19 | +import edgeCard from './edge-card.tpl.html'; | ||
20 | + | ||
21 | +/* eslint-enable import/no-unresolved, import/default */ | ||
22 | + | ||
23 | +/*@ngInject*/ | ||
24 | +export function EdgeCardController(types) { | ||
25 | + | ||
26 | + var vm = this; | ||
27 | + | ||
28 | + vm.types = types; | ||
29 | +} | ||
30 | + | ||
31 | + | ||
32 | +/*@ngInject*/ | ||
33 | +export function EdgeController($rootScope, userService, edgeService, $state, $stateParams, | ||
34 | + $document, $mdDialog, $q, $translate, types, securityTypes, userPermissionsService) { | ||
35 | + | ||
36 | + var edgeActionsList = []; | ||
37 | + | ||
38 | + var edgeGroupActionsList = []; | ||
39 | + | ||
40 | + var vm = this; | ||
41 | + | ||
42 | + vm.types = types; | ||
43 | + | ||
44 | + vm.edgeGridConfig = { | ||
45 | + | ||
46 | + resource: securityTypes.resource.edge, | ||
47 | + | ||
48 | + deleteItemTitleFunc: deleteEdgeTitle, | ||
49 | + deleteItemContentFunc: deleteEdgeText, | ||
50 | + deleteItemsTitleFunc: deleteEdgesTitle, | ||
51 | + deleteItemsActionTitleFunc: deleteEdgesActionTitle, | ||
52 | + deleteItemsContentFunc: deleteEdgesText, | ||
53 | + | ||
54 | + saveItemFunc: saveEdge, | ||
55 | + | ||
56 | + getItemTitleFunc: getEdgeTitle, | ||
57 | + | ||
58 | + itemCardController: 'EdgeCardController', | ||
59 | + itemCardTemplateUrl: edgeCard, | ||
60 | + parentCtl: vm, | ||
61 | + | ||
62 | + actionsList: edgeActionsList, | ||
63 | + groupActionsList: edgeGroupActionsList, | ||
64 | + | ||
65 | + onGridInited: gridInited, | ||
66 | + | ||
67 | + addItemTemplateUrl: addEdgeTemplate, | ||
68 | + | ||
69 | + addItemText: function() { return $translate.instant('edge.add-edge-text') }, | ||
70 | + noItemsText: function() { return $translate.instant('edge.no-edges-text') }, | ||
71 | + itemDetailsText: function() { return $translate.instant('edge.edge-details') } | ||
72 | + }; | ||
73 | + | ||
74 | + if (angular.isDefined($stateParams.items) && $stateParams.items !== null) { | ||
75 | + vm.edgeGridConfig.items = $stateParams.items; | ||
76 | + } | ||
77 | + | ||
78 | + if (angular.isDefined($stateParams.topIndex) && $stateParams.topIndex > 0) { | ||
79 | + vm.edgeGridConfig.topIndex = $stateParams.topIndex; | ||
80 | + } | ||
81 | + | ||
82 | + initController(); | ||
83 | + | ||
84 | + function initController() { | ||
85 | + var fetchEdgesFunction = function (pageLink, edgeType) { | ||
86 | + return edgeService.getEdges(pageLink, true, edgeType); | ||
87 | + }; | ||
88 | + var deleteEdgeFunction = function (edgeId) { | ||
89 | + return edgeService.deleteEdge(edgeId); | ||
90 | + }; | ||
91 | + var refreshEdgesParamsFunction = function() { | ||
92 | + return {"topIndex": vm.topIndex}; | ||
93 | + }; | ||
94 | + | ||
95 | + edgeActionsList.push( | ||
96 | + { | ||
97 | + onAction: function ($event, item) { | ||
98 | + vm.grid.deleteItem($event, item); | ||
99 | + }, | ||
100 | + name: function() { return $translate.instant('action.delete') }, | ||
101 | + details: function() { return $translate.instant('edge.delete') }, | ||
102 | + icon: "delete", | ||
103 | + isEnabled: function() { | ||
104 | + return userPermissionsService.hasGenericPermission(securityTypes.resource.edge, securityTypes.operation.delete); | ||
105 | + } | ||
106 | + } | ||
107 | + ); | ||
108 | + | ||
109 | + edgeGroupActionsList.push( | ||
110 | + { | ||
111 | + onAction: function ($event) { | ||
112 | + vm.grid.deleteItems($event); | ||
113 | + }, | ||
114 | + name: function() { return $translate.instant('edge.delete-edges') }, | ||
115 | + details: deleteEdgesActionTitle, | ||
116 | + icon: "delete" | ||
117 | + } | ||
118 | + ); | ||
119 | + vm.edgeGridConfig.refreshParamsFunc = refreshEdgesParamsFunction; | ||
120 | + vm.edgeGridConfig.fetchItemsFunc = fetchEdgesFunction; | ||
121 | + vm.edgeGridConfig.deleteItemFunc = deleteEdgeFunction; | ||
122 | + | ||
123 | + } | ||
124 | + | ||
125 | + function deleteEdgeTitle(edge) { | ||
126 | + return $translate.instant('edge.delete-edge-title', {edgeName: edge.name}); | ||
127 | + } | ||
128 | + | ||
129 | + function deleteEdgeText() { | ||
130 | + return $translate.instant('edge.delete-edge-text'); | ||
131 | + } | ||
132 | + | ||
133 | + function deleteEdgesTitle(selectedCount) { | ||
134 | + return $translate.instant('edge.delete-edges-title', {count: selectedCount}, 'messageformat'); | ||
135 | + } | ||
136 | + | ||
137 | + function deleteEdgesActionTitle(selectedCount) { | ||
138 | + return $translate.instant('edge.delete-edges-action-title', {count: selectedCount}, 'messageformat'); | ||
139 | + } | ||
140 | + | ||
141 | + function deleteEdgesText () { | ||
142 | + return $translate.instant('edge.delete-edges-text'); | ||
143 | + } | ||
144 | + | ||
145 | + function gridInited(grid) { | ||
146 | + vm.grid = grid; | ||
147 | + } | ||
148 | + | ||
149 | + function getEdgeTitle(edge) { | ||
150 | + return edge ? edge.name : ''; | ||
151 | + } | ||
152 | + | ||
153 | + function saveEdge(edge) { | ||
154 | + var deferred = $q.defer(); | ||
155 | + edgeService.saveEdge(edge).then( | ||
156 | + function success(savedEdge) { | ||
157 | + $rootScope.$broadcast('edgeSaved'); | ||
158 | + deferred.resolve(savedEdge); | ||
159 | + }, | ||
160 | + function fail() { | ||
161 | + deferred.reject(); | ||
162 | + } | ||
163 | + ); | ||
164 | + return deferred.promise; | ||
165 | + } | ||
166 | +} |
ui/src/app/edge/edge.directive.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2019 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 | +/* eslint-disable import/no-unresolved, import/default */ | ||
17 | + | ||
18 | +import edgeFieldsetTemplate from './edge-fieldset.tpl.html'; | ||
19 | + | ||
20 | +/* eslint-enable import/no-unresolved, import/default */ | ||
21 | + | ||
22 | +/*@ngInject*/ | ||
23 | +export default function EdgeDirective($compile, $templateCache, $translate, $mdDialog, $document, toast, types) { | ||
24 | + var linker = function (scope, element) { | ||
25 | + var template = $templateCache.get(edgeFieldsetTemplate); | ||
26 | + element.html(template); | ||
27 | + | ||
28 | + scope.types = types; | ||
29 | + | ||
30 | + scope.onEdgeIdCopied = function() { | ||
31 | + toast.showSuccess($translate.instant('edge.idCopiedMessage'), 750, angular.element(element).parent().parent(), 'bottom left'); | ||
32 | + }; | ||
33 | + | ||
34 | + $compile(element.contents())(scope); | ||
35 | + | ||
36 | + }; | ||
37 | + return { | ||
38 | + restrict: "E", | ||
39 | + link: linker, | ||
40 | + scope: { | ||
41 | + edge: '=', | ||
42 | + isEdit: '=', | ||
43 | + theForm: '=', | ||
44 | + isCreate: '<', | ||
45 | + onExportEdge: '&', | ||
46 | + onDeleteEdge: '&' | ||
47 | + } | ||
48 | + }; | ||
49 | +} |
ui/src/app/edge/edge.routes.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2019 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 | +/* eslint-disable import/no-unresolved, import/default */ | ||
17 | + | ||
18 | +import edgesTemplate from './edges.tpl.html'; | ||
19 | + | ||
20 | +/* eslint-enable import/no-unresolved, import/default */ | ||
21 | + | ||
22 | +/*@ngInject*/ | ||
23 | +export default function EdgeRoutes($stateProvider) { | ||
24 | + | ||
25 | + $stateProvider | ||
26 | + .state('home.edges', { | ||
27 | + url: '/edges', | ||
28 | + params: {'topIndex': 0}, | ||
29 | + module: 'private', | ||
30 | + auth: ['TENANT_ADMIN'], | ||
31 | + views: { | ||
32 | + "content@home": { | ||
33 | + templateUrl: edgesTemplate, | ||
34 | + controllerAs: 'vm', | ||
35 | + controller: 'EdgeController' | ||
36 | + } | ||
37 | + }, | ||
38 | + data: { | ||
39 | + searchEnabled: true, | ||
40 | + pageTitle: 'edge.edges' | ||
41 | + }, | ||
42 | + ncyBreadcrumb: { | ||
43 | + label: '{"icon": "transform", "label": "edge.edges"}' | ||
44 | + } | ||
45 | + }); | ||
46 | +} |
ui/src/app/edge/edges.tpl.html
0 → 100644
1 | +<!-- | ||
2 | + | ||
3 | + Copyright © 2016-2019 The Thingsboard Authors | ||
4 | + | ||
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + you may not use this file except in compliance with the License. | ||
7 | + You may obtain a copy of the License at | ||
8 | + | ||
9 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + | ||
11 | + Unless required by applicable law or agreed to in writing, software | ||
12 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + See the License for the specific language governing permissions and | ||
15 | + limitations under the License. | ||
16 | + | ||
17 | +--> | ||
18 | +<tb-grid grid-configuration="vm.edgeGridConfig"> | ||
19 | + <details-buttons tb-help="'edges'" help-container-id="help-container"> | ||
20 | + <div id="help-container"></div> | ||
21 | + </details-buttons> | ||
22 | + <md-tabs ng-class="{'tb-headless': vm.grid.detailsConfig.isDetailsEditMode}" | ||
23 | + id="tabs" md-border-bottom flex class="tb-absolute-fill"> | ||
24 | + <md-tab label="{{ 'edge.details' | translate }}"> | ||
25 | + <tb-edge edge="vm.grid.operatingItem()" | ||
26 | + is-edit="vm.grid.detailsConfig.isDetailsEditMode" | ||
27 | + the-form="vm.grid.detailsForm" | ||
28 | + on-delete-edge="vm.grid.deleteItem(event, vm.grid.detailsConfig.currentItem)"></tb-edge> | ||
29 | + </md-tab> | ||
30 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('edge' | hasGenericPermission:'readAttributes')" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.attributes' | translate }}"> | ||
31 | + <tb-attribute-table flex | ||
32 | + readonly="!('edge' | hasGenericPermission:'writeAttributes')" | ||
33 | + entity-id="vm.grid.operatingItem().id.id" | ||
34 | + entity-type="{{vm.types.entityType.edge}}" | ||
35 | + entity-name="vm.grid.operatingItem().name" | ||
36 | + default-attribute-scope="{{vm.types.attributesScope.server.value}}"> | ||
37 | + </tb-attribute-table> | ||
38 | + </md-tab> | ||
39 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('edge' | hasGenericPermission:'readTelemetry')" md-on-select="vm.grid.triggerResize()" label="{{ 'attribute.latest-telemetry' | translate }}"> | ||
40 | + <tb-attribute-table flex | ||
41 | + readonly="!('edge' | hasGenericPermission:'writeTelemetry')" | ||
42 | + entity-id="vm.grid.operatingItem().id.id" | ||
43 | + entity-type="{{vm.types.entityType.edge}}" | ||
44 | + entity-name="vm.grid.operatingItem().name" | ||
45 | + default-attribute-scope="{{vm.types.latestTelemetry.value}}" | ||
46 | + disable-attribute-scope-selection="true"> | ||
47 | + </tb-attribute-table> | ||
48 | + </md-tab> | ||
49 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('alarm' | hasGenericPermission:'read')" md-on-select="vm.grid.triggerResize()" label="{{ 'alarm.alarms' | translate }}"> | ||
50 | + <tb-alarm-table flex entity-type="vm.types.entityType.edge" | ||
51 | + entity-id="vm.grid.operatingItem().id.id"> | ||
52 | + </tb-alarm-table> | ||
53 | + </md-tab> | ||
54 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'edge.events' | translate }}"> | ||
55 | + <tb-event-table flex entity-type="vm.types.entityType.edge" | ||
56 | + entity-id="vm.grid.operatingItem().id.id" | ||
57 | + tenant-id="vm.grid.operatingItem().tenantId.id" | ||
58 | + default-event-type="{{vm.types.eventType.error.value}}"> | ||
59 | + </tb-event-table> | ||
60 | + </md-tab> | ||
61 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}"> | ||
62 | + <tb-relation-table flex | ||
63 | + readonly="!('edge' | hasGenericPermission:'write')" | ||
64 | + entity-id="vm.grid.operatingItem().id.id" | ||
65 | + entity-type="{{vm.types.entityType.edge}}"> | ||
66 | + </tb-relation-table> | ||
67 | + </md-tab> | ||
68 | + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && ('auditLog' | hasGenericPermission:'read')" md-on-select="vm.grid.triggerResize()" label="{{ 'audit-log.audit-logs' | translate }}"> | ||
69 | + <tb-audit-log-table flex entity-type="vm.types.entityType.edge" | ||
70 | + entity-id="vm.grid.operatingItem().id.id" | ||
71 | + audit-log-mode="{{vm.types.auditLogMode.entity}}"> | ||
72 | + </tb-audit-log-table> | ||
73 | + </md-tab> | ||
74 | + </md-tabs> | ||
75 | +</tb-grid> |
ui/src/app/edge/index.js
0 → 100644
1 | +/* | ||
2 | + * Copyright © 2016-2019 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 | +import EdgeRoutes from './edge.routes'; | ||
17 | +import {EdgeController, EdgeCardController} from './edge.controller'; | ||
18 | +import EdgeDirective from './edge.directive'; | ||
19 | + | ||
20 | +export default angular.module('thingsboard.edge', []) | ||
21 | + .config(EdgeRoutes) | ||
22 | + .controller('EdgeController', EdgeController) | ||
23 | + .controller('EdgeCardController', EdgeCardController) | ||
24 | + .directive('tbEdge', EdgeDirective) | ||
25 | + .name; |
@@ -179,6 +179,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | @@ -179,6 +179,12 @@ export default function EntityAutocomplete($compile, $templateCache, $q, $filter | ||
179 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; | 179 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; |
180 | scope.entityRequiredText = 'customer.default-customer-required'; | 180 | scope.entityRequiredText = 'customer.default-customer-required'; |
181 | break; | 181 | break; |
182 | + case types.entityType.edge: | ||
183 | + scope.selectEntityText = 'edge.select-edge'; | ||
184 | + scope.entityText = 'edge.edge'; | ||
185 | + scope.noEntitiesMatchingText = 'edge.no-edges-matching'; | ||
186 | + scope.entityRequiredText = 'edge.edge-required'; | ||
187 | + break; | ||
182 | } | 188 | } |
183 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { | 189 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { |
184 | scope.entity = null; | 190 | scope.entity = null; |
@@ -107,6 +107,7 @@ export default angular.module('thingsboard.help', []) | @@ -107,6 +107,7 @@ export default angular.module('thingsboard.help', []) | ||
107 | widgetsConfigRpc: helpBaseUrl + "/docs/user-guide/ui/dashboards#rpc", | 107 | widgetsConfigRpc: helpBaseUrl + "/docs/user-guide/ui/dashboards#rpc", |
108 | widgetsConfigAlarm: helpBaseUrl + "/docs/user-guide/ui/dashboards#alarm", | 108 | widgetsConfigAlarm: helpBaseUrl + "/docs/user-guide/ui/dashboards#alarm", |
109 | widgetsConfigStatic: helpBaseUrl + "/docs/user-guide/ui/dashboards#static", | 109 | widgetsConfigStatic: helpBaseUrl + "/docs/user-guide/ui/dashboards#static", |
110 | + edges: helpBaseUrl + "/docs/user-guide/ui/edges" | ||
110 | }, | 111 | }, |
111 | getRuleNodeLink: function(ruleNode) { | 112 | getRuleNodeLink: function(ruleNode) { |
112 | if (ruleNode && ruleNode.component) { | 113 | if (ruleNode && ruleNode.component) { |
@@ -52,6 +52,7 @@ import thingsboardEntityView from '../entity-view'; | @@ -52,6 +52,7 @@ import thingsboardEntityView from '../entity-view'; | ||
52 | import thingsboardWidgetLibrary from '../widget'; | 52 | import thingsboardWidgetLibrary from '../widget'; |
53 | import thingsboardDashboard from '../dashboard'; | 53 | import thingsboardDashboard from '../dashboard'; |
54 | import thingsboardRuleChain from '../rulechain'; | 54 | import thingsboardRuleChain from '../rulechain'; |
55 | +import thingsboardEdge from '../edge'; | ||
55 | 56 | ||
56 | import thingsboardJsonForm from '../jsonform'; | 57 | import thingsboardJsonForm from '../jsonform'; |
57 | 58 | ||
@@ -94,7 +95,8 @@ export default angular.module('thingsboard.home', [ | @@ -94,7 +95,8 @@ export default angular.module('thingsboard.home', [ | ||
94 | thingsboardDashboardAutocomplete, | 95 | thingsboardDashboardAutocomplete, |
95 | thingsboardKvMap, | 96 | thingsboardKvMap, |
96 | thingsboardJsonObjectEdit, | 97 | thingsboardJsonObjectEdit, |
97 | - thingsboardJsonContent | 98 | + thingsboardJsonContent, |
99 | + thingsboardEdge | ||
98 | ]) | 100 | ]) |
99 | .config(HomeRoutes) | 101 | .config(HomeRoutes) |
100 | .controller('HomeController', HomeController) | 102 | .controller('HomeController', HomeController) |
@@ -705,6 +705,33 @@ | @@ -705,6 +705,33 @@ | ||
705 | "column": "Column", | 705 | "column": "Column", |
706 | "row": "Row" | 706 | "row": "Row" |
707 | }, | 707 | }, |
708 | + "edge": { | ||
709 | + "edge": "Edge", | ||
710 | + "edges": "Edges", | ||
711 | + "management": "Edge management", | ||
712 | + "no-edges-matching": "No edges matching '{{entity}}' were found.", | ||
713 | + "add": "Add Edge", | ||
714 | + "view": "View Edge", | ||
715 | + "no-edges-text": "No edges found", | ||
716 | + "edge-details": "Edge details", | ||
717 | + "add-edge-text": "Add new edge", | ||
718 | + "delete": "Delete edge", | ||
719 | + "delete-edges": "Delete edges", | ||
720 | + "delete-edge-title": "Are you sure you want to delete the edge '{{edgeName}}'?", | ||
721 | + "delete-edge-text": "Be careful, after the confirmation the edge and all related data will become unrecoverable.", | ||
722 | + "delete-edges-title": "Are you sure you want to edge { count, plural, 1 {1 edge} other {# edges} }?", | ||
723 | + "delete-edges-action-title": "Delete { count, plural, 1 {1 edge} other {# edges} }", | ||
724 | + "delete-edges-text": "Be careful, after the confirmation all selected edges will be removed and all related data will become unrecoverable.", | ||
725 | + "name": "Name", | ||
726 | + "name-required": "Name is required.", | ||
727 | + "description": "Description", | ||
728 | + "events": "Events", | ||
729 | + "details": "Details", | ||
730 | + "copyId": "Copy role Id", | ||
731 | + "idCopiedMessage": "Edge Id has been copied to clipboard", | ||
732 | + "permissions": "Permissions", | ||
733 | + "edge-required": "Edge required", | ||
734 | + }, | ||
708 | "error": { | 735 | "error": { |
709 | "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", | 736 | "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", |
710 | "unhandled-error-code": "Unhandled error code: {{errorCode}}", | 737 | "unhandled-error-code": "Unhandled error code: {{errorCode}}", |
@@ -798,6 +825,10 @@ | @@ -798,6 +825,10 @@ | ||
798 | "type-rulenodes": "Rule nodes", | 825 | "type-rulenodes": "Rule nodes", |
799 | "list-of-rulenodes": "{ count, plural, 1 {One rule node} other {List of # rule nodes} }", | 826 | "list-of-rulenodes": "{ count, plural, 1 {One rule node} other {List of # rule nodes} }", |
800 | "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'", | 827 | "rulenode-name-starts-with": "Rule nodes whose names start with '{{prefix}}'", |
828 | + "type-edge": "Edge", | ||
829 | + "type-edges": "Edges", | ||
830 | + "list-of-edges": "{ count, plural, 1 {One edge} other {List of # edges} }", | ||
831 | + "edge-name-starts-with": "Edges whose names start with '{{prefix}}'", | ||
801 | "type-current-customer": "Current Customer", | 832 | "type-current-customer": "Current Customer", |
802 | "search": "Search entities", | 833 | "search": "Search entities", |
803 | "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected", | 834 | "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected", |
@@ -185,6 +185,12 @@ function Menu(userService, $state, $rootScope) { | @@ -185,6 +185,12 @@ function Menu(userService, $state, $rootScope) { | ||
185 | icon: 'view_quilt' | 185 | icon: 'view_quilt' |
186 | }, | 186 | }, |
187 | { | 187 | { |
188 | + name: 'edge.edges', | ||
189 | + type: 'link', | ||
190 | + state: 'home.edges', | ||
191 | + icon: 'toys' | ||
192 | + }, | ||
193 | + { | ||
188 | name: 'widget.widget-library', | 194 | name: 'widget.widget-library', |
189 | type: 'link', | 195 | type: 'link', |
190 | state: 'home.widgets-bundles', | 196 | state: 'home.widgets-bundles', |
@@ -255,6 +261,16 @@ function Menu(userService, $state, $rootScope) { | @@ -255,6 +261,16 @@ function Menu(userService, $state, $rootScope) { | ||
255 | ] | 261 | ] |
256 | }, | 262 | }, |
257 | { | 263 | { |
264 | + name: 'edge.management', | ||
265 | + places: [ | ||
266 | + { | ||
267 | + name: 'edge.edges', | ||
268 | + icon: 'toys', | ||
269 | + state: 'home.edges' | ||
270 | + } | ||
271 | + ] | ||
272 | + }, | ||
273 | + { | ||
258 | name: 'dashboard.management', | 274 | name: 'dashboard.management', |
259 | places: [ | 275 | places: [ |
260 | { | 276 | { |
@@ -307,6 +323,12 @@ function Menu(userService, $state, $rootScope) { | @@ -307,6 +323,12 @@ function Menu(userService, $state, $rootScope) { | ||
307 | icon: 'view_quilt' | 323 | icon: 'view_quilt' |
308 | }, | 324 | }, |
309 | { | 325 | { |
326 | + name: 'edge.edges', | ||
327 | + type: 'link', | ||
328 | + state: 'home.edges', | ||
329 | + icon: 'toys' | ||
330 | + }, | ||
331 | + { | ||
310 | name: 'dashboard.dashboards', | 332 | name: 'dashboard.dashboards', |
311 | type: 'link', | 333 | type: 'link', |
312 | state: 'home.dashboards', | 334 | state: 'home.dashboards', |
@@ -345,6 +367,16 @@ function Menu(userService, $state, $rootScope) { | @@ -345,6 +367,16 @@ function Menu(userService, $state, $rootScope) { | ||
345 | ] | 367 | ] |
346 | }, | 368 | }, |
347 | { | 369 | { |
370 | + name: 'edge.management', | ||
371 | + places: [ | ||
372 | + { | ||
373 | + name: 'edge.edges', | ||
374 | + icon: 'toys', | ||
375 | + state: 'home.edges' | ||
376 | + } | ||
377 | + ] | ||
378 | + }, | ||
379 | + { | ||
348 | name: 'dashboard.view-dashboards', | 380 | name: 'dashboard.view-dashboards', |
349 | places: [ | 381 | places: [ |
350 | { | 382 | { |