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 ); | |
\ No newline at end of file | ... | ... |
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 | +); | |
\ No newline at end of file | ... | ... |
... | ... | @@ -43,12 +43,14 @@ import org.thingsboard.server.common.data.alarm.AlarmId; |
43 | 43 | import org.thingsboard.server.common.data.alarm.AlarmInfo; |
44 | 44 | import org.thingsboard.server.common.data.asset.Asset; |
45 | 45 | import org.thingsboard.server.common.data.audit.ActionType; |
46 | +import org.thingsboard.server.common.data.edge.Edge; | |
46 | 47 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
47 | 48 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
48 | 49 | import org.thingsboard.server.common.data.id.AssetId; |
49 | 50 | import org.thingsboard.server.common.data.id.CustomerId; |
50 | 51 | import org.thingsboard.server.common.data.id.DashboardId; |
51 | 52 | import org.thingsboard.server.common.data.id.DeviceId; |
53 | +import org.thingsboard.server.common.data.id.EdgeId; | |
52 | 54 | import org.thingsboard.server.common.data.id.EntityId; |
53 | 55 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
54 | 56 | import org.thingsboard.server.common.data.id.EntityViewId; |
... | ... | @@ -82,6 +84,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; |
82 | 84 | import org.thingsboard.server.dao.device.ClaimDevicesService; |
83 | 85 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
84 | 86 | import org.thingsboard.server.dao.device.DeviceService; |
87 | +import org.thingsboard.server.dao.edge.EdgeService; | |
85 | 88 | import org.thingsboard.server.dao.entityview.EntityViewService; |
86 | 89 | import org.thingsboard.server.dao.exception.DataValidationException; |
87 | 90 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
... | ... | @@ -185,6 +188,9 @@ public abstract class BaseController { |
185 | 188 | @Autowired |
186 | 189 | protected ClaimDevicesService claimDevicesService; |
187 | 190 | |
191 | + @Autowired | |
192 | + protected EdgeService edgeService; | |
193 | + | |
188 | 194 | @Value("${server.log_controller_error_stack_trace}") |
189 | 195 | @Getter |
190 | 196 | private boolean logControllerErrorStackTrace; |
... | ... | @@ -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 | 479 | DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException { |
462 | 480 | try { |
463 | 481 | validateId(dashboardId, "Incorrect dashboardId " + dashboardId); |
... | ... | @@ -513,7 +531,6 @@ public abstract class BaseController { |
513 | 531 | return ruleNode; |
514 | 532 | } |
515 | 533 | |
516 | - | |
517 | 534 | protected String constructBaseUrl(HttpServletRequest request) { |
518 | 535 | String scheme = request.getScheme(); |
519 | 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 | 119 | case "2.4.0": |
120 | 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 | 127 | log.info("Updating system data..."); |
123 | 128 | |
124 | 129 | systemDataLoaderService.deleteSystemWidgetBundle("charts"); | ... | ... |
... | ... | @@ -267,6 +267,14 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { |
267 | 267 | } catch (InvalidQueryException e) {} |
268 | 268 | log.info("Schema updated."); |
269 | 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 | 278 | default: |
271 | 279 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
272 | 280 | } | ... | ... |
... | ... | @@ -176,6 +176,14 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { |
176 | 176 | log.info("Schema updated."); |
177 | 177 | } |
178 | 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 | 187 | default: |
180 | 188 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
181 | 189 | } | ... | ... |
... | ... | @@ -31,7 +31,8 @@ public enum Resource { |
31 | 31 | RULE_CHAIN(EntityType.RULE_CHAIN), |
32 | 32 | USER(EntityType.USER), |
33 | 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 | 37 | private final EntityType entityType; |
37 | 38 | ... | ... |
... | ... | @@ -42,6 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions { |
42 | 42 | put(Resource.USER, userPermissionChecker); |
43 | 43 | put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); |
44 | 44 | put(Resource.WIDGET_TYPE, widgetsPermissionChecker); |
45 | + put(Resource.EDGE, tenantEntityPermissionChecker); | |
45 | 46 | } |
46 | 47 | |
47 | 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 | 19 | * @author Andrew Shvayka |
20 | 20 | */ |
21 | 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 | 63 | return new WidgetsBundleId(uuid); |
64 | 64 | case WIDGET_TYPE: |
65 | 65 | return new WidgetTypeId(uuid); |
66 | + case EDGE: | |
67 | + return new EdgeId(uuid); | |
66 | 68 | } |
67 | 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 | 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 | 363 | * Cassandra attributes and timeseries constants. |
352 | 364 | */ |
353 | 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 | 711 | AND search_text IS NOT NULL |
712 | 712 | AND id IS NOT NULL |
713 | 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); | |
\ No newline at end of file | ||
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 ); | |
\ No newline at end of file | ... | ... |
... | ... | @@ -243,3 +243,12 @@ CREATE TABLE IF NOT EXISTS entity_view ( |
243 | 243 | search_text varchar(255), |
244 | 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 | +); | |
\ No newline at end of file | ... | ... |
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 | 99 | import thingsboardApiAuditLog from './api/audit-log.service'; |
100 | 100 | import thingsboardApiComponentDescriptor from './api/component-descriptor.service'; |
101 | 101 | import thingsboardApiRuleChain from './api/rule-chain.service'; |
102 | +import thingsboardApiEdge from './api/edge.service'; | |
102 | 103 | |
103 | 104 | import AppConfig from './app.config'; |
104 | 105 | import GlobalInterceptor from './global-interceptor.service'; |
... | ... | @@ -157,6 +158,7 @@ angular.module('thingsboard', [ |
157 | 158 | thingsboardApiAuditLog, |
158 | 159 | thingsboardApiComponentDescriptor, |
159 | 160 | thingsboardApiRuleChain, |
161 | + thingsboardApiEdge, | |
160 | 162 | uiRouter]) |
161 | 163 | .config(AppConfig) |
162 | 164 | .factory('globalInterceptor', GlobalInterceptor) | ... | ... |
... | ... | @@ -358,7 +358,8 @@ export default angular.module('thingsboard.types', []) |
358 | 358 | alarm: "ALARM", |
359 | 359 | rulechain: "RULE_CHAIN", |
360 | 360 | rulenode: "RULE_NODE", |
361 | - entityView: "ENTITY_VIEW" | |
361 | + entityView: "ENTITY_VIEW", | |
362 | + edge: "EDGE" | |
362 | 363 | }, |
363 | 364 | importEntityColumnType: { |
364 | 365 | name: { |
... | ... | @@ -461,7 +462,13 @@ export default angular.module('thingsboard.types', []) |
461 | 462 | "CURRENT_CUSTOMER": { |
462 | 463 | type: 'entity.type-current-customer', |
463 | 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 | 473 | entitySearchDirection: { |
467 | 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 | 179 | scope.noEntitiesMatchingText = 'customer.no-customers-matching'; |
180 | 180 | scope.entityRequiredText = 'customer.default-customer-required'; |
181 | 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 | 189 | if (scope.entity && scope.entity.id.entityType != scope.entityType) { |
184 | 190 | scope.entity = null; | ... | ... |
... | ... | @@ -107,6 +107,7 @@ export default angular.module('thingsboard.help', []) |
107 | 107 | widgetsConfigRpc: helpBaseUrl + "/docs/user-guide/ui/dashboards#rpc", |
108 | 108 | widgetsConfigAlarm: helpBaseUrl + "/docs/user-guide/ui/dashboards#alarm", |
109 | 109 | widgetsConfigStatic: helpBaseUrl + "/docs/user-guide/ui/dashboards#static", |
110 | + edges: helpBaseUrl + "/docs/user-guide/ui/edges" | |
110 | 111 | }, |
111 | 112 | getRuleNodeLink: function(ruleNode) { |
112 | 113 | if (ruleNode && ruleNode.component) { | ... | ... |
... | ... | @@ -52,6 +52,7 @@ import thingsboardEntityView from '../entity-view'; |
52 | 52 | import thingsboardWidgetLibrary from '../widget'; |
53 | 53 | import thingsboardDashboard from '../dashboard'; |
54 | 54 | import thingsboardRuleChain from '../rulechain'; |
55 | +import thingsboardEdge from '../edge'; | |
55 | 56 | |
56 | 57 | import thingsboardJsonForm from '../jsonform'; |
57 | 58 | |
... | ... | @@ -94,7 +95,8 @@ export default angular.module('thingsboard.home', [ |
94 | 95 | thingsboardDashboardAutocomplete, |
95 | 96 | thingsboardKvMap, |
96 | 97 | thingsboardJsonObjectEdit, |
97 | - thingsboardJsonContent | |
98 | + thingsboardJsonContent, | |
99 | + thingsboardEdge | |
98 | 100 | ]) |
99 | 101 | .config(HomeRoutes) |
100 | 102 | .controller('HomeController', HomeController) | ... | ... |
... | ... | @@ -705,6 +705,33 @@ |
705 | 705 | "column": "Column", |
706 | 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 | 735 | "error": { |
709 | 736 | "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", |
710 | 737 | "unhandled-error-code": "Unhandled error code: {{errorCode}}", |
... | ... | @@ -798,6 +825,10 @@ |
798 | 825 | "type-rulenodes": "Rule nodes", |
799 | 826 | "list-of-rulenodes": "{ count, plural, 1 {One rule node} other {List of # rule nodes} }", |
800 | 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 | 832 | "type-current-customer": "Current Customer", |
802 | 833 | "search": "Search entities", |
803 | 834 | "selected-entities": "{ count, plural, 1 {1 entity} other {# entities} } selected", | ... | ... |
... | ... | @@ -185,6 +185,12 @@ function Menu(userService, $state, $rootScope) { |
185 | 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 | 194 | name: 'widget.widget-library', |
189 | 195 | type: 'link', |
190 | 196 | state: 'home.widgets-bundles', |
... | ... | @@ -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 | 274 | name: 'dashboard.management', |
259 | 275 | places: [ |
260 | 276 | { |
... | ... | @@ -307,6 +323,12 @@ function Menu(userService, $state, $rootScope) { |
307 | 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 | 332 | name: 'dashboard.dashboards', |
311 | 333 | type: 'link', |
312 | 334 | state: 'home.dashboards', |
... | ... | @@ -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 | 380 | name: 'dashboard.view-dashboards', |
349 | 381 | places: [ |
350 | 382 | { | ... | ... |