...
|
...
|
@@ -15,12 +15,16 @@ |
15
|
15
|
*/
|
16
|
16
|
package org.thingsboard.server.dao.rule;
|
17
|
17
|
|
|
18
|
+import com.datastax.oss.driver.api.core.uuid.Uuids;
|
|
19
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
20
|
+import com.fasterxml.jackson.databind.node.ObjectNode;
|
18
|
21
|
import com.google.common.util.concurrent.ListenableFuture;
|
19
|
22
|
import lombok.extern.slf4j.Slf4j;
|
20
|
23
|
import org.apache.commons.lang3.StringUtils;
|
21
|
24
|
import org.hibernate.exception.ConstraintViolationException;
|
22
|
25
|
import org.springframework.beans.factory.annotation.Autowired;
|
23
|
26
|
import org.springframework.stereotype.Service;
|
|
27
|
+import org.springframework.util.CollectionUtils;
|
24
|
28
|
import org.thingsboard.server.common.data.BaseData;
|
25
|
29
|
import org.thingsboard.server.common.data.EntityType;
|
26
|
30
|
import org.thingsboard.server.common.data.Tenant;
|
...
|
...
|
@@ -30,11 +34,14 @@ import org.thingsboard.server.common.data.id.RuleNodeId; |
30
|
34
|
import org.thingsboard.server.common.data.id.TenantId;
|
31
|
35
|
import org.thingsboard.server.common.data.page.PageData;
|
32
|
36
|
import org.thingsboard.server.common.data.page.PageLink;
|
|
37
|
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
|
33
|
38
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
34
|
39
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
35
|
40
|
import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
|
36
|
41
|
import org.thingsboard.server.common.data.rule.RuleChain;
|
37
|
42
|
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
|
|
43
|
+import org.thingsboard.server.common.data.rule.RuleChainData;
|
|
44
|
+import org.thingsboard.server.common.data.rule.RuleChainImportResult;
|
38
|
45
|
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
|
39
|
46
|
import org.thingsboard.server.common.data.rule.RuleNode;
|
40
|
47
|
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
...
|
...
|
@@ -46,9 +53,14 @@ import org.thingsboard.server.dao.tenant.TenantDao; |
46
|
53
|
|
47
|
54
|
import java.util.ArrayList;
|
48
|
55
|
import java.util.HashMap;
|
|
56
|
+import java.util.Iterator;
|
49
|
57
|
import java.util.List;
|
50
|
58
|
import java.util.Map;
|
|
59
|
+import java.util.Optional;
|
51
|
60
|
import java.util.concurrent.ExecutionException;
|
|
61
|
+import java.util.stream.Collectors;
|
|
62
|
+
|
|
63
|
+import static org.thingsboard.server.common.data.DataConstants.TENANT;
|
52
|
64
|
|
53
|
65
|
/**
|
54
|
66
|
* Created by igor on 3/12/18.
|
...
|
...
|
@@ -57,6 +69,7 @@ import java.util.concurrent.ExecutionException; |
57
|
69
|
@Slf4j
|
58
|
70
|
public class BaseRuleChainService extends AbstractEntityService implements RuleChainService {
|
59
|
71
|
|
|
72
|
+ private static final int DEFAULT_PAGE_SIZE = 1000;
|
60
|
73
|
@Autowired
|
61
|
74
|
private RuleChainDao ruleChainDao;
|
62
|
75
|
|
...
|
...
|
@@ -358,6 +371,141 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC |
358
|
371
|
tenantRuleChainsRemover.removeEntities(tenantId, tenantId);
|
359
|
372
|
}
|
360
|
373
|
|
|
374
|
+ @Override
|
|
375
|
+ public RuleChainData exportTenantRuleChains(TenantId tenantId, PageLink pageLink) {
|
|
376
|
+ Validator.validateId(tenantId, "Incorrect tenant id for search rule chain request.");
|
|
377
|
+ Validator.validatePageLink(pageLink);
|
|
378
|
+ PageData<RuleChain> ruleChainData = ruleChainDao.findRuleChainsByTenantId(tenantId.getId(), pageLink);
|
|
379
|
+ List<RuleChain> ruleChains = ruleChainData.getData();
|
|
380
|
+ List<RuleChainMetaData> metadata = ruleChains.stream().map(rc -> loadRuleChainMetaData(tenantId, rc.getId())).collect(Collectors.toList());
|
|
381
|
+ RuleChainData rcData = new RuleChainData();
|
|
382
|
+ rcData.setRuleChains(ruleChains);
|
|
383
|
+ rcData.setMetadata(metadata);
|
|
384
|
+ setRandomRuleChainIds(rcData);
|
|
385
|
+ resetRuleNodeIds(metadata);
|
|
386
|
+ return rcData;
|
|
387
|
+ }
|
|
388
|
+
|
|
389
|
+ @Override
|
|
390
|
+ public List<RuleChainImportResult> importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite) {
|
|
391
|
+ List<RuleChainImportResult> importResults = new ArrayList<>();
|
|
392
|
+ setRandomRuleChainIds(ruleChainData);
|
|
393
|
+ resetRuleNodeIds(ruleChainData.getMetadata());
|
|
394
|
+ resetRuleChainMetadataTenantIds(tenantId, ruleChainData.getMetadata());
|
|
395
|
+ if (overwrite) {
|
|
396
|
+ List<RuleChain> persistentRuleChains = findAllTenantRuleChains(tenantId);
|
|
397
|
+ for (RuleChain ruleChain : ruleChainData.getRuleChains()) {
|
|
398
|
+ ComponentLifecycleEvent lifecycleEvent;
|
|
399
|
+ Optional<RuleChain> persistentRuleChainOpt = persistentRuleChains.stream().filter(rc -> rc.getName().equals(ruleChain.getName())).findFirst();
|
|
400
|
+ if (persistentRuleChainOpt.isPresent()) {
|
|
401
|
+ setNewRuleChainId(ruleChain, ruleChainData.getMetadata(), ruleChain.getId(), persistentRuleChainOpt.get().getId());
|
|
402
|
+ ruleChain.setRoot(persistentRuleChainOpt.get().isRoot());
|
|
403
|
+ lifecycleEvent = ComponentLifecycleEvent.UPDATED;
|
|
404
|
+ } else {
|
|
405
|
+ ruleChain.setRoot(false);
|
|
406
|
+ lifecycleEvent = ComponentLifecycleEvent.CREATED;
|
|
407
|
+ }
|
|
408
|
+ ruleChain.setTenantId(tenantId);
|
|
409
|
+ ruleChainDao.save(tenantId, ruleChain);
|
|
410
|
+ importResults.add(new RuleChainImportResult(tenantId, ruleChain.getId(), lifecycleEvent));
|
|
411
|
+ }
|
|
412
|
+ } else {
|
|
413
|
+ if (!CollectionUtils.isEmpty(ruleChainData.getRuleChains())) {
|
|
414
|
+ ruleChainData.getRuleChains().forEach(rc -> {
|
|
415
|
+ rc.setTenantId(tenantId);
|
|
416
|
+ rc.setRoot(false);
|
|
417
|
+ RuleChain savedRc = ruleChainDao.save(tenantId, rc);
|
|
418
|
+ importResults.add(new RuleChainImportResult(tenantId, savedRc.getId(), ComponentLifecycleEvent.CREATED));
|
|
419
|
+ });
|
|
420
|
+ }
|
|
421
|
+ }
|
|
422
|
+ if (!CollectionUtils.isEmpty(ruleChainData.getMetadata())) {
|
|
423
|
+ ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md));
|
|
424
|
+ }
|
|
425
|
+ return importResults;
|
|
426
|
+ }
|
|
427
|
+
|
|
428
|
+ private void resetRuleChainMetadataTenantIds(TenantId tenantId, List<RuleChainMetaData> metaData) {
|
|
429
|
+ for (RuleChainMetaData md : metaData) {
|
|
430
|
+ for (RuleNode node : md.getNodes()) {
|
|
431
|
+ JsonNode nodeConfiguration = node.getConfiguration();
|
|
432
|
+ searchTenantIdRecursive(tenantId, nodeConfiguration);
|
|
433
|
+ }
|
|
434
|
+ }
|
|
435
|
+ }
|
|
436
|
+
|
|
437
|
+ private void searchTenantIdRecursive(TenantId tenantId, JsonNode node) {
|
|
438
|
+ Iterator<String> iter = node.fieldNames();
|
|
439
|
+ boolean isTenantId = false;
|
|
440
|
+ while (iter.hasNext()) {
|
|
441
|
+ String field = iter.next();
|
|
442
|
+ if ("entityType".equals(field) && TENANT.equals(node.get(field).asText())) {
|
|
443
|
+ isTenantId = true;
|
|
444
|
+ break;
|
|
445
|
+ }
|
|
446
|
+ }
|
|
447
|
+ if (isTenantId) {
|
|
448
|
+ ObjectNode objNode = (ObjectNode) node;
|
|
449
|
+ objNode.put("id", tenantId.getId().toString());
|
|
450
|
+ } else {
|
|
451
|
+ Iterator<JsonNode> childIter = node.iterator();
|
|
452
|
+ while (childIter.hasNext()) {
|
|
453
|
+ searchTenantIdRecursive(tenantId, childIter.next());
|
|
454
|
+ }
|
|
455
|
+ }
|
|
456
|
+ }
|
|
457
|
+
|
|
458
|
+ private void setRandomRuleChainIds(RuleChainData ruleChainData) {
|
|
459
|
+ for (RuleChain ruleChain : ruleChainData.getRuleChains()) {
|
|
460
|
+ RuleChainId oldRuleChainId = ruleChain.getId();
|
|
461
|
+ RuleChainId newRuleChainId = new RuleChainId(Uuids.timeBased());
|
|
462
|
+ setNewRuleChainId(ruleChain, ruleChainData.getMetadata(), oldRuleChainId, newRuleChainId);
|
|
463
|
+ ruleChain.setTenantId(null);
|
|
464
|
+ }
|
|
465
|
+ }
|
|
466
|
+
|
|
467
|
+ private void resetRuleNodeIds(List<RuleChainMetaData> metaData) {
|
|
468
|
+ for (RuleChainMetaData md : metaData) {
|
|
469
|
+ for (RuleNode node : md.getNodes()) {
|
|
470
|
+ node.setId(null);
|
|
471
|
+ node.setRuleChainId(null);
|
|
472
|
+ }
|
|
473
|
+ }
|
|
474
|
+ }
|
|
475
|
+
|
|
476
|
+ private List<RuleChain> findAllTenantRuleChains(TenantId tenantId) {
|
|
477
|
+ PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
|
|
478
|
+ return findAllTenantRuleChainsRecursive(tenantId, new ArrayList<>(), pageLink);
|
|
479
|
+ }
|
|
480
|
+
|
|
481
|
+ private List<RuleChain> findAllTenantRuleChainsRecursive(TenantId tenantId, List<RuleChain> accumulator, PageLink pageLink) {
|
|
482
|
+ PageData<RuleChain> persistentRuleChainData = findTenantRuleChains(tenantId, pageLink);
|
|
483
|
+ List<RuleChain> ruleChains = persistentRuleChainData.getData();
|
|
484
|
+ if (!CollectionUtils.isEmpty(ruleChains)) {
|
|
485
|
+ accumulator.addAll(ruleChains);
|
|
486
|
+ }
|
|
487
|
+ if (persistentRuleChainData.hasNext()) {
|
|
488
|
+ return findAllTenantRuleChainsRecursive(tenantId, accumulator, pageLink.nextPageLink());
|
|
489
|
+ }
|
|
490
|
+ return accumulator;
|
|
491
|
+ }
|
|
492
|
+
|
|
493
|
+ private void setNewRuleChainId(RuleChain ruleChain, List<RuleChainMetaData> metadata, RuleChainId oldRuleChainId, RuleChainId newRuleChainId) {
|
|
494
|
+ ruleChain.setId(newRuleChainId);
|
|
495
|
+ for (RuleChainMetaData metaData : metadata) {
|
|
496
|
+ if (metaData.getRuleChainId().equals(oldRuleChainId)) {
|
|
497
|
+ metaData.setRuleChainId(newRuleChainId);
|
|
498
|
+ }
|
|
499
|
+ if (!CollectionUtils.isEmpty(metaData.getRuleChainConnections())) {
|
|
500
|
+ for (RuleChainConnectionInfo rcConnInfo : metaData.getRuleChainConnections()) {
|
|
501
|
+ if (rcConnInfo.getTargetRuleChainId().equals(oldRuleChainId)) {
|
|
502
|
+ rcConnInfo.setTargetRuleChainId(newRuleChainId);
|
|
503
|
+ }
|
|
504
|
+ }
|
|
505
|
+ }
|
|
506
|
+ }
|
|
507
|
+ }
|
|
508
|
+
|
361
|
509
|
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
|
362
|
510
|
try{
|
363
|
511
|
ruleChainDao.removeById(tenantId, ruleChainId.getId());
|
...
|
...
|
|