Commit 33592ca0724dd1005390dbd3068f8beb32b57089
1 parent
940c81b0
Bulk import for devices, assets and edges
Showing
22 changed files
with
1025 additions
and
61 deletions
... | ... | @@ -16,9 +16,12 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | +import lombok.RequiredArgsConstructor; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
19 | 21 | import org.springframework.http.HttpStatus; |
20 | 22 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | 23 | import org.springframework.web.bind.annotation.PathVariable; |
24 | +import org.springframework.web.bind.annotation.PostMapping; | |
22 | 25 | import org.springframework.web.bind.annotation.RequestBody; |
23 | 26 | import org.springframework.web.bind.annotation.RequestMapping; |
24 | 27 | import org.springframework.web.bind.annotation.RequestMethod; |
... | ... | @@ -34,7 +37,6 @@ import org.thingsboard.server.common.data.asset.AssetInfo; |
34 | 37 | import org.thingsboard.server.common.data.asset.AssetSearchQuery; |
35 | 38 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | 39 | import org.thingsboard.server.common.data.edge.Edge; |
37 | -import org.thingsboard.server.common.data.edge.EdgeEventType; | |
38 | 40 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
39 | 41 | import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
40 | 42 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
... | ... | @@ -48,6 +50,9 @@ import org.thingsboard.server.common.data.page.TimePageLink; |
48 | 50 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
49 | 51 | import org.thingsboard.server.dao.model.ModelConstants; |
50 | 52 | import org.thingsboard.server.queue.util.TbCoreComponent; |
53 | +import org.thingsboard.server.service.asset.AssetBulkImportService; | |
54 | +import org.thingsboard.server.service.importing.BulkImportRequest; | |
55 | +import org.thingsboard.server.service.importing.BulkImportResult; | |
51 | 56 | import org.thingsboard.server.service.security.model.SecurityUser; |
52 | 57 | import org.thingsboard.server.service.security.permission.Operation; |
53 | 58 | import org.thingsboard.server.service.security.permission.Resource; |
... | ... | @@ -63,7 +68,10 @@ import static org.thingsboard.server.controller.EdgeController.EDGE_ID; |
63 | 68 | @RestController |
64 | 69 | @TbCoreComponent |
65 | 70 | @RequestMapping("/api") |
71 | +@RequiredArgsConstructor | |
72 | +@Slf4j | |
66 | 73 | public class AssetController extends BaseController { |
74 | + private final AssetBulkImportService assetBulkImportService; | |
67 | 75 | |
68 | 76 | public static final String ASSET_ID = "assetId"; |
69 | 77 | |
... | ... | @@ -108,13 +116,7 @@ public class AssetController extends BaseController { |
108 | 116 | |
109 | 117 | Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); |
110 | 118 | |
111 | - logEntityAction(savedAsset.getId(), savedAsset, | |
112 | - savedAsset.getCustomerId(), | |
113 | - asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); | |
114 | - | |
115 | - if (asset.getId() != null) { | |
116 | - sendEntityNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED); | |
117 | - } | |
119 | + onAssetCreatedOrUpdated(savedAsset, asset.getId() != null); | |
118 | 120 | |
119 | 121 | return savedAsset; |
120 | 122 | } catch (Exception e) { |
... | ... | @@ -124,6 +126,20 @@ public class AssetController extends BaseController { |
124 | 126 | } |
125 | 127 | } |
126 | 128 | |
129 | + private void onAssetCreatedOrUpdated(Asset asset, boolean updated) { | |
130 | + try { | |
131 | + logEntityAction(asset.getId(), asset, | |
132 | + asset.getCustomerId(), | |
133 | + updated ? ActionType.UPDATED : ActionType.ADDED, null); | |
134 | + } catch (ThingsboardException e) { | |
135 | + log.error("Failed to log entity action", e); | |
136 | + } | |
137 | + | |
138 | + if (updated) { | |
139 | + sendEntityNotificationMsg(asset.getTenantId(), asset.getId(), EdgeEventActionType.UPDATED); | |
140 | + } | |
141 | + } | |
142 | + | |
127 | 143 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
128 | 144 | @RequestMapping(value = "/asset/{assetId}", method = RequestMethod.DELETE) |
129 | 145 | @ResponseStatus(value = HttpStatus.OK) |
... | ... | @@ -258,7 +274,7 @@ public class AssetController extends BaseController { |
258 | 274 | try { |
259 | 275 | TenantId tenantId = getCurrentUser().getTenantId(); |
260 | 276 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
261 | - if (type != null && type.trim().length()>0) { | |
277 | + if (type != null && type.trim().length() > 0) { | |
262 | 278 | return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink)); |
263 | 279 | } else { |
264 | 280 | return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); |
... | ... | @@ -321,7 +337,7 @@ public class AssetController extends BaseController { |
321 | 337 | CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
322 | 338 | checkCustomerId(customerId, Operation.READ); |
323 | 339 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
324 | - if (type != null && type.trim().length()>0) { | |
340 | + if (type != null && type.trim().length() > 0) { | |
325 | 341 | return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); |
326 | 342 | } else { |
327 | 343 | return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); |
... | ... | @@ -426,7 +442,7 @@ public class AssetController extends BaseController { |
426 | 442 | @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST) |
427 | 443 | @ResponseBody |
428 | 444 | public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId, |
429 | - @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { | |
445 | + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException { | |
430 | 446 | checkParameter(EDGE_ID, strEdgeId); |
431 | 447 | checkParameter(ASSET_ID, strAssetId); |
432 | 448 | try { |
... | ... | @@ -444,7 +460,7 @@ public class AssetController extends BaseController { |
444 | 460 | |
445 | 461 | sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedAsset.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE); |
446 | 462 | |
447 | - return savedAsset; | |
463 | + return savedAsset; | |
448 | 464 | } catch (Exception e) { |
449 | 465 | |
450 | 466 | logEntityAction(emptyId(EntityType.ASSET), null, |
... | ... | @@ -530,4 +546,13 @@ public class AssetController extends BaseController { |
530 | 546 | throw handleException(e); |
531 | 547 | } |
532 | 548 | } |
549 | + | |
550 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
551 | + @PostMapping("/asset/bulk_import") | |
552 | + public BulkImportResult<Asset> processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { | |
553 | + return assetBulkImportService.processBulkImport(request, getCurrentUser(), importedAssetInfo -> { | |
554 | + onAssetCreatedOrUpdated(importedAssetInfo.getEntity(), importedAssetInfo.isUpdated()); | |
555 | + }); | |
556 | + } | |
557 | + | |
533 | 558 | } | ... | ... |
... | ... | @@ -122,7 +122,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; |
122 | 122 | import org.thingsboard.server.queue.discovery.PartitionService; |
123 | 123 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
124 | 124 | import org.thingsboard.server.queue.util.TbCoreComponent; |
125 | -import org.thingsboard.server.service.action.RuleEngineEntityActionService; | |
125 | +import org.thingsboard.server.service.action.EntityActionService; | |
126 | 126 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
127 | 127 | import org.thingsboard.server.service.edge.EdgeNotificationService; |
128 | 128 | import org.thingsboard.server.service.edge.rpc.EdgeRpcService; |
... | ... | @@ -277,7 +277,7 @@ public abstract class BaseController { |
277 | 277 | protected EdgeRpcService edgeGrpcService; |
278 | 278 | |
279 | 279 | @Autowired |
280 | - protected RuleEngineEntityActionService ruleEngineEntityActionService; | |
280 | + protected EntityActionService entityActionService; | |
281 | 281 | |
282 | 282 | @Value("${server.log_controller_error_stack_trace}") |
283 | 283 | @Getter |
... | ... | @@ -817,13 +817,7 @@ public abstract class BaseController { |
817 | 817 | |
818 | 818 | protected <E extends HasName, I extends EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId, |
819 | 819 | ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { |
820 | - if (customerId == null || customerId.isNullUid()) { | |
821 | - customerId = user.getCustomerId(); | |
822 | - } | |
823 | - if (e == null) { | |
824 | - ruleEngineEntityActionService.pushEntityActionToRuleEngine(entityId, entity, user.getTenantId(), customerId, actionType, user, additionalInfo); | |
825 | - } | |
826 | - auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); | |
820 | + entityActionService.logEntityAction(user, entityId, entity, customerId, actionType, e, additionalInfo); | |
827 | 821 | } |
828 | 822 | |
829 | 823 | ... | ... |
... | ... | @@ -19,10 +19,13 @@ import com.google.common.util.concurrent.FutureCallback; |
19 | 19 | import com.google.common.util.concurrent.Futures; |
20 | 20 | import com.google.common.util.concurrent.ListenableFuture; |
21 | 21 | import com.google.common.util.concurrent.MoreExecutors; |
22 | +import lombok.RequiredArgsConstructor; | |
23 | +import lombok.extern.slf4j.Slf4j; | |
22 | 24 | import org.springframework.http.HttpStatus; |
23 | 25 | import org.springframework.http.ResponseEntity; |
24 | 26 | import org.springframework.security.access.prepost.PreAuthorize; |
25 | 27 | import org.springframework.web.bind.annotation.PathVariable; |
28 | +import org.springframework.web.bind.annotation.PostMapping; | |
26 | 29 | import org.springframework.web.bind.annotation.RequestBody; |
27 | 30 | import org.springframework.web.bind.annotation.RequestMapping; |
28 | 31 | import org.springframework.web.bind.annotation.RequestMethod; |
... | ... | @@ -68,6 +71,9 @@ import org.thingsboard.server.dao.device.claim.ReclaimResult; |
68 | 71 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
69 | 72 | import org.thingsboard.server.dao.model.ModelConstants; |
70 | 73 | import org.thingsboard.server.queue.util.TbCoreComponent; |
74 | +import org.thingsboard.server.service.device.DeviceBulkImportService; | |
75 | +import org.thingsboard.server.service.importing.BulkImportRequest; | |
76 | +import org.thingsboard.server.service.importing.BulkImportResult; | |
71 | 77 | import org.thingsboard.server.service.security.model.SecurityUser; |
72 | 78 | import org.thingsboard.server.service.security.permission.Operation; |
73 | 79 | import org.thingsboard.server.service.security.permission.Resource; |
... | ... | @@ -84,7 +90,10 @@ import static org.thingsboard.server.controller.EdgeController.EDGE_ID; |
84 | 90 | @RestController |
85 | 91 | @TbCoreComponent |
86 | 92 | @RequestMapping("/api") |
93 | +@RequiredArgsConstructor | |
94 | +@Slf4j | |
87 | 95 | public class DeviceController extends BaseController { |
96 | + private final DeviceBulkImportService deviceBulkImportService; | |
88 | 97 | |
89 | 98 | private static final String DEVICE_ID = "deviceId"; |
90 | 99 | private static final String DEVICE_NAME = "deviceName"; |
... | ... | @@ -136,24 +145,7 @@ public class DeviceController extends BaseController { |
136 | 145 | |
137 | 146 | Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); |
138 | 147 | |
139 | - tbClusterService.onDeviceChange(savedDevice, null); | |
140 | - tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), | |
141 | - savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); | |
142 | - tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | |
143 | - | |
144 | - if (!created) { | |
145 | - sendEntityNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventActionType.UPDATED); | |
146 | - } | |
147 | - | |
148 | - logEntityAction(savedDevice.getId(), savedDevice, | |
149 | - savedDevice.getCustomerId(), | |
150 | - created ? ActionType.ADDED : ActionType.UPDATED, null); | |
151 | - | |
152 | - if (device.getId() == null) { | |
153 | - deviceStateService.onDeviceAdded(savedDevice); | |
154 | - } else { | |
155 | - deviceStateService.onDeviceUpdated(savedDevice); | |
156 | - } | |
148 | + onDeviceCreatedOrUpdated(savedDevice, !created); | |
157 | 149 | |
158 | 150 | otaPackageStateService.update(savedDevice, oldDevice); |
159 | 151 | |
... | ... | @@ -166,6 +158,31 @@ public class DeviceController extends BaseController { |
166 | 158 | |
167 | 159 | } |
168 | 160 | |
161 | + private void onDeviceCreatedOrUpdated(Device device, boolean updated) { | |
162 | + tbClusterService.onDeviceChange(device, null); | |
163 | + tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(), | |
164 | + device.getId(), device.getName(), device.getType()), null); | |
165 | + tbClusterService.onEntityStateChange(device.getTenantId(), device.getId(), updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); | |
166 | + | |
167 | + if (updated) { | |
168 | + sendEntityNotificationMsg(device.getTenantId(), device.getId(), EdgeEventActionType.UPDATED); | |
169 | + } | |
170 | + | |
171 | + try { | |
172 | + logEntityAction(device.getId(), device, | |
173 | + device.getCustomerId(), | |
174 | + updated ? ActionType.UPDATED : ActionType.ADDED, null); | |
175 | + } catch (ThingsboardException e) { | |
176 | + log.error("Failed to log entity action", e); | |
177 | + } | |
178 | + | |
179 | + if (updated) { | |
180 | + deviceStateService.onDeviceUpdated(device); | |
181 | + } else { | |
182 | + deviceStateService.onDeviceAdded(device); | |
183 | + } | |
184 | + } | |
185 | + | |
169 | 186 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
170 | 187 | @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.DELETE) |
171 | 188 | @ResponseStatus(value = HttpStatus.OK) |
... | ... | @@ -797,4 +814,14 @@ public class DeviceController extends BaseController { |
797 | 814 | throw handleException(e); |
798 | 815 | } |
799 | 816 | } |
817 | + | |
818 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
819 | + @PostMapping("/device/bulk_import") | |
820 | + public BulkImportResult<Device> processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception { | |
821 | + return deviceBulkImportService.processBulkImport(request, getCurrentUser(), importedDeviceInfo -> { | |
822 | + onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.isUpdated()); | |
823 | + otaPackageStateService.update(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity()); | |
824 | + }); | |
825 | + } | |
826 | + | |
800 | 827 | } | ... | ... |
... | ... | @@ -16,9 +16,11 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | +import lombok.RequiredArgsConstructor; | |
19 | 20 | import org.springframework.http.HttpStatus; |
20 | 21 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | 22 | import org.springframework.web.bind.annotation.PathVariable; |
23 | +import org.springframework.web.bind.annotation.PostMapping; | |
22 | 24 | import org.springframework.web.bind.annotation.RequestBody; |
23 | 25 | import org.springframework.web.bind.annotation.RequestMapping; |
24 | 26 | import org.springframework.web.bind.annotation.RequestMethod; |
... | ... | @@ -49,10 +51,14 @@ import org.thingsboard.server.dao.exception.DataValidationException; |
49 | 51 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
50 | 52 | import org.thingsboard.server.dao.model.ModelConstants; |
51 | 53 | import org.thingsboard.server.queue.util.TbCoreComponent; |
54 | +import org.thingsboard.server.service.edge.EdgeBulkImportService; | |
55 | +import org.thingsboard.server.service.importing.BulkImportRequest; | |
56 | +import org.thingsboard.server.service.importing.BulkImportResult; | |
52 | 57 | import org.thingsboard.server.service.security.model.SecurityUser; |
53 | 58 | import org.thingsboard.server.service.security.permission.Operation; |
54 | 59 | import org.thingsboard.server.service.security.permission.Resource; |
55 | 60 | |
61 | +import java.io.IOException; | |
56 | 62 | import java.util.ArrayList; |
57 | 63 | import java.util.List; |
58 | 64 | import java.util.stream.Collectors; |
... | ... | @@ -60,7 +66,9 @@ import java.util.stream.Collectors; |
60 | 66 | @RestController |
61 | 67 | @TbCoreComponent |
62 | 68 | @RequestMapping("/api") |
69 | +@RequiredArgsConstructor | |
63 | 70 | public class EdgeController extends BaseController { |
71 | + private final EdgeBulkImportService edgeBulkImportService; | |
64 | 72 | |
65 | 73 | public static final String EDGE_ID = "edgeId"; |
66 | 74 | |
... | ... | @@ -128,17 +136,8 @@ public class EdgeController extends BaseController { |
128 | 136 | edge.getId(), edge); |
129 | 137 | |
130 | 138 | Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true)); |
139 | + onEdgeCreatedOrUpdated(tenantId, savedEdge, edgeTemplateRootRuleChain, !created); | |
131 | 140 | |
132 | - if (created) { | |
133 | - ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId()); | |
134 | - edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, edgeTemplateRootRuleChain.getId()); | |
135 | - edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId()); | |
136 | - } | |
137 | - | |
138 | - tbClusterService.onEntityStateChange(savedEdge.getTenantId(), savedEdge.getId(), | |
139 | - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | |
140 | - | |
141 | - logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null); | |
142 | 141 | return savedEdge; |
143 | 142 | } catch (Exception e) { |
144 | 143 | logEntityAction(emptyId(EntityType.EDGE), edge, |
... | ... | @@ -147,6 +146,19 @@ public class EdgeController extends BaseController { |
147 | 146 | } |
148 | 147 | } |
149 | 148 | |
149 | + private void onEdgeCreatedOrUpdated(TenantId tenantId, Edge edge, RuleChain edgeTemplateRootRuleChain, boolean updated) throws IOException, ThingsboardException { | |
150 | + if (!updated) { | |
151 | + ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), edge.getId()); | |
152 | + edgeNotificationService.setEdgeRootRuleChain(tenantId, edge, edgeTemplateRootRuleChain.getId()); | |
153 | + edgeService.assignDefaultRuleChainsToEdge(tenantId, edge.getId()); | |
154 | + } | |
155 | + | |
156 | + tbClusterService.onEntityStateChange(edge.getTenantId(), edge.getId(), | |
157 | + updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED); | |
158 | + | |
159 | + logEntityAction(edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null); | |
160 | + } | |
161 | + | |
150 | 162 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
151 | 163 | @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE) |
152 | 164 | @ResponseStatus(value = HttpStatus.OK) |
... | ... | @@ -580,6 +592,24 @@ public class EdgeController extends BaseController { |
580 | 592 | } |
581 | 593 | } |
582 | 594 | |
595 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
596 | + @PostMapping("/edge/bulk_import") | |
597 | + public BulkImportResult<Edge> processEdgeBulkImport(@RequestBody BulkImportRequest request) throws Exception { | |
598 | + SecurityUser user = getCurrentUser(); | |
599 | + RuleChain edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(user.getTenantId()); | |
600 | + if (edgeTemplateRootRuleChain == null) { | |
601 | + throw new DataValidationException("Root edge rule chain is not available!"); | |
602 | + } | |
603 | + | |
604 | + return edgeBulkImportService.processBulkImport(request, user, importedAssetInfo -> { | |
605 | + try { | |
606 | + onEdgeCreatedOrUpdated(user.getTenantId(), importedAssetInfo.getEntity(), edgeTemplateRootRuleChain, importedAssetInfo.isUpdated()); | |
607 | + } catch (Exception e) { | |
608 | + throw new RuntimeException(e); | |
609 | + } | |
610 | + }); | |
611 | + } | |
612 | + | |
583 | 613 | private void cleanUpLicenseKey(Edge edge) { |
584 | 614 | edge.setEdgeLicenseKey(null); |
585 | 615 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
renamed from
application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.HasName; |
28 | 28 | import org.thingsboard.server.common.data.HasTenantId; |
29 | 29 | import org.thingsboard.server.common.data.User; |
30 | 30 | import org.thingsboard.server.common.data.audit.ActionType; |
31 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
31 | 32 | import org.thingsboard.server.common.data.id.CustomerId; |
32 | 33 | import org.thingsboard.server.common.data.id.EntityId; |
33 | 34 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; |
38 | 39 | import org.thingsboard.server.common.msg.TbMsg; |
39 | 40 | import org.thingsboard.server.common.msg.TbMsgDataType; |
40 | 41 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
42 | +import org.thingsboard.server.dao.audit.AuditLogService; | |
41 | 43 | import org.thingsboard.server.queue.util.TbCoreComponent; |
42 | 44 | import org.thingsboard.server.service.queue.TbClusterService; |
43 | 45 | |
... | ... | @@ -49,8 +51,9 @@ import java.util.stream.Collectors; |
49 | 51 | @Service |
50 | 52 | @RequiredArgsConstructor |
51 | 53 | @Slf4j |
52 | -public class RuleEngineEntityActionService { | |
54 | +public class EntityActionService { | |
53 | 55 | private final TbClusterService tbClusterService; |
56 | + private final AuditLogService auditLogService; | |
54 | 57 | |
55 | 58 | private static final ObjectMapper json = new ObjectMapper(); |
56 | 59 | |
... | ... | @@ -209,6 +212,17 @@ public class RuleEngineEntityActionService { |
209 | 212 | } |
210 | 213 | } |
211 | 214 | |
215 | + public <E extends HasName, I extends EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId, | |
216 | + ActionType actionType, Exception e, Object... additionalInfo) { | |
217 | + if (customerId == null || customerId.isNullUid()) { | |
218 | + customerId = user.getCustomerId(); | |
219 | + } | |
220 | + if (e == null) { | |
221 | + pushEntityActionToRuleEngine(entityId, entity, user.getTenantId(), customerId, actionType, user, additionalInfo); | |
222 | + } | |
223 | + auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); | |
224 | + } | |
225 | + | |
212 | 226 | |
213 | 227 | private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) { |
214 | 228 | T result = null; | ... | ... |
application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.asset; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
19 | +import com.fasterxml.jackson.databind.node.TextNode; | |
20 | +import org.springframework.stereotype.Service; | |
21 | +import org.thingsboard.common.util.JacksonUtil; | |
22 | +import org.thingsboard.server.common.data.asset.Asset; | |
23 | +import org.thingsboard.server.dao.asset.AssetService; | |
24 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
25 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
26 | +import org.thingsboard.server.service.action.EntityActionService; | |
27 | +import org.thingsboard.server.service.importing.AbstractBulkImportService; | |
28 | +import org.thingsboard.server.service.importing.BulkImportRequest; | |
29 | +import org.thingsboard.server.service.importing.ImportedEntityInfo; | |
30 | +import org.thingsboard.server.service.queue.TbClusterService; | |
31 | +import org.thingsboard.server.service.security.AccessValidator; | |
32 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
33 | +import org.thingsboard.server.service.security.permission.AccessControlService; | |
34 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
35 | + | |
36 | +import java.util.Map; | |
37 | +import java.util.Optional; | |
38 | + | |
39 | + | |
40 | +@Service | |
41 | +@TbCoreComponent | |
42 | +public class AssetBulkImportService extends AbstractBulkImportService<Asset> { | |
43 | + private final AssetService assetService; | |
44 | + | |
45 | + public AssetBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, | |
46 | + AccessControlService accessControlService, AccessValidator accessValidator, | |
47 | + EntityActionService entityActionService, TbClusterService clusterService, AssetService assetService) { | |
48 | + super(tsSubscriptionService, tenantProfileCache, accessControlService, accessValidator, entityActionService, clusterService); | |
49 | + this.assetService = assetService; | |
50 | + } | |
51 | + | |
52 | + @Override | |
53 | + protected ImportedEntityInfo<Asset> saveEntity(BulkImportRequest importRequest, Map<BulkImportRequest.ColumnMapping, String> entityData, SecurityUser user) { | |
54 | + ImportedEntityInfo<Asset> importedEntityInfo = new ImportedEntityInfo<>(); | |
55 | + | |
56 | + Asset asset = new Asset(); | |
57 | + asset.setTenantId(user.getTenantId()); | |
58 | + setAssetFields(asset, entityData); | |
59 | + | |
60 | + Asset existingAsset = assetService.findAssetByTenantIdAndName(user.getTenantId(), asset.getName()); | |
61 | + if (existingAsset != null && importRequest.getMapping().getUpdate()) { | |
62 | + importedEntityInfo.setOldEntity(new Asset(existingAsset)); | |
63 | + importedEntityInfo.setUpdated(true); | |
64 | + existingAsset.update(asset); | |
65 | + asset = existingAsset; | |
66 | + } | |
67 | + asset = assetService.saveAsset(asset); | |
68 | + | |
69 | + importedEntityInfo.setEntity(asset); | |
70 | + return importedEntityInfo; | |
71 | + } | |
72 | + | |
73 | + private void setAssetFields(Asset asset, Map<BulkImportRequest.ColumnMapping, String> data) { | |
74 | + ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(asset.getAdditionalInfo()).orElseGet(JacksonUtil::newObjectNode); | |
75 | + data.forEach((columnMapping, value) -> { | |
76 | + switch (columnMapping.getType()) { | |
77 | + case NAME: | |
78 | + asset.setName(value); | |
79 | + break; | |
80 | + case TYPE: | |
81 | + asset.setType(value); | |
82 | + break; | |
83 | + case LABEL: | |
84 | + asset.setLabel(value); | |
85 | + break; | |
86 | + case DESCRIPTION: | |
87 | + additionalInfo.set("description", new TextNode(value)); | |
88 | + break; | |
89 | + } | |
90 | + }); | |
91 | + asset.setAdditionalInfo(additionalInfo); | |
92 | + } | |
93 | + | |
94 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.device; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.node.BooleanNode; | |
19 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
20 | +import com.fasterxml.jackson.databind.node.TextNode; | |
21 | +import com.google.gson.JsonObject; | |
22 | +import com.google.gson.JsonPrimitive; | |
23 | +import lombok.SneakyThrows; | |
24 | +import org.apache.commons.collections.CollectionUtils; | |
25 | +import org.springframework.stereotype.Service; | |
26 | +import org.thingsboard.common.util.JacksonUtil; | |
27 | +import org.thingsboard.server.common.data.Device; | |
28 | +import org.thingsboard.server.common.data.DeviceProfile; | |
29 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | |
30 | +import org.thingsboard.server.common.data.DeviceProfileType; | |
31 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
32 | +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; | |
33 | +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials; | |
34 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | |
35 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | |
36 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
37 | +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; | |
38 | +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; | |
39 | +import org.thingsboard.server.common.data.id.TenantId; | |
40 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | |
41 | +import org.thingsboard.server.common.data.security.DeviceCredentialsType; | |
42 | +import org.thingsboard.server.common.transport.adaptor.JsonConverter; | |
43 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | |
44 | +import org.thingsboard.server.dao.device.DeviceProfileService; | |
45 | +import org.thingsboard.server.dao.device.DeviceService; | |
46 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
47 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
48 | +import org.thingsboard.server.service.action.EntityActionService; | |
49 | +import org.thingsboard.server.service.importing.AbstractBulkImportService; | |
50 | +import org.thingsboard.server.service.importing.BulkImportColumnType; | |
51 | +import org.thingsboard.server.service.importing.BulkImportRequest; | |
52 | +import org.thingsboard.server.service.importing.ImportedEntityInfo; | |
53 | +import org.thingsboard.server.service.queue.TbClusterService; | |
54 | +import org.thingsboard.server.service.security.AccessValidator; | |
55 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
56 | +import org.thingsboard.server.service.security.permission.AccessControlService; | |
57 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
58 | + | |
59 | +import java.util.EnumSet; | |
60 | +import java.util.Map; | |
61 | +import java.util.Optional; | |
62 | +import java.util.Set; | |
63 | +import java.util.stream.Collectors; | |
64 | +import java.util.stream.Stream; | |
65 | + | |
66 | +import static org.thingsboard.server.dao.service.Validator.validateId; | |
67 | + | |
68 | +@Service | |
69 | +@TbCoreComponent | |
70 | +public class DeviceBulkImportService extends AbstractBulkImportService<Device> { | |
71 | + protected final DeviceService deviceService; | |
72 | + protected final DeviceCredentialsService deviceCredentialsService; | |
73 | + protected final DeviceProfileService deviceProfileService; | |
74 | + | |
75 | + public DeviceBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, | |
76 | + AccessControlService accessControlService, AccessValidator accessValidator, | |
77 | + EntityActionService entityActionService, TbClusterService clusterService, | |
78 | + DeviceService deviceService, DeviceCredentialsService deviceCredentialsService, | |
79 | + DeviceProfileService deviceProfileService) { | |
80 | + super(tsSubscriptionService, tenantProfileCache, accessControlService, accessValidator, entityActionService, clusterService); | |
81 | + this.deviceService = deviceService; | |
82 | + this.deviceCredentialsService = deviceCredentialsService; | |
83 | + this.deviceProfileService = deviceProfileService; | |
84 | + } | |
85 | + | |
86 | + @Override | |
87 | + protected ImportedEntityInfo<Device> saveEntity(BulkImportRequest importRequest, Map<BulkImportRequest.ColumnMapping, String> entityData, SecurityUser user) { | |
88 | + ImportedEntityInfo<Device> importedEntityInfo = new ImportedEntityInfo<>(); | |
89 | + | |
90 | + Device device = new Device(); | |
91 | + device.setTenantId(user.getTenantId()); | |
92 | + setDeviceFields(device, entityData); | |
93 | + | |
94 | + Device existingDevice = deviceService.findDeviceByTenantIdAndName(user.getTenantId(), device.getName()); | |
95 | + if (existingDevice != null && importRequest.getMapping().getUpdate()) { | |
96 | + importedEntityInfo.setOldEntity(new Device(existingDevice)); | |
97 | + importedEntityInfo.setUpdated(true); | |
98 | + existingDevice.updateDevice(device); | |
99 | + device = existingDevice; | |
100 | + } | |
101 | + | |
102 | + DeviceCredentials deviceCredentials = createDeviceCredentials(entityData); | |
103 | + if (deviceCredentials.getCredentialsType() != null) { | |
104 | + if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) { | |
105 | + setUpLwM2mDeviceProfile(user.getTenantId(), device); | |
106 | + } | |
107 | + device = deviceService.saveDeviceWithCredentials(device, deviceCredentials); | |
108 | + } else { | |
109 | + device = deviceService.saveDevice(device); | |
110 | + } | |
111 | + | |
112 | + importedEntityInfo.setEntity(device); | |
113 | + | |
114 | + return importedEntityInfo; | |
115 | + } | |
116 | + | |
117 | + private void setDeviceFields(Device device, Map<BulkImportRequest.ColumnMapping, String> data) { | |
118 | + ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(device.getAdditionalInfo()).orElseGet(JacksonUtil::newObjectNode); | |
119 | + data.forEach((columnMapping, value) -> { | |
120 | + switch (columnMapping.getType()) { | |
121 | + case NAME: | |
122 | + device.setName(value); | |
123 | + break; | |
124 | + case TYPE: | |
125 | + device.setType(value); | |
126 | + break; | |
127 | + case LABEL: | |
128 | + device.setLabel(value); | |
129 | + break; | |
130 | + case DESCRIPTION: | |
131 | + additionalInfo.set("description", new TextNode(value)); | |
132 | + break; | |
133 | + case IS_GATEWAY: | |
134 | + additionalInfo.set("gateway", BooleanNode.valueOf(Boolean.parseBoolean(value))); | |
135 | + break; | |
136 | + } | |
137 | + device.setAdditionalInfo(additionalInfo); | |
138 | + }); | |
139 | + } | |
140 | + | |
141 | + @SneakyThrows | |
142 | + private DeviceCredentials createDeviceCredentials(Map<BulkImportRequest.ColumnMapping, String> data) { | |
143 | + Set<BulkImportColumnType> columns = data.keySet().stream().map(BulkImportRequest.ColumnMapping::getType).collect(Collectors.toSet()); | |
144 | + | |
145 | + DeviceCredentials credentials = new DeviceCredentials(); | |
146 | + | |
147 | + if (columns.contains(BulkImportColumnType.ACCESS_TOKEN)) { | |
148 | + credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); | |
149 | + credentials.setCredentialsId(getByColumnType(BulkImportColumnType.ACCESS_TOKEN, data)); | |
150 | + } else if (CollectionUtils.containsAny(columns, EnumSet.of(BulkImportColumnType.MQTT_CLIENT_ID, BulkImportColumnType.MQTT_USER_NAME, BulkImportColumnType.MQTT_PASSWORD))) { | |
151 | + credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC); | |
152 | + | |
153 | + BasicMqttCredentials basicMqttCredentials = new BasicMqttCredentials(); | |
154 | + basicMqttCredentials.setClientId(getByColumnType(BulkImportColumnType.MQTT_CLIENT_ID, data)); | |
155 | + basicMqttCredentials.setUserName(getByColumnType(BulkImportColumnType.MQTT_USER_NAME, data)); | |
156 | + basicMqttCredentials.setPassword(getByColumnType(BulkImportColumnType.MQTT_PASSWORD, data)); | |
157 | + credentials.setCredentialsValue(JacksonUtil.toString(basicMqttCredentials)); | |
158 | + } else if (columns.contains(BulkImportColumnType.X509)) { | |
159 | + credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); | |
160 | + credentials.setCredentialsValue(getByColumnType(BulkImportColumnType.X509, data)); | |
161 | + } else if (columns.contains(BulkImportColumnType.LWM2M_CLIENT_ENDPOINT)) { | |
162 | + credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS); | |
163 | + ObjectNode lwm2mCredentials = JacksonUtil.newObjectNode(); | |
164 | + | |
165 | + ObjectNode client = JacksonUtil.newObjectNode(); | |
166 | + Stream.of(BulkImportColumnType.LWM2M_CLIENT_ENDPOINT, BulkImportColumnType.LWM2M_CLIENT_SECURITY_CONFIG_MODE, | |
167 | + BulkImportColumnType.LWM2M_CLIENT_IDENTITY, BulkImportColumnType.LWM2M_CLIENT_KEY, BulkImportColumnType.LWM2M_CLIENT_CERT) | |
168 | + .forEach(lwm2mClientProperty -> { | |
169 | + String value = getByColumnType(lwm2mClientProperty, data); | |
170 | + if (value != null) { | |
171 | + client.set(lwm2mClientProperty.getKey(), new TextNode(value)); | |
172 | + } | |
173 | + }); | |
174 | + | |
175 | + LwM2MClientCredentials lwM2MClientCredentials = JacksonUtil.treeToValue(client, LwM2MClientCredentials.class); | |
176 | + // so that only fields needed for specific type of lwM2MClientCredentials were saved in json | |
177 | + lwm2mCredentials.set("client", JacksonUtil.valueToTree(lwM2MClientCredentials)); | |
178 | + | |
179 | + ObjectNode bootstrapServer = JacksonUtil.newObjectNode(); | |
180 | + Stream.of(BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE, BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID, | |
181 | + BulkImportColumnType.LWM2M_BOOTSTRAP_SERVER_SECRET_KEY) | |
182 | + .forEach(lwm2mBootstrapServerProperty -> { | |
183 | + String value = getByColumnType(lwm2mBootstrapServerProperty, data); | |
184 | + if (value != null) { | |
185 | + bootstrapServer.set(lwm2mBootstrapServerProperty.getKey(), new TextNode(value)); | |
186 | + } | |
187 | + }); | |
188 | + | |
189 | + ObjectNode lwm2mServer = JacksonUtil.newObjectNode(); | |
190 | + Stream.of(BulkImportColumnType.LWM2M_SERVER_SECURITY_MODE, BulkImportColumnType.LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID, | |
191 | + BulkImportColumnType.LWM2M_SERVER_CLIENT_SECRET_KEY) | |
192 | + .forEach(lwm2mServerProperty -> { | |
193 | + String value = getByColumnType(lwm2mServerProperty, data); | |
194 | + if (value != null) { | |
195 | + lwm2mServer.set(lwm2mServerProperty.getKey(), new TextNode(value)); | |
196 | + } | |
197 | + }); | |
198 | + | |
199 | + ObjectNode bootstrap = JacksonUtil.newObjectNode(); | |
200 | + bootstrap.set("bootstrapServer", bootstrapServer); | |
201 | + bootstrap.set("lwm2mServer", lwm2mServer); | |
202 | + lwm2mCredentials.set("bootstrap", bootstrap); | |
203 | + | |
204 | + credentials.setCredentialsValue(lwm2mCredentials.toString()); | |
205 | + } | |
206 | + | |
207 | + return credentials; | |
208 | + } | |
209 | + | |
210 | + private void setUpLwM2mDeviceProfile(TenantId tenantId, Device device) { | |
211 | + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileByName(tenantId, device.getType()); | |
212 | + if (deviceProfile != null) { | |
213 | + if (deviceProfile.getTransportType() != DeviceTransportType.LWM2M) { | |
214 | + deviceProfile.setTransportType(DeviceTransportType.LWM2M); | |
215 | + deviceProfile.getProfileData().setTransportConfiguration(new Lwm2mDeviceProfileTransportConfiguration()); | |
216 | + deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
217 | + device.setDeviceProfileId(deviceProfile.getId()); | |
218 | + } | |
219 | + } else { | |
220 | + deviceProfile = new DeviceProfile(); | |
221 | + deviceProfile.setTenantId(tenantId); | |
222 | + deviceProfile.setType(DeviceProfileType.DEFAULT); | |
223 | + deviceProfile.setName(device.getType()); | |
224 | + deviceProfile.setTransportType(DeviceTransportType.LWM2M); | |
225 | + deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | |
226 | + | |
227 | + DeviceProfileData deviceProfileData = new DeviceProfileData(); | |
228 | + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | |
229 | + DeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); | |
230 | + DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); | |
231 | + | |
232 | + deviceProfileData.setConfiguration(configuration); | |
233 | + deviceProfileData.setTransportConfiguration(transportConfiguration); | |
234 | + deviceProfileData.setProvisionConfiguration(provisionConfiguration); | |
235 | + deviceProfile.setProfileData(deviceProfileData); | |
236 | + | |
237 | + deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
238 | + device.setDeviceProfileId(deviceProfile.getId()); | |
239 | + } | |
240 | + } | |
241 | + | |
242 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.edge; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
19 | +import com.fasterxml.jackson.databind.node.TextNode; | |
20 | +import org.springframework.stereotype.Service; | |
21 | +import org.thingsboard.common.util.JacksonUtil; | |
22 | +import org.thingsboard.server.common.data.edge.Edge; | |
23 | +import org.thingsboard.server.dao.edge.EdgeService; | |
24 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
25 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
26 | +import org.thingsboard.server.service.action.EntityActionService; | |
27 | +import org.thingsboard.server.service.importing.AbstractBulkImportService; | |
28 | +import org.thingsboard.server.service.importing.BulkImportRequest; | |
29 | +import org.thingsboard.server.service.importing.ImportedEntityInfo; | |
30 | +import org.thingsboard.server.service.queue.TbClusterService; | |
31 | +import org.thingsboard.server.service.security.AccessValidator; | |
32 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
33 | +import org.thingsboard.server.service.security.permission.AccessControlService; | |
34 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
35 | + | |
36 | +import java.util.Map; | |
37 | +import java.util.Optional; | |
38 | + | |
39 | +@Service | |
40 | +@TbCoreComponent | |
41 | +public class EdgeBulkImportService extends AbstractBulkImportService<Edge> { | |
42 | + private final EdgeService edgeService; | |
43 | + | |
44 | + public EdgeBulkImportService(TelemetrySubscriptionService tsSubscriptionService, TbTenantProfileCache tenantProfileCache, | |
45 | + AccessControlService accessControlService, AccessValidator accessValidator, | |
46 | + EntityActionService entityActionService, TbClusterService clusterService, EdgeService edgeService) { | |
47 | + super(tsSubscriptionService, tenantProfileCache, accessControlService, accessValidator, entityActionService, clusterService); | |
48 | + this.edgeService = edgeService; | |
49 | + } | |
50 | + | |
51 | + @Override | |
52 | + protected ImportedEntityInfo<Edge> saveEntity(BulkImportRequest importRequest, Map<BulkImportRequest.ColumnMapping, String> entityData, SecurityUser user) { | |
53 | + ImportedEntityInfo<Edge> importedEntityInfo = new ImportedEntityInfo<>(); | |
54 | + | |
55 | + Edge edge = new Edge(); | |
56 | + edge.setTenantId(user.getTenantId()); | |
57 | + setEdgeFields(edge, entityData); | |
58 | + | |
59 | + Edge existingEdge = edgeService.findEdgeByTenantIdAndName(user.getTenantId(), edge.getName()); | |
60 | + if (existingEdge != null && importRequest.getMapping().getUpdate()) { | |
61 | + importedEntityInfo.setOldEntity(new Edge(existingEdge)); | |
62 | + importedEntityInfo.setUpdated(true); | |
63 | + existingEdge.update(edge); | |
64 | + edge = existingEdge; | |
65 | + } | |
66 | + edge = edgeService.saveEdge(edge, true); | |
67 | + | |
68 | + importedEntityInfo.setEntity(edge); | |
69 | + return importedEntityInfo; | |
70 | + } | |
71 | + | |
72 | + private void setEdgeFields(Edge edge, Map<BulkImportRequest.ColumnMapping, String> data) { | |
73 | + ObjectNode additionalInfo = (ObjectNode) Optional.ofNullable(edge.getAdditionalInfo()).orElseGet(JacksonUtil::newObjectNode); | |
74 | + data.forEach((columnMapping, value) -> { | |
75 | + switch (columnMapping.getType()) { | |
76 | + case NAME: | |
77 | + edge.setName(value); | |
78 | + break; | |
79 | + case TYPE: | |
80 | + edge.setType(value); | |
81 | + break; | |
82 | + case LABEL: | |
83 | + edge.setLabel(value); | |
84 | + break; | |
85 | + case DESCRIPTION: | |
86 | + additionalInfo.set("description", new TextNode(value)); | |
87 | + break; | |
88 | + case EDGE_LICENSE_KEY: | |
89 | + edge.setEdgeLicenseKey(value); | |
90 | + break; | |
91 | + case CLOUD_ENDPOINT: | |
92 | + edge.setCloudEndpoint(value); | |
93 | + break; | |
94 | + case ROUTING_KEY: | |
95 | + edge.setRoutingKey(value); | |
96 | + break; | |
97 | + case SECRET: | |
98 | + edge.setSecret(value); | |
99 | + break; | |
100 | + } | |
101 | + }); | |
102 | + edge.setAdditionalInfo(additionalInfo); | |
103 | + } | |
104 | + | |
105 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.importing; | |
17 | + | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import com.google.gson.JsonObject; | |
20 | +import com.google.gson.JsonPrimitive; | |
21 | +import lombok.RequiredArgsConstructor; | |
22 | +import lombok.SneakyThrows; | |
23 | +import org.apache.commons.lang3.StringUtils; | |
24 | +import org.thingsboard.server.common.data.BaseData; | |
25 | +import org.thingsboard.server.common.data.TenantProfile; | |
26 | +import org.thingsboard.server.common.data.audit.ActionType; | |
27 | +import org.thingsboard.server.common.data.id.EntityId; | |
28 | +import org.thingsboard.server.common.data.id.UUIDBased; | |
29 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
30 | +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | |
31 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
32 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
33 | +import org.thingsboard.server.common.transport.adaptor.JsonConverter; | |
34 | +import org.thingsboard.server.controller.BaseController; | |
35 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
36 | +import org.thingsboard.server.service.action.EntityActionService; | |
37 | +import org.thingsboard.server.service.importing.BulkImportRequest.ColumnMapping; | |
38 | +import org.thingsboard.server.service.queue.TbClusterService; | |
39 | +import org.thingsboard.server.service.security.AccessValidator; | |
40 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
41 | +import org.thingsboard.server.service.security.permission.AccessControlService; | |
42 | +import org.thingsboard.server.service.security.permission.Operation; | |
43 | +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | |
44 | +import org.thingsboard.server.utils.CsvUtils; | |
45 | + | |
46 | +import javax.annotation.Nullable; | |
47 | +import java.util.ArrayList; | |
48 | +import java.util.List; | |
49 | +import java.util.Map; | |
50 | +import java.util.concurrent.TimeUnit; | |
51 | +import java.util.concurrent.atomic.AtomicInteger; | |
52 | +import java.util.function.Consumer; | |
53 | +import java.util.stream.Collectors; | |
54 | +import java.util.stream.Stream; | |
55 | + | |
56 | +@RequiredArgsConstructor | |
57 | +public abstract class AbstractBulkImportService<E extends BaseData<? extends EntityId>> { | |
58 | + protected final TelemetrySubscriptionService tsSubscriptionService; | |
59 | + protected final TbTenantProfileCache tenantProfileCache; | |
60 | + protected final AccessControlService accessControlService; | |
61 | + protected final AccessValidator accessValidator; | |
62 | + protected final EntityActionService entityActionService; | |
63 | + protected final TbClusterService clusterService; | |
64 | + | |
65 | + public final BulkImportResult<E> processBulkImport(BulkImportRequest request, SecurityUser user, Consumer<ImportedEntityInfo<E>> onEntityImported) throws Exception { | |
66 | + BulkImportResult<E> result = new BulkImportResult<>(); | |
67 | + | |
68 | + AtomicInteger i = new AtomicInteger(0); | |
69 | + if (request.getMapping().getHeader()) { | |
70 | + i.incrementAndGet(); | |
71 | + } | |
72 | + | |
73 | + parseData(request).forEach(entityData -> { | |
74 | + i.incrementAndGet(); | |
75 | + try { | |
76 | + ImportedEntityInfo<E> importedEntityInfo = saveEntity(request, entityData, user); | |
77 | + onEntityImported.accept(importedEntityInfo); | |
78 | + | |
79 | + E entity = importedEntityInfo.getEntity(); | |
80 | + | |
81 | + saveKvs(user, entity, entityData); | |
82 | + | |
83 | + if (importedEntityInfo.isUpdated()) { | |
84 | + result.setUpdated(result.getUpdated() + 1); | |
85 | + } else { | |
86 | + result.setCreated(result.getCreated() + 1); | |
87 | + } | |
88 | + } catch (Exception e) { | |
89 | + result.setErrors(result.getErrors() + 1); | |
90 | + result.getErrorsList().add(String.format("Line %d: %s", i.get(), e.getMessage())); | |
91 | + } | |
92 | + }); | |
93 | + | |
94 | + return result; | |
95 | + } | |
96 | + | |
97 | + protected abstract ImportedEntityInfo<E> saveEntity(BulkImportRequest importRequest, Map<ColumnMapping, String> entityData, SecurityUser user); | |
98 | + | |
99 | + /* | |
100 | + * Attributes' values are firstly added to JsonObject in order to then make some type cast, | |
101 | + * because we get all values as strings from CSV | |
102 | + * */ | |
103 | + private void saveKvs(SecurityUser user, E entity, Map<ColumnMapping, String> data) { | |
104 | + Stream.of(BulkImportColumnType.SHARED_ATTRIBUTE, BulkImportColumnType.SERVER_ATTRIBUTE, BulkImportColumnType.TIMESERIES) | |
105 | + .map(kvType -> { | |
106 | + JsonObject kvs = new JsonObject(); | |
107 | + data.entrySet().stream() | |
108 | + .filter(dataEntry -> dataEntry.getKey().getType() == kvType && | |
109 | + StringUtils.isNotEmpty(dataEntry.getKey().getKey())) | |
110 | + .forEach(dataEntry -> kvs.add(dataEntry.getKey().getKey(), new JsonPrimitive(dataEntry.getValue()))); | |
111 | + return Map.entry(kvType, kvs); | |
112 | + }) | |
113 | + .filter(kvsEntry -> kvsEntry.getValue().entrySet().size() > 0) | |
114 | + .forEach(kvsEntry -> { | |
115 | + BulkImportColumnType kvType = kvsEntry.getKey(); | |
116 | + if (kvType == BulkImportColumnType.SHARED_ATTRIBUTE || kvType == BulkImportColumnType.SERVER_ATTRIBUTE) { | |
117 | + saveAttributes(user, entity, kvsEntry, kvType); | |
118 | + } else { | |
119 | + saveTelemetry(user, entity, kvsEntry); | |
120 | + } | |
121 | + }); | |
122 | + } | |
123 | + | |
124 | + @SneakyThrows | |
125 | + private void saveTelemetry(SecurityUser user, E entity, Map.Entry<BulkImportColumnType, JsonObject> kvsEntry) { | |
126 | + List<TsKvEntry> timeseries = JsonConverter.convertToTelemetry(kvsEntry.getValue(), System.currentTimeMillis()).entrySet().stream() | |
127 | + .flatMap(entry -> entry.getValue().stream().map(kvEntry -> new BasicTsKvEntry(entry.getKey(), kvEntry))) | |
128 | + .collect(Collectors.toList()); | |
129 | + | |
130 | + accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entity.getId(), (result, tenantId, entityId) -> { | |
131 | + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); | |
132 | + long tenantTtl = TimeUnit.DAYS.toSeconds(((DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration()).getDefaultStorageTtlDays()); | |
133 | + tsSubscriptionService.saveAndNotify(tenantId, user.getCustomerId(), entityId, timeseries, tenantTtl, new FutureCallback<Void>() { | |
134 | + @Override | |
135 | + public void onSuccess(@Nullable Void tmp) { | |
136 | + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, | |
137 | + ActionType.TIMESERIES_UPDATED, null, timeseries); | |
138 | + } | |
139 | + | |
140 | + @Override | |
141 | + public void onFailure(Throwable t) { | |
142 | + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, | |
143 | + ActionType.TIMESERIES_UPDATED, BaseController.toException(t), timeseries); | |
144 | + throw new RuntimeException(t); | |
145 | + } | |
146 | + }); | |
147 | + }); | |
148 | + } | |
149 | + | |
150 | + @SneakyThrows | |
151 | + private void saveAttributes(SecurityUser user, E entity, Map.Entry<BulkImportColumnType, JsonObject> kvsEntry, BulkImportColumnType kvType) { | |
152 | + String scope = kvType.getKey(); | |
153 | + List<AttributeKvEntry> attributes = new ArrayList<>(JsonConverter.convertToAttributes(kvsEntry.getValue())); | |
154 | + | |
155 | + accessValidator.validateEntityAndCallback(user, Operation.WRITE_ATTRIBUTES, entity.getId(), (result, tenantId, entityId) -> { | |
156 | + tsSubscriptionService.saveAndNotify(tenantId, entityId, scope, attributes, new FutureCallback<>() { | |
157 | + | |
158 | + @Override | |
159 | + public void onSuccess(Void unused) { | |
160 | + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, | |
161 | + null, ActionType.ATTRIBUTES_UPDATED, null, scope, attributes); | |
162 | + } | |
163 | + | |
164 | + @Override | |
165 | + public void onFailure(Throwable throwable) { | |
166 | + entityActionService.logEntityAction(user, (UUIDBased & EntityId) entityId, null, | |
167 | + null, ActionType.ATTRIBUTES_UPDATED, BaseController.toException(throwable), | |
168 | + scope, attributes); | |
169 | + throw new RuntimeException(throwable); | |
170 | + } | |
171 | + | |
172 | + }); | |
173 | + }); | |
174 | + } | |
175 | + | |
176 | + protected final String getByColumnType(BulkImportColumnType bulkImportColumnType, Map<ColumnMapping, String> data) { | |
177 | + return data.entrySet().stream().filter(entry -> entry.getKey().getType() == bulkImportColumnType).findFirst().map(Map.Entry::getValue).orElse(null); | |
178 | + } | |
179 | + | |
180 | + private List<Map<ColumnMapping, String>> parseData(BulkImportRequest request) throws Exception { | |
181 | + List<List<String>> records = CsvUtils.parseCsv(request.getFile(), request.getMapping().getDelimiter()); | |
182 | + if (request.getMapping().getHeader()) { | |
183 | + records.remove(0); | |
184 | + } | |
185 | + | |
186 | + List<ColumnMapping> columnsMappings = request.getMapping().getColumns(); | |
187 | + | |
188 | + return records.stream() | |
189 | + .map(record -> Stream.iterate(0, i -> i < record.size(), i -> i + 1) | |
190 | + .map(i -> Map.entry(columnsMappings.get(i), record.get(i))) | |
191 | + .filter(entry -> StringUtils.isNotEmpty(entry.getValue())) | |
192 | + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))) | |
193 | + .collect(Collectors.toList()); | |
194 | + } | |
195 | + | |
196 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/importing/BulkImportColumnType.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.importing; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | +import org.thingsboard.server.common.data.DataConstants; | |
20 | + | |
21 | +public enum BulkImportColumnType { | |
22 | + NAME, | |
23 | + TYPE, | |
24 | + LABEL, | |
25 | + SHARED_ATTRIBUTE(DataConstants.SHARED_SCOPE), | |
26 | + SERVER_ATTRIBUTE(DataConstants.SERVER_SCOPE), | |
27 | + TIMESERIES, | |
28 | + ACCESS_TOKEN, | |
29 | + X509, | |
30 | + MQTT_CLIENT_ID, | |
31 | + MQTT_USER_NAME, | |
32 | + MQTT_PASSWORD, | |
33 | + LWM2M_CLIENT_ENDPOINT("endpoint"), | |
34 | + LWM2M_CLIENT_SECURITY_CONFIG_MODE("securityConfigClientMode"), | |
35 | + LWM2M_CLIENT_IDENTITY("identity"), | |
36 | + LWM2M_CLIENT_KEY("key"), | |
37 | + LWM2M_CLIENT_CERT("cert"), | |
38 | + LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE("securityMode"), | |
39 | + LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID("clientPublicKeyOrId"), | |
40 | + LWM2M_BOOTSTRAP_SERVER_SECRET_KEY("clientSecretKey"), | |
41 | + LWM2M_SERVER_SECURITY_MODE("securityMode"), | |
42 | + LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID("clientPublicKeyOrId"), | |
43 | + LWM2M_SERVER_CLIENT_SECRET_KEY("clientSecretKey"), | |
44 | + IS_GATEWAY, | |
45 | + DESCRIPTION, | |
46 | + EDGE_LICENSE_KEY, | |
47 | + CLOUD_ENDPOINT, | |
48 | + ROUTING_KEY, | |
49 | + SECRET; | |
50 | + | |
51 | + @Getter | |
52 | + private String key; | |
53 | + | |
54 | + BulkImportColumnType() { | |
55 | + } | |
56 | + | |
57 | + BulkImportColumnType(String key) { | |
58 | + this.key = key; | |
59 | + } | |
60 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/importing/BulkImportRequest.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.importing; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +import java.util.List; | |
21 | + | |
22 | +@Data | |
23 | +public class BulkImportRequest { | |
24 | + private String file; | |
25 | + private Mapping mapping; | |
26 | + | |
27 | + @Data | |
28 | + public static class Mapping { | |
29 | + private List<ColumnMapping> columns; | |
30 | + private Character delimiter; | |
31 | + private Boolean update; | |
32 | + private Boolean header; | |
33 | + } | |
34 | + | |
35 | + @Data | |
36 | + public static class ColumnMapping { | |
37 | + private BulkImportColumnType type; | |
38 | + private String key; | |
39 | + } | |
40 | + | |
41 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.importing; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +import java.util.LinkedList; | |
21 | +import java.util.List; | |
22 | + | |
23 | +@Data | |
24 | +public class BulkImportResult<E> { | |
25 | + private int created = 0; | |
26 | + private int updated = 0; | |
27 | + private int errors = 0; | |
28 | + private List<String> errorsList = new LinkedList<>(); | |
29 | + | |
30 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/importing/ImportedEntityInfo.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.importing; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class ImportedEntityInfo<E> { | |
22 | + private E entity; | |
23 | + private boolean isUpdated; | |
24 | + private E oldEntity; | |
25 | +} | ... | ... |
... | ... | @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.id.AlarmId; |
26 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
27 | 27 | import org.thingsboard.server.common.data.page.PageData; |
28 | 28 | import org.thingsboard.server.common.data.page.PageLink; |
29 | -import org.thingsboard.server.common.data.page.SortOrder; | |
30 | 29 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
31 | 30 | import org.thingsboard.server.common.msg.queue.ServiceType; |
32 | 31 | import org.thingsboard.server.dao.alarm.AlarmDao; |
... | ... | @@ -34,17 +33,12 @@ import org.thingsboard.server.dao.alarm.AlarmService; |
34 | 33 | import org.thingsboard.server.dao.relation.RelationService; |
35 | 34 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
36 | 35 | import org.thingsboard.server.dao.tenant.TenantDao; |
37 | -import org.thingsboard.server.dao.util.PsqlDao; | |
38 | 36 | import org.thingsboard.server.queue.discovery.PartitionService; |
39 | 37 | import org.thingsboard.server.queue.util.TbCoreComponent; |
40 | -import org.thingsboard.server.service.action.RuleEngineEntityActionService; | |
41 | -import org.thingsboard.server.service.ttl.AbstractCleanUpService; | |
38 | +import org.thingsboard.server.service.action.EntityActionService; | |
42 | 39 | |
43 | -import java.sql.Connection; | |
44 | -import java.sql.SQLException; | |
45 | 40 | import java.util.Date; |
46 | 41 | import java.util.Optional; |
47 | -import java.util.UUID; | |
48 | 42 | import java.util.concurrent.TimeUnit; |
49 | 43 | |
50 | 44 | @TbCoreComponent |
... | ... | @@ -60,7 +54,7 @@ public class AlarmsCleanUpService { |
60 | 54 | private final AlarmDao alarmDao; |
61 | 55 | private final AlarmService alarmService; |
62 | 56 | private final RelationService relationService; |
63 | - private final RuleEngineEntityActionService ruleEngineEntityActionService; | |
57 | + private final EntityActionService entityActionService; | |
64 | 58 | private final PartitionService partitionService; |
65 | 59 | private final TbTenantProfileCache tenantProfileCache; |
66 | 60 | |
... | ... | @@ -90,7 +84,7 @@ public class AlarmsCleanUpService { |
90 | 84 | toRemove.getData().forEach(alarmId -> { |
91 | 85 | relationService.deleteEntityRelations(tenantId, alarmId); |
92 | 86 | Alarm alarm = alarmService.deleteAlarm(tenantId, alarmId).getAlarm(); |
93 | - ruleEngineEntityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); | |
87 | + entityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); | |
94 | 88 | }); |
95 | 89 | |
96 | 90 | totalRemoved += toRemove.getTotalElements(); | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 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.utils; | |
17 | + | |
18 | +import lombok.AccessLevel; | |
19 | +import lombok.NoArgsConstructor; | |
20 | +import org.apache.commons.csv.CSVFormat; | |
21 | +import org.apache.commons.csv.CSVRecord; | |
22 | +import org.apache.commons.io.input.CharSequenceReader; | |
23 | + | |
24 | +import java.util.List; | |
25 | +import java.util.stream.Collectors; | |
26 | +import java.util.stream.Stream; | |
27 | + | |
28 | +@NoArgsConstructor(access = AccessLevel.PRIVATE) | |
29 | +public class CsvUtils { | |
30 | + | |
31 | + public static List<List<String>> parseCsv(String content, Character delimiter) throws Exception { | |
32 | + CSVFormat csvFormat = delimiter.equals(',') ? CSVFormat.DEFAULT : CSVFormat.DEFAULT.withDelimiter(delimiter); | |
33 | + | |
34 | + List<CSVRecord> records; | |
35 | + try (CharSequenceReader reader = new CharSequenceReader(content)) { | |
36 | + records = csvFormat.parse(reader).getRecords(); | |
37 | + } | |
38 | + | |
39 | + return records.stream() | |
40 | + .map(record -> Stream.iterate(0, i -> i < record.size(), i -> i + 1) | |
41 | + .map(record::get) | |
42 | + .collect(Collectors.toList())) | |
43 | + .collect(Collectors.toList()); | |
44 | + } | |
45 | + | |
46 | +} | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.validation.NoXss; |
29 | 29 | |
30 | 30 | import java.io.ByteArrayInputStream; |
31 | 31 | import java.io.IOException; |
32 | +import java.util.Optional; | |
32 | 33 | |
33 | 34 | @EqualsAndHashCode(callSuper = true) |
34 | 35 | @Slf4j |
... | ... | @@ -83,6 +84,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
83 | 84 | this.setDeviceData(device.getDeviceData()); |
84 | 85 | this.setFirmwareId(device.getFirmwareId()); |
85 | 86 | this.setSoftwareId(device.getSoftwareId()); |
87 | + Optional.ofNullable(device.getAdditionalInfo()).ifPresent(this::setAdditionalInfo); | |
86 | 88 | return this; |
87 | 89 | } |
88 | 90 | ... | ... |
... | ... | @@ -25,6 +25,8 @@ import org.thingsboard.server.common.data.id.CustomerId; |
25 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
26 | 26 | import org.thingsboard.server.common.data.validation.NoXss; |
27 | 27 | |
28 | +import java.util.Optional; | |
29 | + | |
28 | 30 | @EqualsAndHashCode(callSuper = true) |
29 | 31 | public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements HasName, HasTenantId, HasCustomerId { |
30 | 32 | |
... | ... | @@ -56,6 +58,15 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
56 | 58 | this.label = asset.getLabel(); |
57 | 59 | } |
58 | 60 | |
61 | + public void update(Asset asset) { | |
62 | + this.tenantId = asset.getTenantId(); | |
63 | + this.customerId = asset.getCustomerId(); | |
64 | + this.name = asset.getName(); | |
65 | + this.type = asset.getType(); | |
66 | + this.label = asset.getLabel(); | |
67 | + Optional.ofNullable(asset.getAdditionalInfo()).ifPresent(this::setAdditionalInfo); | |
68 | + } | |
69 | + | |
59 | 70 | public TenantId getTenantId() { |
60 | 71 | return tenantId; |
61 | 72 | } | ... | ... |
... | ... | @@ -26,7 +26,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; |
26 | 26 | @JsonSubTypes.Type(value = NoSecClientCredentials.class, name = "NO_SEC"), |
27 | 27 | @JsonSubTypes.Type(value = PSKClientCredentials.class, name = "PSK"), |
28 | 28 | @JsonSubTypes.Type(value = RPKClientCredentials.class, name = "RPK"), |
29 | - @JsonSubTypes.Type(value = X509ClientCredentials.class, name = "X509")}) | |
29 | + @JsonSubTypes.Type(value = X509ClientCredentials.class, name = "X509") | |
30 | +}) | |
30 | 31 | public interface LwM2MClientCredentials { |
31 | 32 | |
32 | 33 | @JsonIgnore | ... | ... |
... | ... | @@ -15,7 +15,6 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data.edge; |
17 | 17 | |
18 | -import com.fasterxml.jackson.databind.JsonNode; | |
19 | 18 | import lombok.EqualsAndHashCode; |
20 | 19 | import lombok.Getter; |
21 | 20 | import lombok.Setter; |
... | ... | @@ -70,6 +69,19 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H |
70 | 69 | this.cloudEndpoint = edge.getCloudEndpoint(); |
71 | 70 | } |
72 | 71 | |
72 | + public void update(Edge edge) { | |
73 | + this.tenantId = edge.getTenantId(); | |
74 | + this.customerId = edge.getCustomerId(); | |
75 | + this.rootRuleChainId = edge.getRootRuleChainId(); | |
76 | + this.type = edge.getType(); | |
77 | + this.label = edge.getLabel(); | |
78 | + this.name = edge.getName(); | |
79 | + this.routingKey = edge.getRoutingKey(); | |
80 | + this.secret = edge.getSecret(); | |
81 | + this.edgeLicenseKey = edge.getEdgeLicenseKey(); | |
82 | + this.cloudEndpoint = edge.getCloudEndpoint(); | |
83 | + } | |
84 | + | |
73 | 85 | @Override |
74 | 86 | public String getSearchText() { |
75 | 87 | return getName(); | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.transport.adaptor; |
17 | 17 | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
18 | 19 | import com.google.gson.Gson; |
19 | 20 | import com.google.gson.JsonArray; |
20 | 21 | import com.google.gson.JsonElement; |
... | ... | @@ -577,6 +578,14 @@ public class JsonConverter { |
577 | 578 | return GSON.toJson(element); |
578 | 579 | } |
579 | 580 | |
581 | + public static JsonObject toJsonObject(Object o) { | |
582 | + return (JsonObject) GSON.toJsonTree(o); | |
583 | + } | |
584 | + | |
585 | + public static <T> T fromJson(JsonElement element, Class<T> type) { | |
586 | + return GSON.fromJson(element, type); | |
587 | + } | |
588 | + | |
580 | 589 | public static void setTypeCastEnabled(boolean enabled) { |
581 | 590 | isTypeCastEnabled = enabled; |
582 | 591 | } | ... | ... |
... | ... | @@ -118,4 +118,9 @@ public class JacksonUtil { |
118 | 118 | public static <T> JsonNode valueToTree(T value) { |
119 | 119 | return OBJECT_MAPPER.valueToTree(value); |
120 | 120 | } |
121 | + | |
122 | + public static <T> T treeToValue(JsonNode tree, Class<T> type) throws JsonProcessingException { | |
123 | + return OBJECT_MAPPER.treeToValue(tree, type); | |
124 | + } | |
125 | + | |
121 | 126 | } | ... | ... |
... | ... | @@ -220,6 +220,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
220 | 220 | if (foundDeviceCredentials == null) { |
221 | 221 | deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); |
222 | 222 | } else { |
223 | + deviceCredentials.setId(foundDeviceCredentials.getId()); | |
223 | 224 | deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials); |
224 | 225 | } |
225 | 226 | } | ... | ... |