diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index 709ab4fee6..f26cf21934 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java index 8f24b737f2..31eca488b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java @@ -109,7 +109,7 @@ public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); - ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> ruleChainFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index c8ca814fe8..8afbfabb62 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -109,7 +109,7 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); - ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + ListenableFuture> relations = relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); return Futures.transformAsync(relations, input -> { List> ruleChainsFutures = new ArrayList<>(input.size()); for (EntityRelation relation : input) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index 1c5c9658a6..f37017068e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -23,6 +23,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.TextPageData; import org.thingsboard.server.common.data.page.TextPageLink; @@ -318,6 +319,28 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { ruleChainService.deleteRuleChainById(tenantId, savedRuleChainMetaData.getRuleChainId()); } + @Test + public void testGetDefaultEdgeRuleChains() throws Exception { + RuleChainId ruleChainId = saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 1"); + saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 2"); + List result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); + Assert.assertEquals(2, result.size()); + + ruleChainService.removeDefaultEdgeRuleChain(tenantId, ruleChainId); + + result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); + Assert.assertEquals(1, result.size()); + } + private RuleChainId saveRuleChainAndSetDefaultEdge(String name) { + RuleChain edgeRuleChain = new RuleChain(); + edgeRuleChain.setTenantId(tenantId); + edgeRuleChain.setType(RuleChainType.EDGE); + edgeRuleChain.setName(name); + RuleChain savedEdgeRuleChain = ruleChainService.saveRuleChain(edgeRuleChain); + ruleChainService.addDefaultEdgeRuleChain(tenantId, savedEdgeRuleChain.getId()); + return savedEdgeRuleChain.getId(); + } + private RuleChainMetaData createRuleChainMetadata() throws Exception { RuleChain ruleChain = new RuleChain(); ruleChain.setName("My RuleChain"); diff --git a/msa/js-executor/package-lock.json b/msa/js-executor/package-lock.json index 2d7b2b2726..9b4eb92a82 100644 --- a/msa/js-executor/package-lock.json +++ b/msa/js-executor/package-lock.json @@ -1942,14 +1942,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1964,20 +1962,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -2094,8 +2089,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -2107,7 +2101,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2122,7 +2115,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2234,8 +2226,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -2247,7 +2238,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -2369,7 +2359,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 9fef9f1679..1fbb81066e 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -1554,6 +1554,7 @@ "assign-rulechains": "Assign rulechains", "assign-new-rulechain": "Assign new rulechain", "delete-rulechains": "Delete rulechains", + "default": "Default", "unassign-rulechain": "Unassign rulechain", "unassign-rulechains": "Unassign rulechains", "unassign-rulechain-title": "Are you sure you want to unassign the rulechain '{{ruleChainTitle}}'?", @@ -1576,7 +1577,13 @@ "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.", - "invalid-rulechain-type-error": "Unable to import rule chain: Invalid rule chain type. Expected type is {{expectedRuleChainType}}." + "invalid-rulechain-type-error": "Unable to import rule chain: Invalid rule chain type. Expected type is {{expectedRuleChainType}}.", + "set-default-edge": "Make edge rule chain default", + "set-default-edge-title": "Are you sure you want to make the edge rule chain '{{ruleChainName}}' default?", + "set-default-edge-text": "After the confirmation the edge rule chain will be added to default list and handle all incoming transport messages.", + "remove-default-edge": "Remove edge rule chain from defaults", + "remove-default-edge-title": "Are you sure you want to remove the edge rule chain '{{ruleChainName}}' from default list?", + "remove-default-edge-text": "After the confirmation the edge rule chain will stop handling all incoming transport messages." }, "rulenode": { "details": "Details", diff --git a/ui/src/app/rulechain/rulechain-card.tpl.html b/ui/src/app/rulechain/rulechain-card.tpl.html index 86a7bbbb22..4e28413327 100644 --- a/ui/src/app/rulechain/rulechain-card.tpl.html +++ b/ui/src/app/rulechain/rulechain-card.tpl.html @@ -15,5 +15,11 @@ limitations under the License. --> -
{{'rulechain.assigned-to-edges' | translate}}: '{{vm.item.assignedEdgesText}}'
-
rulechain.root
+
rulechain.root +
+
rulechain.default +
diff --git a/ui/src/app/rulechain/rulechain.routes.js b/ui/src/app/rulechain/rulechain.routes.js index 13c1844f74..8f12031873 100644 --- a/ui/src/app/rulechain/rulechain.routes.js +++ b/ui/src/app/rulechain/rulechain.routes.js @@ -209,7 +209,7 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider ruleChainsType: 'edge' }, ncyBreadcrumb: { - label: '{"icon": "settings_ethernet", "label": "{{ vm.edgeRuleChainsTitle }}", "translate": "false"}' + label: '{"icon": "settings_ethernet", "label": "rulechain.edge-rulechains"}' } }).state('home.edges.ruleChains.ruleChain', { url: '/:ruleChainId', @@ -249,4 +249,4 @@ export default function RuleChainRoutes($stateProvider, NodeTemplatePathProvider label: '{"icon": "settings_ethernet", "label": "{{ vm.ruleChain.name }}", "translate": "false"}' } }); -} \ No newline at end of file +} diff --git a/ui/src/app/rulechain/rulechains.controller.js b/ui/src/app/rulechain/rulechains.controller.js index ff1d393618..eac031011a 100644 --- a/ui/src/app/rulechain/rulechains.controller.js +++ b/ui/src/app/rulechain/rulechains.controller.js @@ -94,6 +94,8 @@ export default function RuleChainsController(ruleChainService, userService, edge vm.isRootRuleChain = isRootRuleChain; vm.isNonRootRuleChain = isNonRootRuleChain; + vm.isDefaultEdgeRuleChain = isDefaultEdgeRuleChain; + var defaultEdgeRuleChainIds = []; vm.exportRuleChain = exportRuleChain; vm.setRootRuleChain = setRootRuleChain; @@ -234,9 +236,10 @@ export default function RuleChainsController(ruleChainService, userService, edge details: function() { return $translate.instant('rulechain.import') }, icon: "file_upload" }); + } else if (vm.ruleChainsScope === 'edge') { fetchRuleChainsFunction = function (pageLink) { - return ruleChainService.getEdgeRuleChains(edgeId, pageLink); + return fetchEdgeRuleChains(edgeId, pageLink); }; deleteRuleChainFunction = function (ruleChainId) { return ruleChainService.unassignRuleChainFromEdge(edgeId, ruleChainId); @@ -252,6 +255,26 @@ export default function RuleChainsController(ruleChainService, userService, edge isEnabled: isNonRootRuleChain }); + ruleChainActionsList.push({ + onAction: function ($event, item) { + setDefaultEdgeRuleChain($event, item); + }, + name: function() { return $translate.instant('rulechain.set-default-edge') }, + details: function() { return $translate.instant('rulechain.set-default-edge') }, + icon: "bookmark_outline", + isEnabled: isNonDefaultEdgeRuleChain + }); + + ruleChainActionsList.push({ + onAction: function ($event, item) { + removeDefaultEdgeRuleChain($event, item); + }, + name: function() { return $translate.instant('rulechain.remove-default-edge') }, + details: function() { return $translate.instant('rulechain.remove-default-edge') }, + icon: "bookmark", + isEnabled: isDefaultEdgeRuleChain + }); + ruleChainActionsList.push( { onAction: function ($event, item) { @@ -315,29 +338,51 @@ export default function RuleChainsController(ruleChainService, userService, edge vm.grid = grid; } + function getDefaultEdges(ruleChains) { + var deferred = $q.defer(); + ruleChainService.getDefaultEdgeRuleChains(null).then( + function success(response) { + response.map(function (ruleChain) { + defaultEdgeRuleChainIds.push(ruleChain.id.id) + }); + + const data = ruleChains.data; + data.map(function (ruleChain) { + ruleChain.isDefault = defaultEdgeRuleChainIds.some( + id => ruleChain.id.id.includes(id)); + return ruleChain; + }); + ruleChains.data = data; + + deferred.resolve(ruleChains); + }, function fail() { + deferred.reject(); + } + ) + return deferred.promise; + } + function fetchRuleChains(pageLink, type) { - if (type === types.systemRuleChainType) { - return ruleChainService.getRuleChains(pageLink, null, type); - } else { - var deferred = $q.defer(); - ruleChainService.getRuleChains(pageLink, null, type).then( - // TODO: deaflynx - // function success(response) { - // ruleChainService.getDefaultEdgeRuleChains().then( - // function success(defaultEdgeRuleChains) { - // // response.data merge with defaultEdgeRuleChains - // deferred.resolve(data); - // }, - // function fail() { - // deferred.reject(); - // } - // ); - // }, - function fail() { + return ruleChainService.getRuleChains(pageLink, null, type); + } + + function fetchEdgeRuleChains(edgeId, pageLink) { + var deferred = $q.defer(); + ruleChainService.getRuleChains(pageLink, null, types.edgeRuleChainType).then( + function success(ruleChains) { + getDefaultEdges(ruleChains).then( + function success(response) { + deferred.resolve(response); + }, function fail() { + deferred.reject(); + } + ); + // deferred.resolve(response); + }, function fail() { deferred.reject(); - }); - return deferred.promise; - } + } + ); + return deferred.promise; } function saveRuleChain(ruleChain) { @@ -389,6 +434,14 @@ export default function RuleChainsController(ruleChainService, userService, edge } } + function isDefaultEdgeRuleChain(ruleChain) { + return angular.isDefined(ruleChain) && !ruleChain.root && defaultEdgeRuleChainIds.includes(ruleChain.id.id) === true; + } + + function isNonDefaultEdgeRuleChain(ruleChain) { + return angular.isDefined(ruleChain) && !ruleChain.root && defaultEdgeRuleChainIds.includes(ruleChain.id.id) === false; + } + function exportRuleChain($event, ruleChain) { $event.stopPropagation(); importExport.exportRuleChain(ruleChain.id.id); @@ -421,6 +474,42 @@ export default function RuleChainsController(ruleChainService, userService, edge }); } + function setDefaultEdgeRuleChain($event, ruleChain) { + $event.stopPropagation(); + var confirm = $mdDialog.confirm() + .targetEvent($event) + .title($translate.instant('rulechain.set-default-edge-title', {ruleChainName: ruleChain.name})) + .htmlContent($translate.instant('rulechain.set-default-edge-text')) + .ariaLabel($translate.instant('rulechain.set-default-edge')) + .cancel($translate.instant('action.no')) + .ok($translate.instant('action.yes')); + $mdDialog.show(confirm).then(function () { + ruleChainService.addDefaultEdgeRuleChain(ruleChain.id.id).then( + () => { + vm.grid.refreshList(); + } + ); + }); + } + + function removeDefaultEdgeRuleChain($event, ruleChain) { + $event.stopPropagation(); + var confirm = $mdDialog.confirm() + .targetEvent($event) + .title($translate.instant('rulechain.remove-default-edge-title', {ruleChainName: ruleChain.name})) + .htmlContent($translate.instant('rulechain.remove-default-edge-text')) + .ariaLabel($translate.instant('rulechain.remove-default-edge')) + .cancel($translate.instant('action.no')) + .ok($translate.instant('action.yes')); + $mdDialog.show(confirm).then(function () { + ruleChainService.removeDefaultEdgeRuleChain(ruleChain.id.id).then( + () => { + vm.grid.refreshList(); + } + ); + }); + } + function setDefaultRootEdgeRuleChain($event, ruleChain) { $event.stopPropagation(); var confirm = $mdDialog.confirm()