@openremote/or-rules 1.8.0-snapshot.20250725074716 → 1.8.0-snapshot.20250725120001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -30
- package/custom-elements.json +13 -13
- package/dist/umd/index.bundle.js +4744 -4744
- package/dist/umd/index.bundle.js.map +1 -1
- package/lib/flow-viewer/components/confirmation-dialog.js +61 -24
- package/lib/flow-viewer/components/connection-container.js +35 -1
- package/lib/flow-viewer/components/connection-line.js +117 -28
- package/lib/flow-viewer/components/context-menu.js +140 -45
- package/lib/flow-viewer/components/editor-workspace.js +282 -20
- package/lib/flow-viewer/components/flow-editor.js +160 -47
- package/lib/flow-viewer/components/flow-node-socket.js +146 -31
- package/lib/flow-viewer/components/flow-node.js +192 -29
- package/lib/flow-viewer/components/internal-picker.js +271 -54
- package/lib/flow-viewer/components/node-menu-item.js +132 -32
- package/lib/flow-viewer/components/node-panel.js +104 -60
- package/lib/flow-viewer/components/notification-dialog.js +55 -23
- package/lib/flow-viewer/components/popup-modal.js +113 -54
- package/lib/flow-viewer/components/rule-browser.js +119 -30
- package/lib/flow-viewer/components/selectable-element.js +71 -1
- package/lib/flow-viewer/components/selection-box.js +119 -15
- package/lib/flow-viewer/components/top-bar.js +116 -49
- package/lib/flow-viewer/components/workspace-contextmenu-options.js +128 -5
- package/lib/flow-viewer/components/writable-dropdown.js +51 -5
- package/lib/flow-viewer/converters/node-converter.js +10 -1
- package/lib/flow-viewer/flow-viewer.js +19 -1
- package/lib/flow-viewer/models/camera.js +2 -1
- package/lib/flow-viewer/models/context-menu-button.js +2 -1
- package/lib/flow-viewer/models/light-node-collection.js +2 -1
- package/lib/flow-viewer/models/status.js +8 -1
- package/lib/flow-viewer/node-structure/copy.machine.js +34 -1
- package/lib/flow-viewer/node-structure/identity.assigner.js +10 -1
- package/lib/flow-viewer/node-structure/identity.dom.link.js +4 -1
- package/lib/flow-viewer/node-structure/index.js +5 -1
- package/lib/flow-viewer/node-structure/socket.type.matcher.js +50 -1
- package/lib/flow-viewer/node-structure/utils.js +109 -1
- package/lib/flow-viewer/services/copy-paste-manager.js +59 -1
- package/lib/flow-viewer/services/exporter.js +67 -1
- package/lib/flow-viewer/services/input.js +80 -1
- package/lib/flow-viewer/services/integration.js +27 -1
- package/lib/flow-viewer/services/modal.js +29 -8
- package/lib/flow-viewer/services/project.js +222 -1
- package/lib/flow-viewer/services/shortcuts.js +63 -1
- package/lib/flow-viewer/styles/editor-workspace-style.js +55 -53
- package/lib/flow-viewer/styles/flow-node-style.js +95 -93
- package/lib/flow-viewer/styles/picker-styles.js +31 -29
- package/lib/flow-viewer/utils.js +49 -1
- package/lib/index.js +953 -56
- package/lib/json-viewer/forms/or-rule-form-alarm.js +91 -18
- package/lib/json-viewer/forms/or-rule-form-email-message.js +51 -12
- package/lib/json-viewer/forms/or-rule-form-localized.js +269 -43
- package/lib/json-viewer/forms/or-rule-form-push-notification.js +152 -63
- package/lib/json-viewer/forms/or-rule-form-webhook.js +296 -101
- package/lib/json-viewer/modals/or-rule-alarm-modal.js +173 -17
- package/lib/json-viewer/modals/or-rule-notification-modal.js +196 -11
- package/lib/json-viewer/modals/or-rule-radial-modal.js +142 -17
- package/lib/json-viewer/modals/or-rule-webhook-modal.js +78 -8
- package/lib/json-viewer/or-rule-action-alarm.js +97 -5
- package/lib/json-viewer/or-rule-action-attribute.js +235 -33
- package/lib/json-viewer/or-rule-action-notification.js +465 -56
- package/lib/json-viewer/or-rule-action-webhook.js +49 -18
- package/lib/json-viewer/or-rule-asset-query.js +849 -126
- package/lib/json-viewer/or-rule-condition.js +216 -29
- package/lib/json-viewer/or-rule-json-viewer.js +393 -34
- package/lib/json-viewer/or-rule-then-otherwise.js +609 -113
- package/lib/json-viewer/or-rule-trigger-query.js +227 -57
- package/lib/json-viewer/or-rule-when.js +343 -126
- package/lib/or-rule-group-viewer.js +106 -12
- package/lib/or-rule-text-viewer.js +133 -22
- package/lib/or-rule-tree.js +601 -57
- package/lib/or-rule-validity.js +373 -62
- package/lib/or-rule-viewer.js +361 -81
- package/lib/style.js +89 -64
- package/package.json +11 -11
package/lib/or-rule-tree.js
CHANGED
|
@@ -1,57 +1,601 @@
|
|
|
1
|
-
var
|
|
2
|
-
.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var OrRuleTree_1;
|
|
17
|
+
/*
|
|
18
|
+
* Copyright 2025, OpenRemote Inc.
|
|
19
|
+
*
|
|
20
|
+
* See the CONTRIBUTORS.txt file in the distribution for a
|
|
21
|
+
* full listing of individual contributors.
|
|
22
|
+
*
|
|
23
|
+
* This program is free software: you can redistribute it and/or modify
|
|
24
|
+
* it under the terms of the GNU Affero General Public License as
|
|
25
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
26
|
+
* License, or (at your option) any later version.
|
|
27
|
+
*
|
|
28
|
+
* This program is distributed in the hope that it will be useful,
|
|
29
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
30
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
31
|
+
* GNU Affero General Public License for more details.
|
|
32
|
+
*
|
|
33
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
34
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
35
|
+
*/
|
|
36
|
+
import { OrTreeMenu, TreeMenuSelection } from "@openremote/or-tree-menu";
|
|
37
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
38
|
+
import { css, html } from "lit";
|
|
39
|
+
import manager, { Util } from "@openremote/core";
|
|
40
|
+
import { i18next } from "@openremote/or-translate";
|
|
41
|
+
import { getContentWithMenuTemplate } from "@openremote/or-mwc-components/or-mwc-menu";
|
|
42
|
+
import { InputType } from "@openremote/or-mwc-components/or-mwc-input";
|
|
43
|
+
import { showOkCancelDialog } from "@openremote/or-mwc-components/or-mwc-dialog";
|
|
44
|
+
import { OrRules, OrRulesAddEvent, OrRulesRequestAddEvent, OrRulesRequestDeleteEvent, OrRulesRequestGroupEvent, OrRulesRequestSelectionEvent, OrRulesSelectionEvent, RuleViewInfoMap } from "./index";
|
|
45
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
46
|
+
import { when } from "lit/directives/when.js";
|
|
47
|
+
const styling = css `
|
|
48
|
+
.iconfill-gray {
|
|
49
|
+
--or-icon-fill: var(--internal-or-rules-list-icon-color-ok);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.iconfill-red {
|
|
53
|
+
--or-icon-fill: var(--internal-or-rules-list-icon-color-error);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#rules-tree-global-header {
|
|
57
|
+
display: flex;
|
|
58
|
+
justify-content: space-between;
|
|
59
|
+
align-items: center;
|
|
60
|
+
padding: 0 15px;
|
|
61
|
+
min-height: 48px;
|
|
62
|
+
background: var(--or-app-color3, #4c4c4c);
|
|
63
|
+
color: var(--or-app-color7, white);
|
|
64
|
+
--or-icon-fill: var(--or-app-color7, white);
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
export var RuleTreeSorting;
|
|
68
|
+
(function (RuleTreeSorting) {
|
|
69
|
+
RuleTreeSorting["NAME"] = "name";
|
|
70
|
+
RuleTreeSorting["CREATED_ON"] = "createdOn";
|
|
71
|
+
RuleTreeSorting["LANGUAGE"] = "language";
|
|
72
|
+
RuleTreeSorting["STATUS"] = "status";
|
|
73
|
+
})(RuleTreeSorting || (RuleTreeSorting = {}));
|
|
74
|
+
let OrRuleTree = OrRuleTree_1 = class OrRuleTree extends OrTreeMenu {
|
|
75
|
+
constructor() {
|
|
76
|
+
super(...arguments);
|
|
77
|
+
/**
|
|
78
|
+
* Defines whether only global rulesets should be visible.
|
|
79
|
+
*/
|
|
80
|
+
this.global = false;
|
|
81
|
+
/**
|
|
82
|
+
* Sets every element in the tree to readonly mode
|
|
83
|
+
*/
|
|
84
|
+
this.readonly = false;
|
|
85
|
+
this.nodes = [];
|
|
86
|
+
this.draggable = true;
|
|
87
|
+
this.selection = TreeMenuSelection.MULTI;
|
|
88
|
+
this.sortOptions = [RuleTreeSorting.NAME, RuleTreeSorting.CREATED_ON, RuleTreeSorting.STATUS, RuleTreeSorting.LANGUAGE];
|
|
89
|
+
this.sortBy = RuleTreeSorting.NAME;
|
|
90
|
+
this.groupFirst = true;
|
|
91
|
+
this.menuTitle = "rules";
|
|
92
|
+
this._loadingPromises = [];
|
|
93
|
+
this._selectedTypes = new Set();
|
|
94
|
+
// Cache of empty groups (when no rule has this groupId), so they stay during refreshes.
|
|
95
|
+
this._cachedEmptyGroupNodes = [];
|
|
96
|
+
}
|
|
97
|
+
static get styles() {
|
|
98
|
+
return [...super.styles, styling];
|
|
99
|
+
}
|
|
100
|
+
willUpdate(changedProps) {
|
|
101
|
+
var _a, _b, _c;
|
|
102
|
+
if (changedProps.has("rules")) {
|
|
103
|
+
if (!this.rules) {
|
|
104
|
+
this._loadRulesets(this.global).then(rulesets => this.rules = rulesets);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// This stops a group from showing up twice after its first rule is added.
|
|
108
|
+
// It removes the old, empty version of the group from memory so only the new, updated one is displayed.
|
|
109
|
+
const ruleNodes = this._getRuleNodes(this.rules);
|
|
110
|
+
const ruleNodeIds = new Set(ruleNodes.map(node => node.id));
|
|
111
|
+
const emptyGroups = this._cachedEmptyGroupNodes
|
|
112
|
+
.filter(cachedNode => !ruleNodeIds.has(cachedNode.id));
|
|
113
|
+
this.nodes = [...ruleNodes, ...emptyGroups];
|
|
114
|
+
this._cachedEmptyGroupNodes = emptyGroups;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (changedProps.has("nodes")) {
|
|
118
|
+
this._cachedEmptyGroupNodes = ((_a = this.nodes) === null || _a === void 0 ? void 0 : _a.filter(n => !!n.children && n.children.length === 0)) || [];
|
|
119
|
+
}
|
|
120
|
+
if (changedProps.has("config") && this.config) {
|
|
121
|
+
if (((_b = this.config.controls) === null || _b === void 0 ? void 0 : _b.multiSelect) !== undefined) {
|
|
122
|
+
this.selection = ((_c = this.config.controls) === null || _c === void 0 ? void 0 : _c.multiSelect) ? TreeMenuSelection.MULTI : TreeMenuSelection.LEAF;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return super.willUpdate(changedProps);
|
|
126
|
+
}
|
|
127
|
+
firstUpdated(changedProps) {
|
|
128
|
+
if (!this.rules) {
|
|
129
|
+
this._loadRulesets(this.global).then(rulesets => {
|
|
130
|
+
this.rules = rulesets;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
_getSingleNodeSlotTemplate(node) {
|
|
135
|
+
var _a, _b, _c;
|
|
136
|
+
return html `
|
|
137
|
+
<or-icon slot="prefix" icon="${this._getRulesetLangIcon((_a = node.ruleset) === null || _a === void 0 ? void 0 : _a.lang)}"></or-icon>
|
|
138
|
+
<span>${node.label}</span>
|
|
139
|
+
<or-icon slot="suffix" icon=${ifDefined(this._getRulesetStatusIcon((_b = node.ruleset) === null || _b === void 0 ? void 0 : _b.status))}
|
|
140
|
+
class=${this._getRulesetStatusColorClass((_c = node.ruleset) === null || _c === void 0 ? void 0 : _c.status)}
|
|
141
|
+
></or-icon>
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
_getGroupNodeSlotTemplate(node) {
|
|
145
|
+
var _a;
|
|
146
|
+
let icon;
|
|
147
|
+
const childrenStatus = (_a = node.children) === null || _a === void 0 ? void 0 : _a.map(child => { var _a; return (_a = child.ruleset) === null || _a === void 0 ? void 0 : _a.status; }).filter(status => status);
|
|
148
|
+
const hasError = (childrenStatus === null || childrenStatus === void 0 ? void 0 : childrenStatus.find(c => ["LOOP_ERROR" /* RulesetStatus.LOOP_ERROR */, "EXECUTION_ERROR" /* RulesetStatus.EXECUTION_ERROR */, "COMPILATION_ERROR" /* RulesetStatus.COMPILATION_ERROR */].includes(c))) || false;
|
|
149
|
+
if (hasError) {
|
|
150
|
+
const errorCounts = ["LOOP_ERROR" /* RulesetStatus.LOOP_ERROR */, "EXECUTION_ERROR" /* RulesetStatus.EXECUTION_ERROR */, "COMPILATION_ERROR" /* RulesetStatus.COMPILATION_ERROR */].map(err => [err, childrenStatus.filter(s => s === err).length]);
|
|
151
|
+
const mostCommonError = errorCounts.reduce((prev, curr) => curr[1] > prev[1] ? curr : prev)[0];
|
|
152
|
+
icon = this._getRulesetStatusIcon(mostCommonError);
|
|
153
|
+
}
|
|
154
|
+
return html `
|
|
155
|
+
<or-icon slot="prefix" icon="folder"></or-icon>
|
|
156
|
+
<span>${node.label}</span>
|
|
157
|
+
<or-icon slot="suffix" icon=${ifDefined(icon)} class="iconfill-red"></or-icon>
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
_getHeaderTemplate() {
|
|
161
|
+
return html `
|
|
162
|
+
${when(manager.isSuperUser(), () => html `
|
|
163
|
+
<div id="rules-tree-global-header">
|
|
164
|
+
<or-translate value="realmRules"></or-translate>
|
|
165
|
+
<or-mwc-input type=${InputType.SWITCH} @or-mwc-input-changed=${this._onGlobalSwitch}></or-mwc-input>
|
|
166
|
+
<or-translate value="globalRules"></or-translate>
|
|
167
|
+
</div>
|
|
168
|
+
`)}
|
|
169
|
+
<div id="tree-header">
|
|
170
|
+
<h3 id="tree-header-title">
|
|
171
|
+
<or-translate value=${this.menuTitle}></or-translate>
|
|
172
|
+
</h3>
|
|
173
|
+
<div id="tree-header-actions">
|
|
174
|
+
${when((this._selectedTypes.size === 1 && this._selectedTypes.has('ruleset')) && !this.readonly, () => html `
|
|
175
|
+
${when(this._findSelectedTreeNodes().length === 1, () => html `
|
|
176
|
+
<or-mwc-input type=${InputType.BUTTON} icon="content-copy" @or-mwc-input-changed=${this._onCopyClicked}></or-mwc-input>
|
|
177
|
+
`)}
|
|
178
|
+
<or-mwc-input type=${InputType.BUTTON} icon="delete" @or-mwc-input-changed=${this._onDeleteClicked}></or-mwc-input>
|
|
179
|
+
`)}
|
|
180
|
+
${when((this._selectedTypes.size === 1 && this._selectedTypes.has('group')) && !this.readonly, () => html `
|
|
181
|
+
<or-mwc-input type=${InputType.BUTTON} icon="delete" @or-mwc-input-changed=${this._onDeleteClicked}></or-mwc-input>
|
|
182
|
+
`)}
|
|
183
|
+
${this._getAddActionTemplate()}
|
|
184
|
+
${this._getSortActionTemplate(this.sortBy, this.sortOptions)}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* HTML callback on when the global switch is toggled.
|
|
191
|
+
* It normally toggles between 'global' and 'realm' rulesets when the user is a superuser.
|
|
192
|
+
*/
|
|
193
|
+
_onGlobalSwitch(ev) {
|
|
194
|
+
this.global = ev.detail.value;
|
|
195
|
+
this.refresh();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* HTML callback on when the 'copy' menu option is pressed.
|
|
199
|
+
* The function duplicates the current selected rule, and shows it inside the viewer window.
|
|
200
|
+
*/
|
|
201
|
+
_onCopyClicked(_ev) {
|
|
202
|
+
const selected = this._findSelectedTreeNodes();
|
|
203
|
+
if (selected.length !== 1) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const node = selected[0];
|
|
207
|
+
const ruleset = JSON.parse(JSON.stringify(node.ruleset));
|
|
208
|
+
delete ruleset.lastModified;
|
|
209
|
+
delete ruleset.createdOn;
|
|
210
|
+
delete ruleset.status;
|
|
211
|
+
delete ruleset.error;
|
|
212
|
+
delete ruleset.id;
|
|
213
|
+
ruleset.name = ruleset.name + " copy";
|
|
214
|
+
if (this.config && this.config.rulesetCopyHandler && !this.config.rulesetCopyHandler(ruleset)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
Util.dispatchCancellableEvent(this, new OrRulesRequestAddEvent({
|
|
218
|
+
ruleset: ruleset,
|
|
219
|
+
sourceRuleset: node.ruleset
|
|
220
|
+
})).then((detail) => {
|
|
221
|
+
if (detail.allow) {
|
|
222
|
+
this.dispatchEvent(new OrRulesAddEvent(detail.detail));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* HTML callback on when the 'delete' menu option is pressed.
|
|
228
|
+
* The function deletes the rule that is affiliated with selected tree node,
|
|
229
|
+
* after prompting a modal that the user confirms this action.
|
|
230
|
+
*/
|
|
231
|
+
_onDeleteClicked(_ev) {
|
|
232
|
+
const selected = this._findSelectedTreeNodes();
|
|
233
|
+
if (selected.length > 0) {
|
|
234
|
+
const selectedRules = selected.map(node => node.ruleset).filter(x => x);
|
|
235
|
+
// If groups are selected, loop through the children, and add them to the selectedRules;
|
|
236
|
+
const groupNodes = selected.filter(group => group.children);
|
|
237
|
+
const groupNames = groupNodes.map(node => node.label);
|
|
238
|
+
const selectedChildNodes = groupNodes.flatMap(group => group.children);
|
|
239
|
+
const selectedChildRules = selectedChildNodes.flatMap(node => node.ruleset ? [node.ruleset] : []);
|
|
240
|
+
selectedRules.push(...selectedChildRules);
|
|
241
|
+
// Transform rules into RulesetNode for consumers of the or-rule-tree
|
|
242
|
+
const selectedRuleNodes = selectedRules.map(ruleset => ({ ruleset: ruleset, selected: true }));
|
|
243
|
+
// If only groups are selected without any rules, prompt "delete groups" dialog.
|
|
244
|
+
if (selectedRuleNodes.length === 0 && groupNodes.length > 0) {
|
|
245
|
+
showOkCancelDialog(i18next.t("deleteRulesetGroups"), i18next.t("deleteRulesetGroupsConfirm", { groupNames: groupNames }), i18next.t("delete"))
|
|
246
|
+
.then((ok) => {
|
|
247
|
+
if (ok) {
|
|
248
|
+
this.deselectAllNodes();
|
|
249
|
+
this.nodes = this.nodes.filter(n => !groupNames.includes(n.label));
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// Request deletion of rulesets, and prompt dialog.
|
|
255
|
+
Util.dispatchCancellableEvent(this, new OrRulesRequestDeleteEvent(selectedRuleNodes))
|
|
256
|
+
.then((detail) => {
|
|
257
|
+
if (detail.allow) {
|
|
258
|
+
const names = selectedRules.map(r => r.name);
|
|
259
|
+
showOkCancelDialog(i18next.t("deleteRulesets"), i18next.t("deleteRulesetsConfirm", { ruleNames: names }), i18next.t("delete"))
|
|
260
|
+
.then((ok) => {
|
|
261
|
+
if (ok) {
|
|
262
|
+
this.deselectAllNodes();
|
|
263
|
+
this._deleteRulesets(selectedRules)
|
|
264
|
+
.catch(e => console.error(e))
|
|
265
|
+
.finally(() => {
|
|
266
|
+
this.nodes = this.nodes.filter(n => !groupNames.includes(n.label));
|
|
267
|
+
this.refresh();
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Utility function that returns a promise for deleting a list of rulesets, based on their type.
|
|
277
|
+
* @param rulesets - List of rulesets to delete.
|
|
278
|
+
*/
|
|
279
|
+
_deleteRulesets(rulesets) {
|
|
280
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
281
|
+
const promises = [];
|
|
282
|
+
rulesets.forEach(ruleset => {
|
|
283
|
+
switch (ruleset.type) {
|
|
284
|
+
case "asset":
|
|
285
|
+
promises.push(manager.rest.api.RulesResource.deleteAssetRuleset(ruleset.id));
|
|
286
|
+
break;
|
|
287
|
+
case "realm":
|
|
288
|
+
promises.push(manager.rest.api.RulesResource.deleteRealmRuleset(ruleset.id));
|
|
289
|
+
break;
|
|
290
|
+
case "global":
|
|
291
|
+
promises.push(manager.rest.api.RulesResource.deleteGlobalRuleset(ruleset.id));
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
return Promise.all(promises);
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Public function that allows consumers to select a ruleset in the tree.
|
|
300
|
+
* It finds the tree node in the UI, and selects it.
|
|
301
|
+
* @param ruleset - ruleset in the tree to be selected
|
|
302
|
+
* @param silent - skips the modal of 'discard changes' and does not dispatch an event
|
|
303
|
+
*/
|
|
304
|
+
selectRuleset(ruleset, silent = false) {
|
|
305
|
+
const rulesetNode = this.nodes.find(n => n.ruleset === ruleset);
|
|
306
|
+
if (rulesetNode) {
|
|
307
|
+
const uiNode = this._getUiNodeFromTree(rulesetNode);
|
|
308
|
+
if (uiNode)
|
|
309
|
+
this._selectNode(uiNode, !silent);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
console.warn("Could not select ruleset; it was not found.");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
_dispatchSelectEvent(nodes) {
|
|
316
|
+
const lastSelected = this._lastSelectedNode ? this._getTreeNodeFromTree(this._lastSelectedNode) : undefined;
|
|
317
|
+
// Only select if ONE is selected
|
|
318
|
+
const newSelected = (!nodes || nodes.length > 1) ? undefined : nodes[0];
|
|
319
|
+
const isGroupNode = (node) => !!(node === null || node === void 0 ? void 0 : node.children);
|
|
320
|
+
const oldNodes = lastSelected ? isGroupNode(lastSelected) ? [{ type: "group", groupId: lastSelected.id }] : [{ type: "rule", ruleset: lastSelected.ruleset, selected: false }] : [];
|
|
321
|
+
const newNodes = newSelected ? isGroupNode(newSelected) ? [{ type: "group", groupId: newSelected.id }] : [{ type: "rule", ruleset: newSelected.ruleset, selected: true }] : [];
|
|
322
|
+
// Ask for consumers if selection is allowed. If event.preventDefault() is called, we cancel the selection
|
|
323
|
+
const success = this.dispatchEvent(new OrRulesRequestSelectionEvent({ oldNodes: oldNodes, newNodes: newNodes }));
|
|
324
|
+
if (!success)
|
|
325
|
+
return false;
|
|
326
|
+
// If succeeded, actually sent the "select" event.
|
|
327
|
+
this.dispatchEvent(new OrRulesSelectionEvent({ oldNodes: oldNodes, newNodes: newNodes }));
|
|
328
|
+
// and update the selected types in cache
|
|
329
|
+
const selectedTypes = new Set();
|
|
330
|
+
if (nodes === null || nodes === void 0 ? void 0 : nodes.find(n => isGroupNode(n)))
|
|
331
|
+
selectedTypes.add('group');
|
|
332
|
+
if (nodes === null || nodes === void 0 ? void 0 : nodes.find(n => !isGroupNode(n)))
|
|
333
|
+
selectedTypes.add('ruleset');
|
|
334
|
+
this._selectedTypes = selectedTypes;
|
|
335
|
+
return super._dispatchSelectEvent(nodes);
|
|
336
|
+
}
|
|
337
|
+
deselectAllNodes() {
|
|
338
|
+
super.deselectAllNodes();
|
|
339
|
+
this._selectedTypes = new Set();
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Gets the list of {@link RuleTreeNode[]} (tree nodes) based on a list of rulesets.
|
|
343
|
+
* It maps the ruleset to the correct tree node format. For example, when a groupId is set in the Ruleset meta,
|
|
344
|
+
* we group them together as children of a new group node.
|
|
345
|
+
* @param rules - List of rulesets
|
|
346
|
+
*/
|
|
347
|
+
_getRuleNodes(rules) {
|
|
348
|
+
const groupList = new Set(rules.map(r => { var _a; return (_a = r.meta) === null || _a === void 0 ? void 0 : _a.groupId; }).filter(x => x));
|
|
349
|
+
const groupedNodes = Array.from(groupList).map(group => ({
|
|
350
|
+
id: group,
|
|
351
|
+
label: group,
|
|
352
|
+
children: rules.filter(r => { var _a; return ((_a = r.meta) === null || _a === void 0 ? void 0 : _a.groupId) === group; }).map(rule => {
|
|
353
|
+
var _a;
|
|
354
|
+
return ({
|
|
355
|
+
id: (_a = rule.id) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
356
|
+
ruleset: rule,
|
|
357
|
+
label: rule.name
|
|
358
|
+
});
|
|
359
|
+
})
|
|
360
|
+
}));
|
|
361
|
+
const ungroupedRules = rules.filter(r => { var _a; return !((_a = r.meta) === null || _a === void 0 ? void 0 : _a.groupId); });
|
|
362
|
+
const ungroupedNodes = ungroupedRules.map(rule => {
|
|
363
|
+
var _a;
|
|
364
|
+
return ({
|
|
365
|
+
id: (_a = rule.id) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
366
|
+
ruleset: rule,
|
|
367
|
+
label: rule.name
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
return [...ungroupedNodes, ...groupedNodes];
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Utility function that returns a promise for loading the rulesets, based on the {@link global} and {@link realm} parameters
|
|
374
|
+
* @param global - Whether only global rulesets should be loaded instead.
|
|
375
|
+
* @param realm - Name of the realm to load (if global is `false` or `undefined`)
|
|
376
|
+
*/
|
|
377
|
+
_loadRulesets() {
|
|
378
|
+
return __awaiter(this, arguments, void 0, function* (global = false, realm = manager.displayRealm) {
|
|
379
|
+
if (global) {
|
|
380
|
+
const promise = manager.rest.api.RulesResource.getGlobalRulesets({ fullyPopulate: true });
|
|
381
|
+
return (yield promise).data;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
const promise = manager.rest.api.RulesResource.getRealmRulesets(realm, { fullyPopulate: true });
|
|
385
|
+
return (yield promise).data;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Refreshes the tree content, and re-fetches the list of rules.
|
|
391
|
+
*/
|
|
392
|
+
refresh() {
|
|
393
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
394
|
+
this._cachedEmptyGroupNodes = this.nodes.filter(n => !!n.children && n.children.length === 0);
|
|
395
|
+
yield this.getUpdateComplete();
|
|
396
|
+
const promise = this._loadRulesets(this.global);
|
|
397
|
+
promise.then(rulesets => {
|
|
398
|
+
this.rules = rulesets;
|
|
399
|
+
});
|
|
400
|
+
return promise;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Generates a HTML {@link TemplateResult} for the "add" button in the controls menu.
|
|
405
|
+
*/
|
|
406
|
+
_getAddActionTemplate() {
|
|
407
|
+
const menuOptions = (this._getAllowedLanguages() || []).map(l => ({ value: l, text: i18next.t("rulesLanguages." + l) }));
|
|
408
|
+
menuOptions.push(null);
|
|
409
|
+
menuOptions.push({ value: "group", text: i18next.t("group") });
|
|
410
|
+
const onValueChange = (value) => {
|
|
411
|
+
value === "group" ? this._onGroupAddClick() : this._onRulesetAddClick(value);
|
|
412
|
+
};
|
|
413
|
+
return getContentWithMenuTemplate(html `
|
|
414
|
+
<or-mwc-input type=${InputType.BUTTON} icon="plus" title="${i18next.t("addRule")}"></or-mwc-input>`, menuOptions, undefined, value => onValueChange(String(value)));
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* HTML callback function for clicking a ruleset language in the "add rule" menu.
|
|
418
|
+
* @param lang - The language to be used for adding a rule.
|
|
419
|
+
* @param global - Whether to create a global ruleset or not.
|
|
420
|
+
*/
|
|
421
|
+
_onRulesetAddClick(lang, global = this.global) {
|
|
422
|
+
const type = global ? "global" : "realm";
|
|
423
|
+
const realm = manager.isSuperUser() ? manager.displayRealm : manager.config.realm;
|
|
424
|
+
const ruleset = {
|
|
425
|
+
type: type,
|
|
426
|
+
name: OrRules.DEFAULT_RULESET_NAME,
|
|
427
|
+
lang: lang,
|
|
428
|
+
realm: realm,
|
|
429
|
+
rules: undefined
|
|
430
|
+
};
|
|
431
|
+
const selectedNodes = this._findSelectedTreeNodes();
|
|
432
|
+
if (selectedNodes.length === 1 && selectedNodes[0].children) {
|
|
433
|
+
const selectedGroupNode = selectedNodes[0];
|
|
434
|
+
const groupId = selectedGroupNode.id;
|
|
435
|
+
if (groupId) {
|
|
436
|
+
ruleset.meta = Object.assign(Object.assign({}, (ruleset.meta || {})), { groupId: groupId });
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (this.config && this.config.rulesetAddHandler && !this.config.rulesetAddHandler(ruleset)) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (this.config && this.config.rulesetTemplates && this.config.rulesetTemplates[lang]) {
|
|
443
|
+
ruleset.rules = this.config.rulesetTemplates[lang];
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
ruleset.rules = RuleViewInfoMap[lang].viewRulesetTemplate;
|
|
447
|
+
}
|
|
448
|
+
// Ensure config hasn't messed with certain values
|
|
449
|
+
if (type === "realm") {
|
|
450
|
+
ruleset.realm = realm;
|
|
451
|
+
}
|
|
452
|
+
const detail = {
|
|
453
|
+
ruleset: ruleset,
|
|
454
|
+
isCopy: false
|
|
455
|
+
};
|
|
456
|
+
Util.dispatchCancellableEvent(this, new OrRulesRequestAddEvent(detail))
|
|
457
|
+
.then((detail) => {
|
|
458
|
+
if (detail.allow) {
|
|
459
|
+
this.dispatchEvent(new OrRulesAddEvent(detail.detail));
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* HTML callback function for clicking "add group" in the controls menu.
|
|
465
|
+
*/
|
|
466
|
+
_onGroupAddClick() {
|
|
467
|
+
this.deselectAllNodes();
|
|
468
|
+
const groupName = i18next.t('ruleGroupNameDefault') || "Group name";
|
|
469
|
+
this.dispatchEvent(new OrRulesRequestGroupEvent(groupName));
|
|
470
|
+
}
|
|
471
|
+
_getSortFunction(sortBy) {
|
|
472
|
+
const sorting = sortBy;
|
|
473
|
+
switch (sorting) {
|
|
474
|
+
case RuleTreeSorting.LANGUAGE: {
|
|
475
|
+
return Util.sortByString(node => { var _a; return ((_a = node.ruleset) === null || _a === void 0 ? void 0 : _a.lang) || node.label; });
|
|
476
|
+
}
|
|
477
|
+
case RuleTreeSorting.CREATED_ON: {
|
|
478
|
+
return Util.sortByNumber(node => { var _a; return ((_a = node.ruleset) === null || _a === void 0 ? void 0 : _a.createdOn) || 0; });
|
|
479
|
+
}
|
|
480
|
+
case RuleTreeSorting.STATUS: {
|
|
481
|
+
return Util.sortByString(node => { var _a; return ((_a = node.ruleset) === null || _a === void 0 ? void 0 : _a.status) || node.label; });
|
|
482
|
+
}
|
|
483
|
+
default: {
|
|
484
|
+
return super._getSortFunction(sortBy);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Utility function for getting the list of allowed languages based on {@link config} and {@link global} parameters.
|
|
490
|
+
* @param config - Rules configuration to use
|
|
491
|
+
* @param global - Whether only to include global rulesets
|
|
492
|
+
*/
|
|
493
|
+
_getAllowedLanguages(config = this.config, global = this.global) {
|
|
494
|
+
var _a;
|
|
495
|
+
const languages = ((_a = config === null || config === void 0 ? void 0 : config.controls) === null || _a === void 0 ? void 0 : _a.allowedLanguages) ? [...config.controls.allowedLanguages] : OrRuleTree_1.DEFAULT_ALLOWED_LANGUAGES;
|
|
496
|
+
const groovyIndex = languages.indexOf("GROOVY" /* RulesetLang.GROOVY */);
|
|
497
|
+
const flowIndex = languages.indexOf("FLOW" /* RulesetLang.FLOW */);
|
|
498
|
+
if (!manager.isSuperUser()) {
|
|
499
|
+
if (groovyIndex > 0)
|
|
500
|
+
languages.splice(groovyIndex, 1);
|
|
501
|
+
}
|
|
502
|
+
else if (groovyIndex < 0) {
|
|
503
|
+
languages.push("GROOVY" /* RulesetLang.GROOVY */);
|
|
504
|
+
}
|
|
505
|
+
if (global) {
|
|
506
|
+
if (flowIndex > 0)
|
|
507
|
+
languages.splice(flowIndex, 1);
|
|
508
|
+
}
|
|
509
|
+
return languages;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Utility function that gets a Material Design icon based on the ruleset language
|
|
513
|
+
* @param lang - The ruleset language to get an icon of.
|
|
514
|
+
*/
|
|
515
|
+
_getRulesetLangIcon(lang) {
|
|
516
|
+
switch (lang) {
|
|
517
|
+
case ("JSON" /* RulesetLang.JSON */):
|
|
518
|
+
return "ray-start-arrow";
|
|
519
|
+
case ("FLOW" /* RulesetLang.FLOW */):
|
|
520
|
+
return "transit-connection-variant";
|
|
521
|
+
case ("GROOVY" /* RulesetLang.GROOVY */):
|
|
522
|
+
return "alpha-g-box-outline";
|
|
523
|
+
case ("JAVASCRIPT" /* RulesetLang.JAVASCRIPT */):
|
|
524
|
+
return "language-javascript";
|
|
525
|
+
default:
|
|
526
|
+
return "mdi-state-machine";
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Utility function that gets a Material Design icon based on the ruleset status
|
|
531
|
+
* @param status - The ruleset status to get an icon of.
|
|
532
|
+
*/
|
|
533
|
+
_getRulesetStatusIcon(status) {
|
|
534
|
+
switch (status) {
|
|
535
|
+
case "DEPLOYED" /* RulesetStatus.DEPLOYED */:
|
|
536
|
+
return;
|
|
537
|
+
case "READY" /* RulesetStatus.READY */:
|
|
538
|
+
return "check";
|
|
539
|
+
case "COMPILATION_ERROR" /* RulesetStatus.COMPILATION_ERROR */:
|
|
540
|
+
case "LOOP_ERROR" /* RulesetStatus.LOOP_ERROR */:
|
|
541
|
+
case "EXECUTION_ERROR" /* RulesetStatus.EXECUTION_ERROR */:
|
|
542
|
+
return "alert-octagon";
|
|
543
|
+
case "DISABLED" /* RulesetStatus.DISABLED */:
|
|
544
|
+
return "minus-circle";
|
|
545
|
+
case "PAUSED" /* RulesetStatus.PAUSED */:
|
|
546
|
+
return "calendar-arrow-right";
|
|
547
|
+
case "EXPIRED" /* RulesetStatus.EXPIRED */:
|
|
548
|
+
return "calendar-remove";
|
|
549
|
+
case "REMOVED" /* RulesetStatus.REMOVED */:
|
|
550
|
+
return "close";
|
|
551
|
+
default:
|
|
552
|
+
return "stop";
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Utility function that returns a CSS class for the icon element, based on the ruleset status
|
|
557
|
+
* @param status - The ruleset status to get a CSS class of.
|
|
558
|
+
*/
|
|
559
|
+
_getRulesetStatusColorClass(status) {
|
|
560
|
+
switch (status) {
|
|
561
|
+
case "DEPLOYED" /* RulesetStatus.DEPLOYED */:
|
|
562
|
+
case "READY" /* RulesetStatus.READY */:
|
|
563
|
+
return "iconfill-gray";
|
|
564
|
+
case "COMPILATION_ERROR" /* RulesetStatus.COMPILATION_ERROR */:
|
|
565
|
+
case "LOOP_ERROR" /* RulesetStatus.LOOP_ERROR */:
|
|
566
|
+
case "EXECUTION_ERROR" /* RulesetStatus.EXECUTION_ERROR */:
|
|
567
|
+
return "iconfill-red";
|
|
568
|
+
case "DISABLED" /* RulesetStatus.DISABLED */:
|
|
569
|
+
case "PAUSED" /* RulesetStatus.PAUSED */:
|
|
570
|
+
case "EXPIRED" /* RulesetStatus.EXPIRED */:
|
|
571
|
+
case "REMOVED" /* RulesetStatus.REMOVED */:
|
|
572
|
+
return "iconfill-gray";
|
|
573
|
+
default:
|
|
574
|
+
return "iconfill-gray";
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
OrRuleTree.DEFAULT_ALLOWED_LANGUAGES = ["JSON" /* RulesetLang.JSON */, "GROOVY" /* RulesetLang.GROOVY */, "JAVASCRIPT" /* RulesetLang.JAVASCRIPT */, "FLOW" /* RulesetLang.FLOW */];
|
|
579
|
+
__decorate([
|
|
580
|
+
property({ type: Array })
|
|
581
|
+
], OrRuleTree.prototype, "rules", void 0);
|
|
582
|
+
__decorate([
|
|
583
|
+
property({ type: Object })
|
|
584
|
+
], OrRuleTree.prototype, "config", void 0);
|
|
585
|
+
__decorate([
|
|
586
|
+
property({ type: Boolean })
|
|
587
|
+
], OrRuleTree.prototype, "global", void 0);
|
|
588
|
+
__decorate([
|
|
589
|
+
property({ type: Boolean })
|
|
590
|
+
], OrRuleTree.prototype, "readonly", void 0);
|
|
591
|
+
__decorate([
|
|
592
|
+
state()
|
|
593
|
+
], OrRuleTree.prototype, "_loadingPromises", void 0);
|
|
594
|
+
__decorate([
|
|
595
|
+
state()
|
|
596
|
+
], OrRuleTree.prototype, "_selectedTypes", void 0);
|
|
597
|
+
OrRuleTree = OrRuleTree_1 = __decorate([
|
|
598
|
+
customElement("or-rule-tree")
|
|
599
|
+
], OrRuleTree);
|
|
600
|
+
export { OrRuleTree };
|
|
601
|
+
//# sourceMappingURL=or-rule-tree.js.map
|