Added default edge root rule chain

This commit is contained in:
Volodymyr Babak 2020-03-09 13:57:38 +02:00
parent b97e82df7d
commit 6751c511f1
13 changed files with 284 additions and 36 deletions

View File

@ -0,0 +1,145 @@
{
"ruleChain": {
"additionalInfo": null,
"name": "Edge Root Rule Chain",
"type": "EDGE",
"firstRuleNodeId": null,
"root": true,
"debugMode": false,
"configuration": null,
"assignedEdges": [],
"assignedEdgesIds": []
},
"metadata": {
"firstNodeIndex": 2,
"nodes": [
{
"additionalInfo": {
"layoutX": 823,
"layoutY": 157
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"debugMode": false,
"configuration": {
"defaultTTL": 0
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 52
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
"name": "Save Client Attributes",
"debugMode": false,
"configuration": {
"scope": "CLIENT_SCOPE"
}
},
{
"additionalInfo": {
"layoutX": 347,
"layoutY": 149
},
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
"name": "Message Type Switch",
"debugMode": false,
"configuration": {
"version": 0
}
},
{
"additionalInfo": {
"layoutX": 825,
"layoutY": 266
},
"type": "org.thingsboard.rule.engine.action.TbLogNode",
"name": "Log RPC from Device",
"debugMode": false,
"configuration": {
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 378
},
"type": "org.thingsboard.rule.engine.action.TbLogNode",
"name": "Log Other",
"debugMode": false,
"configuration": {
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 466
},
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
"name": "RPC Call Request",
"debugMode": false,
"configuration": {
"timeoutInSeconds": 60
}
},
{
"additionalInfo": {
"layoutX": 1134,
"layoutY": 132
},
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
"name": "Push to cloud",
"debugMode": false,
"configuration": {
"version": 0
}
}
],
"connections": [
{
"fromIndex": 0,
"toIndex": 6,
"type": "Success"
},
{
"fromIndex": 1,
"toIndex": 6,
"type": "Success"
},
{
"fromIndex": 2,
"toIndex": 4,
"type": "Other"
},
{
"fromIndex": 2,
"toIndex": 1,
"type": "Post attributes"
},
{
"fromIndex": 2,
"toIndex": 0,
"type": "Post telemetry"
},
{
"fromIndex": 2,
"toIndex": 3,
"type": "RPC Request from Device"
},
{
"fromIndex": 2,
"toIndex": 5,
"type": "RPC Request to Device"
},
{
"fromIndex": 3,
"toIndex": 6,
"type": "Success"
}
],
"ruleChainConnections": null
}
}

View File

@ -2,9 +2,9 @@
"ruleChain": {
"additionalInfo": null,
"name": "Root Rule Chain",
"type": "SYSTEM",
"firstRuleNodeId": null,
"root": true,
"type": "SYSTEM",
"debugMode": false,
"configuration": null
},

View File

@ -90,9 +90,9 @@ public class EdgeController extends BaseController {
Edge result = checkNotNull(edgeService.saveEdge(edge));
if (created) {
RuleChain rootTenantRuleChain = ruleChainService.getRootTenantRuleChain(tenantId);
ruleChainService.assignRuleChainToEdge(tenantId, rootTenantRuleChain.getId(), result.getId());
edgeService.setRootRuleChain(tenantId, result, rootTenantRuleChain.getId());
RuleChain defaultRootEdgeRuleChain = ruleChainService.getDefaultRootEdgeRuleChain(tenantId);
ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), result.getId());
edgeService.setRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId());
}
logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null);

View File

@ -430,7 +430,7 @@ public class RuleChainController extends BaseController {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE);
RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
logEntityAction(ruleChainId, ruleChain,
null,
@ -494,7 +494,7 @@ public class RuleChainController extends BaseController {
}
for (EdgeId edgeId : removedEdgeIds) {
ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId);
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
@ -581,7 +581,7 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : edgeIds) {
ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId);
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
@ -620,4 +620,23 @@ public class RuleChainController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/defaultRootEdge", method = RequestMethod.POST)
@ResponseBody
public RuleChain setDefaultRootEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
ruleChainService.setDefaultRootEdgeRuleChain(getTenantId(), ruleChainId);
return ruleChain;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN),
null,
null,
ActionType.UPDATED, e, strRuleChainId);
throw handleException(e);
}
}
}

View File

@ -115,7 +115,6 @@ public class InstallScripts {
RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
ruleChain.setTenantId(tenantId);
ruleChain.setType(RuleChainType.SYSTEM);
ruleChain = ruleChainService.saveRuleChain(ruleChain);
ruleChainMetaData.setRuleChainId(ruleChain.getId());

View File

@ -71,11 +71,15 @@ public interface RuleChainService {
RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId);
RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId);
RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId, boolean remove);
void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
ListenableFuture<TimePageData<RuleChain>> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
RuleChain getDefaultRootEdgeRuleChain(TenantId tenantId);
boolean setDefaultRootEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId);
}

View File

@ -215,6 +215,7 @@ public class BaseEdgeService extends AbstractEntityService implements EdgeServic
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
dashboardService.unassignEdgeDashboards(tenantId, edgeId);
// TODO: validate that rule chains are removed by deleteEntityRelations(tenantId, edgeId); call
ruleChainService.unassignEdgeRuleChains(tenantId, edgeId);
List<Object> list = new ArrayList<>();

View File

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.dao.rule;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -46,7 +45,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
@ -61,9 +59,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.service.Validator.validateString;
/**
* Created by igor on 3/12/18.
@ -72,8 +67,6 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
@Slf4j
public class BaseRuleChainService extends AbstractEntityService implements RuleChainService {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private RuleChainDao ruleChainDao;
@ -86,9 +79,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Autowired
private EdgeDao edgeDao;
@Autowired
private EdgeService edgeService;
@Override
public RuleChain saveRuleChain(RuleChain ruleChain) {
ruleChainValidator.validate(ruleChain, RuleChain::getTenantId);
@ -120,7 +110,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true);
ruleChain.setType(RuleChainType.SYSTEM);
ruleChainDao.save(tenantId, ruleChain);
return true;
} catch (ExecutionException | InterruptedException e) {
@ -284,17 +273,27 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
public RuleChain getRootTenantRuleChain(TenantId tenantId) {
return getRootRuleChainByType(tenantId, RuleChainType.SYSTEM);
}
private RuleChain getRootRuleChainByType(TenantId tenantId, RuleChainType type) {
Validator.validateId(tenantId, "Incorrect tenant id for search request.");
List<EntityRelation> relations = relationService.findByFrom(tenantId, tenantId, RelationTypeGroup.RULE_CHAIN);
if (relations != null && !relations.isEmpty()) {
EntityRelation relation = relations.get(0);
for (EntityRelation relation : relations) {
RuleChainId ruleChainId = new RuleChainId(relation.getTo().getId());
return findRuleChainById(tenantId, ruleChainId);
RuleChain ruleChainById = findRuleChainById(tenantId, ruleChainId);
if (type.equals(ruleChainById.getType())) {
return ruleChainById;
}
}
return null;
} else {
return null;
}
}
@Override
public List<RuleNode> getRuleChainNodes(TenantId tenantId, RuleChainId ruleChainId) {
Validator.validateId(ruleChainId, "Incorrect rule chain id for search request.");
@ -416,13 +415,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId) {
public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId, boolean remove) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't unassign rule chain from non-existent edge!");
}
if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
if (!remove && edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't unassign root rule chain from edge [" + edge.getName() + "]. Please assign another root rule chain first!");
}
if (ruleChain.removeAssignedEdge(edge)) {
@ -478,6 +477,34 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
});
}
@Override
public RuleChain getDefaultRootEdgeRuleChain(TenantId tenantId) {
return getRootRuleChainByType(tenantId, RuleChainType.EDGE);
}
@Override
public boolean setDefaultRootEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) {
RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
RuleChain previousDefaultRootEdgeRuleChain = getDefaultRootEdgeRuleChain(ruleChain.getTenantId());
if (!previousDefaultRootEdgeRuleChain.getId().equals(ruleChain.getId())) {
try {
deleteRelation(tenantId, new EntityRelation(previousDefaultRootEdgeRuleChain.getTenantId(), previousDefaultRootEdgeRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
previousDefaultRootEdgeRuleChain.setRoot(false);
ruleChainDao.save(tenantId, previousDefaultRootEdgeRuleChain);
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true);
ruleChainDao.save(tenantId, ruleChain);
return true;
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to set default root edge rule chain, ruleChainId: [{}]", ruleChainId);
throw new RuntimeException(e);
}
}
return false;
}
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
List<EntityRelation> nodeRelations = getRuleChainToNodeRelations(tenantId, ruleChainId);
for (EntityRelation relation : nodeRelations) {
@ -536,12 +563,18 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (tenant == null) {
throw new DataValidationException("Rule chain is referencing to non-existent tenant!");
}
if (ruleChain.isRoot()) {
if (ruleChain.isRoot() && RuleChainType.SYSTEM.equals(ruleChain.getType())) {
RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId());
if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) {
throw new DataValidationException("Another root rule chain is present in scope of current tenant!");
}
}
if (ruleChain.isRoot() && RuleChainType.EDGE.equals(ruleChain.getType())) {
RuleChain defaultRootEdgeRuleChain = getDefaultRootEdgeRuleChain(ruleChain.getTenantId());
if (defaultRootEdgeRuleChain != null && !defaultRootEdgeRuleChain.getId().equals(ruleChain.getId())) {
throw new DataValidationException("Another default root edge rule chain is present in scope of current tenant!");
}
}
}
};
@ -579,9 +612,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
protected void removeEntity(TenantId tenantId, RuleChain entity) {
unassignRuleChainFromEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge.getId());
unassignRuleChainFromEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge.getId(), true);
}
}
private class EdgeRuleChainsUpdater extends TimePaginatedRemover<Edge, RuleChain> {

View File

@ -40,13 +40,15 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
addRuleChainEdges: addRuleChainEdges,
removeRuleChainEdges: removeRuleChainEdges,
getEdgeRuleChains: getEdgeRuleChains,
getEdgesRuleChains: getEdgesRuleChains,
assignRuleChainToEdge: assignRuleChainToEdge,
unassignRuleChainFromEdge: unassignRuleChainFromEdge
unassignRuleChainFromEdge: unassignRuleChainFromEdge,
setDefaultRootEdgeRuleChain: setDefaultRootEdgeRuleChain
};
return service;
function getRuleChains (pageLink, config, type) {
function getRuleChains(pageLink, config, type) {
var deferred = $q.defer();
var url = '/api/ruleChains?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
@ -341,6 +343,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
return deferred.promise;
}
function getEdgesRuleChains(pageLink, config) {
return getRuleChains(pageLink, config, types.edgeRuleChainType);
}
function getEdgeRuleChains(edgeId, pageLink, config) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/ruleChains?limit=' + pageLink.limit;
@ -381,6 +387,17 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
return deferred.promise;
}
function setDefaultRootEdgeRuleChain(ruleChainId) {
var deferred = $q.defer();
var url = '/api/ruleChain/' + ruleChainId + '/defaultRootEdge';
$http.post(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function prepareRuleChains(ruleChainsData) {
if (ruleChainsData.data) {
for (var i = 0; i < ruleChainsData.data.length; i++) {

View File

@ -1447,7 +1447,10 @@
"assign-to-edges-text": "Please select the edges to assign the rulechain(s)",
"unassign-from-edges": "Unassign Rule Chain(s) From Edges",
"unassign-from-edges-text": "Please select the edges to unassign from the rulechain(s)",
"assigned-to-edges": "Assigned to edges"
"assigned-to-edges": "Assigned to edges",
"set-default-root-edge": "Make rule chain default root",
"set-default-root-edge-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' default edge root?",
"set-default-root-edge-rulechain-text": "After the confirmation the rule chain will become default edge root and will handle all incoming transport messages."
},
"rulenode": {
"details": "Details",

View File

@ -52,7 +52,7 @@ export default function AddRuleChainsToEdgeController(ruleChainService, $mdDialo
fetchMoreItems_: function () {
if (vm.ruleChains.hasNext && !vm.ruleChains.pending) {
vm.ruleChains.pending = true;
ruleChainService.getRuleChains(vm.ruleChains.nextPageLink).then(
ruleChainService.getEdgesRuleChains(vm.ruleChains.nextPageLink).then(
function success(ruleChains) {
vm.ruleChains.data = ruleChains.data;
vm.ruleChains.nextPageLink = ruleChains.nextPageLink;

View File

@ -202,6 +202,16 @@ export default function RuleChainsController(ruleChainService, userService, edge
isEnabled: isNonRootRuleChain
});
ruleChainActionsList.push({
onAction: function ($event, item) {
setDefaultRootEdgeRuleChain($event, item);
},
name: function() { return $translate.instant('rulechain.set-default-root-edge') },
details: function() { return $translate.instant('rulechain.set-default-root-edge') },
icon: "flag",
isEnabled: isNonRootRuleChain
});
ruleChainGroupActionsList.push(
{
onAction: function ($event, items) {
@ -426,6 +436,24 @@ export default function RuleChainsController(ruleChainService, userService, edge
});
}
function setDefaultRootEdgeRuleChain($event, ruleChain) {
$event.stopPropagation();
var confirm = $mdDialog.confirm()
.targetEvent($event)
.title($translate.instant('rulechain.set-default-root-edge-rulechain-title', {ruleChainName: ruleChain.name}))
.htmlContent($translate.instant('rulechain.set-default-root-edge-rulechain-text'))
.ariaLabel($translate.instant('rulechain.set-root-rulechain-text'))
.cancel($translate.instant('action.no'))
.ok($translate.instant('action.yes'));
$mdDialog.show(confirm).then(function () {
ruleChainService.setDefaultRootEdgeRuleChain(ruleChain.id.id).then(
() => {
vm.grid.refreshList();
}
);
});
}
function manageAssignedEdges($event, ruleChain) {
showManageAssignedEdgesDialog($event, [ruleChain.id.id], 'manage', ruleChain.assignedEdgesIds);
}
@ -488,7 +516,7 @@ export default function RuleChainsController(ruleChainService, userService, edge
$event.stopPropagation();
}
var pageSize = 10;
ruleChainService.getRuleChains({limit: pageSize, textSearch: ''}).then(
ruleChainService.getEdgesRuleChains({limit: pageSize, textSearch: ''}).then(
function success(_ruleChains) {
var ruleChains = {
pageSize: pageSize,

View File

@ -231,12 +231,12 @@ function Menu(userService, $state, $rootScope) {
{
name: 'rulechain.system-rulechains',
icon: 'settings_ethernet',
state: 'home.ruleChains'
state: 'home.ruleChains.system'
},
{
name: 'rulechain.edge-rulechains',
icon: 'router',
state: 'home.edgesRuleChains'
state: 'home.ruleChains.edge'
}
]
},