@openremote/or-rules 1.8.0-snapshot.20250725074716 → 1.8.0-snapshot.20250725120000
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/index.js
CHANGED
|
@@ -1,56 +1,953 @@
|
|
|
1
|
-
var __decorate
|
|
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
|
-
|
|
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
|
+
/*
|
|
17
|
+
* Copyright 2025, OpenRemote Inc.
|
|
18
|
+
*
|
|
19
|
+
* See the CONTRIBUTORS.txt file in the distribution for a
|
|
20
|
+
* full listing of individual contributors.
|
|
21
|
+
*
|
|
22
|
+
* This program is free software: you can redistribute it and/or modify
|
|
23
|
+
* it under the terms of the GNU Affero General Public License as
|
|
24
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
25
|
+
* License, or (at your option) any later version.
|
|
26
|
+
*
|
|
27
|
+
* This program is distributed in the hope that it will be useful,
|
|
28
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
29
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
30
|
+
* GNU Affero General Public License for more details.
|
|
31
|
+
*
|
|
32
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
33
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
34
|
+
*/
|
|
35
|
+
import { css, html, LitElement, unsafeCSS } from "lit";
|
|
36
|
+
import { customElement, property, query, state } from "lit/decorators.js";
|
|
37
|
+
import manager, { DefaultBoxShadow, DefaultColor1, DefaultColor2, DefaultColor3, DefaultColor4, DefaultColor5, DefaultColor6, Util } from "@openremote/core";
|
|
38
|
+
import i18next from "i18next";
|
|
39
|
+
import "@openremote/or-icon";
|
|
40
|
+
import { AssetModelUtil } from "@openremote/model";
|
|
41
|
+
import "@openremote/or-translate";
|
|
42
|
+
import "@openremote/or-mwc-components/or-mwc-drawer";
|
|
43
|
+
import { translate } from "@openremote/or-translate";
|
|
44
|
+
import "./or-rule-viewer";
|
|
45
|
+
import "./or-rule-group-viewer";
|
|
46
|
+
import "./or-rule-tree";
|
|
47
|
+
import "./flow-viewer/flow-viewer";
|
|
48
|
+
import { showOkCancelDialog } from "@openremote/or-mwc-components/or-mwc-dialog";
|
|
49
|
+
import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar";
|
|
50
|
+
import { OrTreeDragEvent } from "@openremote/or-tree-menu";
|
|
51
|
+
import { when } from "lit/directives/when.js";
|
|
52
|
+
export { buttonStyle } from "./style";
|
|
53
|
+
export var TimeTriggerType;
|
|
54
|
+
(function (TimeTriggerType) {
|
|
55
|
+
TimeTriggerType["TIME_OF_DAY"] = "TIME_OF_DAY";
|
|
56
|
+
})(TimeTriggerType || (TimeTriggerType = {}));
|
|
57
|
+
export var AssetQueryOperator;
|
|
58
|
+
(function (AssetQueryOperator) {
|
|
59
|
+
AssetQueryOperator["VALUE_EMPTY"] = "empty";
|
|
60
|
+
AssetQueryOperator["VALUE_NOT_EMPTY"] = "notEmpty";
|
|
61
|
+
AssetQueryOperator["EQUALS"] = "equals";
|
|
62
|
+
AssetQueryOperator["NOT_EQUALS"] = "notEquals";
|
|
63
|
+
AssetQueryOperator["GREATER_THAN"] = "greaterThan";
|
|
64
|
+
AssetQueryOperator["GREATER_EQUALS"] = "greaterEquals";
|
|
65
|
+
AssetQueryOperator["LESS_THAN"] = "lessThan";
|
|
66
|
+
AssetQueryOperator["LESS_EQUALS"] = "lessEquals";
|
|
67
|
+
AssetQueryOperator["BETWEEN"] = "between";
|
|
68
|
+
AssetQueryOperator["NOT_BETWEEN"] = "notBetween";
|
|
69
|
+
AssetQueryOperator["CONTAINS"] = "contains";
|
|
70
|
+
AssetQueryOperator["NOT_CONTAINS"] = "notContains";
|
|
71
|
+
AssetQueryOperator["STARTS_WITH"] = "startsWith";
|
|
72
|
+
AssetQueryOperator["NOT_STARTS_WITH"] = "notStartsWith";
|
|
73
|
+
AssetQueryOperator["ENDS_WITH"] = "endsWith";
|
|
74
|
+
AssetQueryOperator["NOT_ENDS_WITH"] = "notEndsWith";
|
|
75
|
+
AssetQueryOperator["CONTAINS_KEY"] = "containsKey";
|
|
76
|
+
AssetQueryOperator["NOT_CONTAINS_KEY"] = "notContainsKey";
|
|
77
|
+
AssetQueryOperator["INDEX_CONTAINS"] = "indexContains";
|
|
78
|
+
AssetQueryOperator["NOT_INDEX_CONTAINS"] = "notIndexContains";
|
|
79
|
+
AssetQueryOperator["LENGTH_EQUALS"] = "lengthEquals";
|
|
80
|
+
AssetQueryOperator["NOT_LENGTH_EQUALS"] = "notLengthEquals";
|
|
81
|
+
AssetQueryOperator["LENGTH_GREATER_THAN"] = "lengthGreaterThan";
|
|
82
|
+
AssetQueryOperator["LENGTH_LESS_THAN"] = "lengthLessThan";
|
|
83
|
+
AssetQueryOperator["IS_TRUE"] = "true";
|
|
84
|
+
AssetQueryOperator["IS_FALSE"] = "false";
|
|
85
|
+
AssetQueryOperator["WITHIN_RADIUS"] = "withinRadius";
|
|
86
|
+
AssetQueryOperator["OUTSIDE_RADIUS"] = "outsideRadius";
|
|
87
|
+
AssetQueryOperator["WITHIN_RECTANGLE"] = "withinRectangle";
|
|
88
|
+
AssetQueryOperator["OUTSIDE_RECTANGLE"] = "outsideRectangle";
|
|
89
|
+
AssetQueryOperator["NOT_UPDATED_FOR"] = "notUpdatedFor";
|
|
90
|
+
})(AssetQueryOperator || (AssetQueryOperator = {}));
|
|
91
|
+
export const RuleViewInfoMap = {
|
|
92
|
+
JSON: {
|
|
93
|
+
viewTemplateProvider: (ruleset, config, readonly) => html `<or-rule-json-viewer id="rule-view" .ruleset="${ruleset}" .config="${config}" .readonly="${readonly}"></or-rule-json-viewer>`,
|
|
94
|
+
},
|
|
95
|
+
FLOW: {
|
|
96
|
+
viewTemplateProvider: (ruleset, config, readonly) => html `<flow-editor id="rule-view" .ruleset="${ruleset}" .readonly="${readonly}"></flow-editor>`
|
|
97
|
+
},
|
|
98
|
+
GROOVY: {
|
|
99
|
+
viewTemplateProvider: (ruleset, config, readonly) => html `
|
|
100
|
+
<or-rule-text-viewer id="rule-view" .ruleset="${ruleset}" .config="${config}" .readonly="${readonly}"></or-rule-text-viewer>
|
|
101
|
+
`,
|
|
102
|
+
viewRulesetTemplate: "package demo.rules\n" +
|
|
103
|
+
"\n" +
|
|
104
|
+
"import org.openremote.manager.rules.RulesBuilder\n" +
|
|
105
|
+
"import org.openremote.model.notification.*\n" +
|
|
106
|
+
"import org.openremote.model.attribute.AttributeInfo\n" +
|
|
107
|
+
"import org.openremote.model.asset.Asset\n" +
|
|
108
|
+
"import org.openremote.model.asset.impl.*\n" +
|
|
109
|
+
"import org.openremote.model.query.*\n" +
|
|
110
|
+
"import org.openremote.model.query.filter.*\n" +
|
|
111
|
+
"import org.openremote.model.rules.Assets\n" +
|
|
112
|
+
"import org.openremote.model.rules.Notifications\n" +
|
|
113
|
+
"import org.openremote.model.rules.Users\n" +
|
|
114
|
+
"\n" +
|
|
115
|
+
"import java.util.logging.Logger\n" +
|
|
116
|
+
"import java.util.stream.Collectors\n" +
|
|
117
|
+
"\n" +
|
|
118
|
+
"Logger LOG = binding.LOG\n" +
|
|
119
|
+
"RulesBuilder rules = binding.rules\n" +
|
|
120
|
+
"Notifications notifications = binding.notifications\n" +
|
|
121
|
+
"Users users = binding.users\n" +
|
|
122
|
+
"Assets assets = binding.assets\n" +
|
|
123
|
+
"\n" +
|
|
124
|
+
"/*\n" +
|
|
125
|
+
"* A groovy rule is made up of a when closure (LHS) which must return a boolean indicating whether the then closure (RHS)\n" +
|
|
126
|
+
"* should be executed. The rule engine will periodically evaluate the when closure and if it evaluates to true then the\n" +
|
|
127
|
+
"* rule then closure will execute.\n" +
|
|
128
|
+
"*\n" +
|
|
129
|
+
"* NOTE: DO NOT MODIFY THE FACTS IN THE WHEN CLOSURE THIS SHOULD BE DONE IN THE THEN CLOSURE\n" +
|
|
130
|
+
"*\n" +
|
|
131
|
+
"* To avoid an infinite rule loop the when closure should not continually return true for subsequent executions\n" +
|
|
132
|
+
"* so either the then closure should perform an action that prevents the when closure from matching on subsequent\n" +
|
|
133
|
+
"* evaluations, or custom facts should be used, some ideas:\n" +
|
|
134
|
+
"*\n" +
|
|
135
|
+
"* - Change the value of an attribute being matched in the when closure (which will prevent it matching on subsequent evaluations)\n" +
|
|
136
|
+
"* - Insert a custom fact on first match and test this fact in the when closure to determine when the rule should match again (for\n" +
|
|
137
|
+
"* example if a rule should match whenever the asset state changes the asset state timestamp can be used)\n" +
|
|
138
|
+
"*/\n" +
|
|
139
|
+
"\n" +
|
|
140
|
+
"rules.add()\n" +
|
|
141
|
+
" .name(\"Example rule\")\n" +
|
|
142
|
+
" .when({\n" +
|
|
143
|
+
" facts ->\n" +
|
|
144
|
+
"\n" +
|
|
145
|
+
" // Find first matching asset state using an asset query\n" +
|
|
146
|
+
"\n" +
|
|
147
|
+
" facts.matchFirstAssetState(\n" +
|
|
148
|
+
"\n" +
|
|
149
|
+
" // Find asset state by asset type and attribute name\n" +
|
|
150
|
+
" new AssetQuery().types(ThingAsset).attributeNames(\"someAttribute\")\n" +
|
|
151
|
+
"\n" +
|
|
152
|
+
" // Find asset state by asset ID and attribute name\n" +
|
|
153
|
+
" //new AssetQuery().ids(\"7CaBoyiDhtdf2kn1Xso1w5\").attributeNames(\"someAttribute\")\n" +
|
|
154
|
+
"\n" +
|
|
155
|
+
" // Find asset state by asset type, attribute name and value string predicate\n" +
|
|
156
|
+
" //new AssetQuery().types(ThingAsset).attributes(\n" +
|
|
157
|
+
" // new AttributePredicate()\n" +
|
|
158
|
+
" // .name(\"someAttribute\")\n" +
|
|
159
|
+
" // .value(new StringPredicate()\n" +
|
|
160
|
+
" // .value(\"someValue\")\n" +
|
|
161
|
+
" // .match(AssetQuery.Match.EXACT)\n" +
|
|
162
|
+
" // .caseSensitive(true)))\n" +
|
|
163
|
+
"\n" +
|
|
164
|
+
" // Find asset state by asset type and location attribute predicate\n" +
|
|
165
|
+
" //new AssetQuery().types(ThingAsset).attributes(\n" +
|
|
166
|
+
" // new AttributePredicate()\n" +
|
|
167
|
+
" // .name(Asset.LOCATION)\n" +
|
|
168
|
+
" // .value(new RadialGeofencePredicate()\n" +
|
|
169
|
+
" // .radius(100)\n" +
|
|
170
|
+
" // .lat(50.0)\n" +
|
|
171
|
+
" // .lng(0.0)))\n" +
|
|
172
|
+
"\n" +
|
|
173
|
+
" ).map { assetState ->\n" +
|
|
174
|
+
"\n" +
|
|
175
|
+
" // Use logging to help with debugging if needed" +
|
|
176
|
+
" //LOG.info(\"ATTRIBUTE FOUND\")\n" +
|
|
177
|
+
"\n" +
|
|
178
|
+
" // Check if this rule really should fire this time\n" +
|
|
179
|
+
" Optional<Long> lastFireTimestamp = facts.getOptional(\"someAttribute\")\n" +
|
|
180
|
+
" if (lastFireTimestamp.isPresent() && assetState.getTimestamp() <= lastFireTimestamp.get()) {\n" +
|
|
181
|
+
" return false\n" +
|
|
182
|
+
" }\n" +
|
|
183
|
+
"\n" +
|
|
184
|
+
" // OK to fire if we reach here\n" +
|
|
185
|
+
"\n" +
|
|
186
|
+
" // Compute and bind any facts required for the then closure\n" +
|
|
187
|
+
" facts.bind(\"assetState\", assetState)\n" +
|
|
188
|
+
" true\n" +
|
|
189
|
+
" }.orElseGet {\n" +
|
|
190
|
+
" // Asset state didn't match so clear out any custom facts to allow the rule to fire next time the when closure matches\n" +
|
|
191
|
+
" facts.remove(\"someAttribute\")\n" +
|
|
192
|
+
" false\n" +
|
|
193
|
+
" }\n" +
|
|
194
|
+
"\n" +
|
|
195
|
+
"})\n" +
|
|
196
|
+
" .then({\n" +
|
|
197
|
+
" facts ->\n" +
|
|
198
|
+
"\n" +
|
|
199
|
+
" // Extract any binded facts\n" +
|
|
200
|
+
" AttributeInfo assetState = facts.bound(\"assetState\")\n" +
|
|
201
|
+
"\n" +
|
|
202
|
+
" // Insert the custom fact to prevent rule loop\n" +
|
|
203
|
+
" facts.put(\"someAttribute\", assetState.getTimestamp())\n" +
|
|
204
|
+
"\n" +
|
|
205
|
+
" // Write to attributes\n" +
|
|
206
|
+
" def otherAttributeValue = null\n" +
|
|
207
|
+
" if (assetState.getValue().orElse{null} == \"Value 1\") {\n" +
|
|
208
|
+
" otherAttributeValue = \"Converted Value 1\"\n" +
|
|
209
|
+
" } else if (assetState.getValue().orElse{null} == \"Value 2\") {\n" +
|
|
210
|
+
" otherAttributeValue = \"Converted Value 2\"\n" +
|
|
211
|
+
" } else {\n" +
|
|
212
|
+
" otherAttributeValue = \"Unknown\"\n" +
|
|
213
|
+
" }\n" +
|
|
214
|
+
" assets.dispatch(assetState.id, \"otherAttribute\", otherAttributeValue)\n" +
|
|
215
|
+
"\n" +
|
|
216
|
+
" // Generate notifications (useful for rules that check if an attribute is out of range)\n" +
|
|
217
|
+
" //notifications.send(new Notification()\n" +
|
|
218
|
+
" // .setName(\"Attribute alert\")\n" +
|
|
219
|
+
" // .setMessage(new EmailNotificationMessage()\n" +
|
|
220
|
+
" // .setTo(\"no-reply@openremote.io\")\n" +
|
|
221
|
+
" // .setSubject(\"Attribute out of range: Attribute=${assetState.name} Asset ID=${assetState.id}\")\n" +
|
|
222
|
+
" // .setText(\"Some text body\")\n" +
|
|
223
|
+
" // .setHtml(\"<p>Or some HTML body</p>\")\n" +
|
|
224
|
+
" // )\n" +
|
|
225
|
+
" //)\n" +
|
|
226
|
+
"})"
|
|
227
|
+
},
|
|
228
|
+
JAVASCRIPT: {
|
|
229
|
+
viewTemplateProvider: (ruleset, config, readonly) => html `
|
|
230
|
+
<or-rule-text-viewer id="rule-view" .ruleset="${ruleset}" .config="${config}" .readonly="${readonly}"></or-rule-text-viewer>
|
|
231
|
+
`,
|
|
232
|
+
viewRulesetTemplate: "rules = [ // An array of rules, add more objects to add more rules\n" +
|
|
233
|
+
" {\n" +
|
|
234
|
+
" name: \"Set bar to foo on someAttribute\",\n" +
|
|
235
|
+
" description: \"An example rule that sets 'bar' on someAttribute when it is 'foo'\",\n" +
|
|
236
|
+
" when: function(facts) {\n" +
|
|
237
|
+
" return facts.matchAssetState(\n" +
|
|
238
|
+
" new AssetQuery().types(AssetType.THING).attributeValue(\"someAttribute\", \"foo\")\n" +
|
|
239
|
+
" ).map(function(thing) {\n" +
|
|
240
|
+
" facts.bind(\"assetId\", thing.id);\n" +
|
|
241
|
+
" return true;\n" +
|
|
242
|
+
" }).orElse(false);\n" +
|
|
243
|
+
" },\n" +
|
|
244
|
+
" then: function(facts) {\n" +
|
|
245
|
+
" facts.updateAssetState(\n" +
|
|
246
|
+
" facts.bound(\"assetId\"), \"someAttribute\", \"bar\"\n" +
|
|
247
|
+
" );\n" +
|
|
248
|
+
" }\n" +
|
|
249
|
+
" }\n" +
|
|
250
|
+
"]"
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
function getAssetDescriptorFromSection(assetType, config, useActionConfig) {
|
|
254
|
+
if (!config || !config.descriptors) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const section = useActionConfig ? config.descriptors.action : config.descriptors.when;
|
|
258
|
+
const allSection = config.descriptors.all;
|
|
259
|
+
const descriptor = section && section.assets ? section.assets[assetType] ? section.assets[assetType] : section.assets["*"] : undefined;
|
|
260
|
+
if (descriptor) {
|
|
261
|
+
return descriptor;
|
|
262
|
+
}
|
|
263
|
+
return allSection && allSection.assets ? allSection.assets[assetType] ? allSection.assets[assetType] : allSection.assets["*"] : undefined;
|
|
264
|
+
}
|
|
265
|
+
export function getAssetTypeFromQuery(query) {
|
|
266
|
+
return query && query.types && query.types.length > 0 && query.types[0] ? query.types[0] : undefined;
|
|
267
|
+
}
|
|
268
|
+
export function getAssetIdsFromQuery(query) {
|
|
269
|
+
return query && query.ids ? [...query.ids] : undefined;
|
|
270
|
+
}
|
|
271
|
+
export const getAssetTypes = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
272
|
+
// RT: Change to just get all asset types for now as if an instance of a particular asset doesn't exist you
|
|
273
|
+
// won't be able to create a rule for it (e.g. if no ConsoleAsset in a realm then cannot create a console rule)
|
|
274
|
+
return AssetModelUtil.getAssetTypeInfos().map(ati => ati.assetDescriptor.name);
|
|
275
|
+
// const response = await manager.rest.api.AssetResource.queryAssets({
|
|
276
|
+
// select: {
|
|
277
|
+
// attributes: []
|
|
278
|
+
// },
|
|
279
|
+
// realm: {
|
|
280
|
+
// realm: manager.displayRealm
|
|
281
|
+
// },
|
|
282
|
+
// recursive: true
|
|
283
|
+
// });
|
|
284
|
+
//
|
|
285
|
+
// if (response && response.data) {
|
|
286
|
+
// return response.data.map(asset => asset.type!);
|
|
287
|
+
// }
|
|
288
|
+
});
|
|
289
|
+
export function getAssetInfos(config, useActionConfig) {
|
|
290
|
+
const assetDescriptors = AssetModelUtil.getAssetDescriptors();
|
|
291
|
+
return getAssetTypes().then(availibleAssetTypes => {
|
|
292
|
+
let allowedAssetTypes = availibleAssetTypes ? availibleAssetTypes : [];
|
|
293
|
+
let excludedAssetTypes = [];
|
|
294
|
+
if (!config || !config.descriptors) {
|
|
295
|
+
return assetDescriptors.map((ad) => AssetModelUtil.getAssetTypeInfo(ad));
|
|
296
|
+
}
|
|
297
|
+
const section = useActionConfig ? config.descriptors.action : config.descriptors.when;
|
|
298
|
+
if ((section && section.includeAssets) || (config.descriptors.all && config.descriptors.all.includeAssets)) {
|
|
299
|
+
allowedAssetTypes = [];
|
|
300
|
+
if (section && section.includeAssets) {
|
|
301
|
+
allowedAssetTypes = [...section.includeAssets];
|
|
302
|
+
}
|
|
303
|
+
if (config.descriptors.all && config.descriptors.all.includeAssets) {
|
|
304
|
+
allowedAssetTypes = [...config.descriptors.all.includeAssets];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (section && section.excludeAssets) {
|
|
308
|
+
excludedAssetTypes = [...section.excludeAssets];
|
|
309
|
+
}
|
|
310
|
+
if (config.descriptors.all && config.descriptors.all.excludeAssets) {
|
|
311
|
+
excludedAssetTypes = excludedAssetTypes.concat(config.descriptors.all.excludeAssets);
|
|
312
|
+
}
|
|
313
|
+
return assetDescriptors.filter((ad) => {
|
|
314
|
+
if (allowedAssetTypes.length > 0 && allowedAssetTypes.indexOf(ad.name) < 0) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
return excludedAssetTypes.indexOf(ad.name) < 0;
|
|
318
|
+
}).map((ad) => {
|
|
319
|
+
let typeInfo = AssetModelUtil.getAssetTypeInfo(ad);
|
|
320
|
+
// Amalgamate matching descriptor from config if defined
|
|
321
|
+
const configDescriptor = getAssetDescriptorFromSection(ad.name, config, useActionConfig);
|
|
322
|
+
if (!configDescriptor) {
|
|
323
|
+
return typeInfo;
|
|
324
|
+
}
|
|
325
|
+
const modifiedTypeInfo = {
|
|
326
|
+
assetDescriptor: typeInfo.assetDescriptor ? Object.assign({}, typeInfo.assetDescriptor) : { descriptorType: "asset" },
|
|
327
|
+
attributeDescriptors: typeInfo.attributeDescriptors ? [...typeInfo.attributeDescriptors] : [],
|
|
328
|
+
metaItemDescriptors: typeInfo.metaItemDescriptors ? [...typeInfo.metaItemDescriptors] : [],
|
|
329
|
+
valueDescriptors: typeInfo.valueDescriptors ? [...typeInfo.valueDescriptors] : []
|
|
330
|
+
};
|
|
331
|
+
if (configDescriptor.icon) {
|
|
332
|
+
modifiedTypeInfo.assetDescriptor.icon = configDescriptor.icon;
|
|
333
|
+
}
|
|
334
|
+
if (configDescriptor.color) {
|
|
335
|
+
modifiedTypeInfo.assetDescriptor.colour = configDescriptor.color;
|
|
336
|
+
}
|
|
337
|
+
// Remove any excluded attributes
|
|
338
|
+
if (modifiedTypeInfo.attributeDescriptors) {
|
|
339
|
+
const includedAttributes = configDescriptor.includeAttributes !== undefined ? configDescriptor.includeAttributes : undefined;
|
|
340
|
+
const excludedAttributes = configDescriptor.excludeAttributes !== undefined ? configDescriptor.excludeAttributes : undefined;
|
|
341
|
+
if (includedAttributes || excludedAttributes) {
|
|
342
|
+
modifiedTypeInfo.attributeDescriptors = modifiedTypeInfo.attributeDescriptors.filter((mad) => (!includedAttributes || includedAttributes.some((inc) => Util.stringMatch(inc, mad.name)))
|
|
343
|
+
&& (!excludedAttributes || !excludedAttributes.some((exc) => Util.stringMatch(exc, mad.name))));
|
|
344
|
+
}
|
|
345
|
+
// Override any attribute descriptors
|
|
346
|
+
if (configDescriptor.attributeDescriptors) {
|
|
347
|
+
modifiedTypeInfo.attributeDescriptors.map((attributeDescriptor) => {
|
|
348
|
+
let configAttributeDescriptor = configDescriptor.attributeDescriptors[attributeDescriptor.name];
|
|
349
|
+
if (!configAttributeDescriptor) {
|
|
350
|
+
configAttributeDescriptor = section && section.attributeDescriptors ? section.attributeDescriptors[attributeDescriptor.name] : undefined;
|
|
351
|
+
}
|
|
352
|
+
if (!configAttributeDescriptor) {
|
|
353
|
+
configAttributeDescriptor = config.descriptors.all && config.descriptors.all.attributeDescriptors ? config.descriptors.all.attributeDescriptors[attributeDescriptor.name] : undefined;
|
|
354
|
+
}
|
|
355
|
+
if (configAttributeDescriptor) {
|
|
356
|
+
if (configAttributeDescriptor.type) {
|
|
357
|
+
attributeDescriptor.type = configAttributeDescriptor.type;
|
|
358
|
+
}
|
|
359
|
+
if (configAttributeDescriptor.format) {
|
|
360
|
+
attributeDescriptor.format = configAttributeDescriptor.format;
|
|
361
|
+
}
|
|
362
|
+
if (configAttributeDescriptor.units) {
|
|
363
|
+
attributeDescriptor.units = configAttributeDescriptor.units;
|
|
364
|
+
}
|
|
365
|
+
if (configAttributeDescriptor.constraints) {
|
|
366
|
+
attributeDescriptor.constraints = attributeDescriptor.constraints ? [...configAttributeDescriptor.constraints, ...attributeDescriptor.constraints] : configAttributeDescriptor.constraints;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return modifiedTypeInfo;
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// Function for getting assets by type
|
|
377
|
+
// loadedAssets is an object given as parameter that will be updated if new assets are fetched.
|
|
378
|
+
export function getAssetsByType(type, realm, loadedAssets) {
|
|
379
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
380
|
+
if (loadedAssets === null || loadedAssets === void 0 ? void 0 : loadedAssets.has(type)) {
|
|
381
|
+
return {
|
|
382
|
+
assets: loadedAssets === null || loadedAssets === void 0 ? void 0 : loadedAssets.get(type),
|
|
383
|
+
loadedAssets: loadedAssets
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
if (!loadedAssets) {
|
|
388
|
+
loadedAssets = new Map();
|
|
389
|
+
}
|
|
390
|
+
const assetQuery = {
|
|
391
|
+
types: [type],
|
|
392
|
+
orderBy: {
|
|
393
|
+
property: "NAME" /* AssetQueryOrderBy$Property.NAME */
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
if (realm != undefined) {
|
|
397
|
+
assetQuery.realm = { name: realm };
|
|
398
|
+
}
|
|
399
|
+
const response = yield manager.rest.api.AssetResource.queryAssets(assetQuery);
|
|
400
|
+
loadedAssets.set(type, response.data);
|
|
401
|
+
return {
|
|
402
|
+
assets: response.data,
|
|
403
|
+
loadedAssets: loadedAssets
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
export class OrRulesRuleChangedEvent extends CustomEvent {
|
|
409
|
+
constructor(valid) {
|
|
410
|
+
super(OrRulesRuleChangedEvent.NAME, {
|
|
411
|
+
bubbles: true,
|
|
412
|
+
composed: true,
|
|
413
|
+
detail: valid
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
OrRulesRuleChangedEvent.NAME = "or-rules-rule-changed";
|
|
418
|
+
export class OrRulesRuleUnsupportedEvent extends CustomEvent {
|
|
419
|
+
constructor() {
|
|
420
|
+
super(OrRulesRuleUnsupportedEvent.NAME, {
|
|
421
|
+
bubbles: true,
|
|
422
|
+
composed: true
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
OrRulesRuleUnsupportedEvent.NAME = "or-rules-rule-unsupported";
|
|
427
|
+
export class OrRulesRequestSelectionEvent extends CustomEvent {
|
|
428
|
+
constructor(request) {
|
|
429
|
+
super(OrRulesRequestSelectionEvent.NAME, {
|
|
430
|
+
bubbles: true,
|
|
431
|
+
composed: true,
|
|
432
|
+
cancelable: true,
|
|
433
|
+
detail: request
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
OrRulesRequestSelectionEvent.NAME = "or-rules-request-selection";
|
|
438
|
+
export class OrRulesSelectionEvent extends CustomEvent {
|
|
439
|
+
constructor(nodes) {
|
|
440
|
+
super(OrRulesSelectionEvent.NAME, {
|
|
441
|
+
bubbles: true,
|
|
442
|
+
composed: true,
|
|
443
|
+
detail: nodes
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
OrRulesSelectionEvent.NAME = "or-rules-selection";
|
|
448
|
+
export class OrRulesRequestAddEvent extends CustomEvent {
|
|
449
|
+
constructor(detail) {
|
|
450
|
+
super(OrRulesRequestAddEvent.NAME, {
|
|
451
|
+
bubbles: true,
|
|
452
|
+
composed: true,
|
|
453
|
+
detail: {
|
|
454
|
+
detail: detail,
|
|
455
|
+
allow: true
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
OrRulesRequestAddEvent.NAME = "or-rules-request-add";
|
|
461
|
+
export class OrRulesRequestGroupEvent extends CustomEvent {
|
|
462
|
+
constructor(name) {
|
|
463
|
+
super(OrRulesRequestGroupEvent.NAME, {
|
|
464
|
+
bubbles: true,
|
|
465
|
+
composed: true,
|
|
466
|
+
detail: {
|
|
467
|
+
value: name,
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
OrRulesRequestGroupEvent.NAME = "or-rules-request-group";
|
|
473
|
+
export class OrRulesAddEvent extends CustomEvent {
|
|
474
|
+
constructor(detail) {
|
|
475
|
+
super(OrRulesAddEvent.NAME, {
|
|
476
|
+
bubbles: true,
|
|
477
|
+
composed: true,
|
|
478
|
+
detail: detail
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
OrRulesAddEvent.NAME = "or-rules-add";
|
|
483
|
+
export class OrRulesRequestDeleteEvent extends CustomEvent {
|
|
484
|
+
constructor(request) {
|
|
485
|
+
super(OrRulesRequestDeleteEvent.NAME, {
|
|
486
|
+
bubbles: true,
|
|
487
|
+
composed: true,
|
|
488
|
+
detail: {
|
|
489
|
+
detail: request,
|
|
490
|
+
allow: true
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
OrRulesRequestDeleteEvent.NAME = "or-rules-request-delete";
|
|
496
|
+
export class OrRulesRequestSaveEvent extends CustomEvent {
|
|
497
|
+
constructor(ruleset) {
|
|
498
|
+
super(OrRulesRequestSaveEvent.NAME, {
|
|
499
|
+
bubbles: true,
|
|
500
|
+
composed: true,
|
|
501
|
+
detail: {
|
|
502
|
+
allow: true,
|
|
503
|
+
detail: ruleset
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
OrRulesRequestSaveEvent.NAME = "or-rules-request-save";
|
|
509
|
+
export class OrRulesSaveEvent extends CustomEvent {
|
|
510
|
+
constructor(result) {
|
|
511
|
+
super(OrRulesSaveEvent.NAME, {
|
|
512
|
+
bubbles: true,
|
|
513
|
+
composed: true,
|
|
514
|
+
detail: result
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
OrRulesSaveEvent.NAME = "or-rules-save";
|
|
519
|
+
export class OrRulesDeleteEvent extends CustomEvent {
|
|
520
|
+
constructor(rulesets) {
|
|
521
|
+
super(OrRulesDeleteEvent.NAME, {
|
|
522
|
+
bubbles: true,
|
|
523
|
+
composed: true,
|
|
524
|
+
detail: rulesets
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
OrRulesDeleteEvent.NAME = "or-rules-delete";
|
|
529
|
+
export class OrRulesGroupNameChangeEvent extends CustomEvent {
|
|
530
|
+
constructor(name) {
|
|
531
|
+
super(OrRulesGroupNameChangeEvent.NAME, {
|
|
532
|
+
bubbles: true,
|
|
533
|
+
composed: true,
|
|
534
|
+
cancelable: true,
|
|
535
|
+
detail: {
|
|
536
|
+
value: name
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
OrRulesGroupNameChangeEvent.NAME = "or-rules-group-name-change";
|
|
542
|
+
// language=CSS
|
|
543
|
+
export const style = css `
|
|
544
|
+
|
|
545
|
+
:host {
|
|
546
|
+
display: flex;
|
|
547
|
+
height: 100%;
|
|
548
|
+
width: 100%;
|
|
549
|
+
|
|
550
|
+
--internal-or-rules-background-color: var(--or-rules-background-color, var(--or-app-color2, ${unsafeCSS(DefaultColor2)}));
|
|
551
|
+
--internal-or-rules-text-color: var(--or-rules-text-color, inherit);
|
|
552
|
+
--internal-or-rules-button-color: var(--or-rules-button-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)}));
|
|
553
|
+
--internal-or-rules-invalid-color: var(--or-rules-invalid-color, var(--or-app-color6, ${unsafeCSS(DefaultColor6)}));
|
|
554
|
+
--internal-or-rules-panel-color: var(--or-rules-panel-color, var(--or-app-color1, ${unsafeCSS(DefaultColor1)}));
|
|
555
|
+
--internal-or-rules-line-color: var(--or-rules-line-color, var(--or-app-color5, ${unsafeCSS(DefaultColor5)}));
|
|
556
|
+
|
|
557
|
+
--internal-or-rules-list-selected-color: var(--or-rules-list-selected-color, var(--or-app-color2, ${unsafeCSS(DefaultColor2)}));
|
|
558
|
+
--internal-or-rules-list-text-color: var(--or-rules-list-text-color, var(--or-app-color3, ${unsafeCSS(DefaultColor3)}));
|
|
559
|
+
--internal-or-rules-list-text-size: var(--or-rules-list-text-size, 15px);
|
|
560
|
+
--internal-or-rules-list-header-height: var(--or-rules-list-header-height, 48px);
|
|
561
|
+
|
|
562
|
+
--internal-or-rules-list-icon-color-error: var(--or-rules-list-icon-color-error, var(--or-app-color6, ${unsafeCSS(DefaultColor6)}));
|
|
563
|
+
--internal-or-rules-list-icon-color-ok: var(--or-rules-list-icon-color-ok, var(--or-app-color5, ${unsafeCSS(DefaultColor5)}));
|
|
564
|
+
|
|
565
|
+
--internal-or-rules-list-button-size: var(--or-rules-list-button-size, 24px);
|
|
566
|
+
|
|
567
|
+
--internal-or-rules-header-background-color: var(--or-rules-header-background-color, var(--or-app-color3, ${unsafeCSS(DefaultColor3)}));
|
|
568
|
+
--internal-or-rules-header-height: var(--or-rules-header-height, unset);
|
|
569
|
+
|
|
570
|
+
--or-panel-background-color: var(--internal-or-rules-panel-color);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
or-rule-tree {
|
|
574
|
+
min-width: 300px;
|
|
575
|
+
width: 300px;
|
|
576
|
+
z-index: 2;
|
|
577
|
+
display: flex;
|
|
578
|
+
flex-direction: column;
|
|
579
|
+
background-color: var(--internal-or-rules-panel-color);
|
|
580
|
+
color: var(--internal-or-rules-list-text-color);
|
|
581
|
+
box-shadow: ${unsafeCSS(DefaultBoxShadow)};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
or-rule-viewer, or-rule-group-viewer {
|
|
585
|
+
z-index: 1;
|
|
586
|
+
}
|
|
587
|
+
`;
|
|
588
|
+
let OrRules = class OrRules extends translate(i18next)(LitElement) {
|
|
589
|
+
static get styles() {
|
|
590
|
+
return [
|
|
591
|
+
style
|
|
592
|
+
];
|
|
593
|
+
}
|
|
594
|
+
constructor() {
|
|
595
|
+
super();
|
|
596
|
+
this.addEventListener(OrRulesRequestSelectionEvent.NAME, this._onRuleSelectionRequested);
|
|
597
|
+
this.addEventListener(OrRulesSelectionEvent.NAME, this._onRuleSelectionChanged);
|
|
598
|
+
this.addEventListener(OrRulesAddEvent.NAME, this._onRuleAdd);
|
|
599
|
+
this.addEventListener(OrRulesSaveEvent.NAME, this._onRuleSave);
|
|
600
|
+
this.addEventListener(OrTreeDragEvent.NAME, this._onRuleDrag);
|
|
601
|
+
this.addEventListener(OrRulesRequestGroupEvent.NAME, this._onGroupAddRequest);
|
|
602
|
+
this.addEventListener(OrRulesGroupNameChangeEvent.NAME, this._onGroupNameChange);
|
|
603
|
+
}
|
|
604
|
+
render() {
|
|
605
|
+
return html `
|
|
606
|
+
<or-rule-tree id="rule-tree" ?readonly=${this._isReadonly()} .config=${this.config}></or-rule-tree>
|
|
607
|
+
${when(this._selectedGroup, () => html `
|
|
608
|
+
<or-rule-group-viewer .group="${this._selectedGroup}" .readonly="${this._isReadonly()}"></or-rule-group-viewer>
|
|
609
|
+
`, () => html `
|
|
610
|
+
<or-rule-viewer id="rule-viewer" .config="${this.config}" .readonly="${this._isReadonly()}"></or-rule-viewer>
|
|
611
|
+
`)}
|
|
612
|
+
`;
|
|
613
|
+
}
|
|
614
|
+
refresh() {
|
|
615
|
+
var _a;
|
|
616
|
+
console.debug("Refreshing the rules content...");
|
|
617
|
+
if (this._viewer) {
|
|
618
|
+
this._viewer.ruleset = undefined;
|
|
619
|
+
}
|
|
620
|
+
(_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.refresh();
|
|
621
|
+
}
|
|
622
|
+
_isReadonly() {
|
|
623
|
+
return this.readonly || !manager.hasRole("write:rules" /* ClientRole.WRITE_RULES */);
|
|
624
|
+
}
|
|
625
|
+
_confirmContinue(action) {
|
|
626
|
+
var _a;
|
|
627
|
+
if ((_a = this._viewer) === null || _a === void 0 ? void 0 : _a.modified) {
|
|
628
|
+
showOkCancelDialog(i18next.t("loseChanges"), i18next.t("confirmContinueRulesetModified"), i18next.t("discard"))
|
|
629
|
+
.then((ok) => action(ok));
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
action(true);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* HTML event callback on when a "rule (or group) selection is requested".
|
|
637
|
+
* It is a cancellable event that can prompt a user "to discard changes" if they have been made.
|
|
638
|
+
* @param event - The cancellable selection event
|
|
639
|
+
*/
|
|
640
|
+
_onRuleSelectionRequested(event) {
|
|
641
|
+
var _a;
|
|
642
|
+
const isModified = (_a = this._viewer) === null || _a === void 0 ? void 0 : _a.modified;
|
|
643
|
+
if (!isModified) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
// Prevent the initial request
|
|
647
|
+
event.preventDefault();
|
|
648
|
+
console.debug("Prevented new rule selection; prompting user changes have been made.");
|
|
649
|
+
// Prompt user to "discard changes", and continue the selection afterwards
|
|
650
|
+
this._confirmContinue((ok) => {
|
|
651
|
+
if (ok) {
|
|
652
|
+
this._onNodeSelectionChanged(event.detail);
|
|
653
|
+
event.detail.newNodes.filter(n => n.type === "rule")
|
|
654
|
+
.forEach(n => { var _a; return (_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.selectRuleset(n.ruleset, true); });
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
event.detail.oldNodes.filter(n => n.type === "rule")
|
|
658
|
+
.forEach(n => { var _a; return (_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.selectRuleset(n.ruleset, true); });
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* HTML event callback on when a new rule (or group) has been selected.
|
|
664
|
+
* @param event - The selection event
|
|
665
|
+
*/
|
|
666
|
+
_onRuleSelectionChanged(event) {
|
|
667
|
+
this._onNodeSelectionChanged(event.detail);
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Utility event callback function called by {@link _onRuleSelectionRequested} and {@link _onRuleSelectionChanged},
|
|
671
|
+
* when a new node in the {@link OrRuleTree} element is selected. It processes the event, and updates the {@link OrRuleViewer} with the new rule.
|
|
672
|
+
* Alternatively, if a group is selected, it updates {@link _selectedGroup} instead.
|
|
673
|
+
* @param payload - Event payload of the {@link RulesetBaseNode} selection
|
|
674
|
+
*/
|
|
675
|
+
_onNodeSelectionChanged(payload) {
|
|
676
|
+
var _a;
|
|
677
|
+
const selectedNodes = payload.newNodes;
|
|
678
|
+
const groupNodes = selectedNodes.filter(n => n.type === "group"); // list of selected group nodes
|
|
679
|
+
// If any group has been selected
|
|
680
|
+
if (groupNodes.length === 1) {
|
|
681
|
+
if (this._viewer)
|
|
682
|
+
this._viewer.ruleset = undefined; // clear viewer, since a group is selected instead
|
|
683
|
+
this._selectedGroup = groupNodes[0].groupId;
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
// If the user has clicked the same node so let's force reload it
|
|
687
|
+
else if (Util.objectsEqual(selectedNodes, payload.oldNodes)) {
|
|
688
|
+
console.debug("Force reloading rule viewer...");
|
|
689
|
+
const node = (_a = selectedNodes[0]) === null || _a === void 0 ? void 0 : _a.ruleset;
|
|
690
|
+
this._viewer.ruleset = node ? Object.assign({}, node.ruleset) : undefined;
|
|
691
|
+
// Otherwise, select the new rule or group.
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
const groupNodes = selectedNodes.filter(n => n.type === "group"); // list of selected group nodes
|
|
695
|
+
const rulesetNodes = selectedNodes.filter(n => n.type === "rule"); // list of selected rule nodes
|
|
696
|
+
const selectedIds = rulesetNodes.map((node) => node.ruleset.id);
|
|
697
|
+
console.debug(`Selecting rule IDs ${selectedIds}`);
|
|
698
|
+
// Deselect the group, and select either a new rule or group after that.
|
|
699
|
+
this._deselectGroup().then(() => {
|
|
700
|
+
if (rulesetNodes.length === 1) {
|
|
701
|
+
this.getUpdateComplete().then(() => {
|
|
702
|
+
console.debug("Ruleset viewer is now showing", rulesetNodes[0].ruleset.name);
|
|
703
|
+
this._viewer.ruleset = Object.assign({}, rulesetNodes[0].ruleset);
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
this._viewer.ruleset = undefined; // clear viewer, since a group is selected instead
|
|
708
|
+
if (groupNodes.length === 1) {
|
|
709
|
+
this._selectedGroup = groupNodes[0].groupId;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* HTML callback event for when a new rule is added.
|
|
717
|
+
* @param event - A {@link CustomEvent} with the new ruleset as payload.
|
|
718
|
+
*/
|
|
719
|
+
_onRuleAdd(event) {
|
|
720
|
+
// Load the ruleset into the viewer
|
|
721
|
+
this._deselectGroup().then(() => {
|
|
722
|
+
this._viewer.ruleset = event.detail.ruleset;
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* HTML callback event for when a rule has been saved by the user.
|
|
727
|
+
* @param event - A {@link CustomEvent} with a {@link SaveResult} payload.
|
|
728
|
+
*/
|
|
729
|
+
_onRuleSave(event) {
|
|
730
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
731
|
+
var _a, _b, _c, _d;
|
|
732
|
+
if (event.detail.success) {
|
|
733
|
+
// Reset the modified state, which disables the "save" button
|
|
734
|
+
if (this._viewer)
|
|
735
|
+
this._viewer.modified = false;
|
|
736
|
+
// Fetch the updated rules, and change the viewer to the latest version
|
|
737
|
+
const newRulesets = yield ((_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.refresh());
|
|
738
|
+
this._checkForViewerUpdate(undefined, newRulesets);
|
|
739
|
+
// After the tree has refreshed, check if the saved rule belongs to a group.
|
|
740
|
+
const savedRuleset = event.detail.ruleset;
|
|
741
|
+
const groupId = (_b = savedRuleset.meta) === null || _b === void 0 ? void 0 : _b.groupId;
|
|
742
|
+
// If it has a group ID, expand tree component to show that group and rules that are inside.
|
|
743
|
+
if (groupId && this._rulesTree) {
|
|
744
|
+
this._rulesTree.expandGroup(groupId);
|
|
745
|
+
}
|
|
746
|
+
// Select the ruleset if it's new
|
|
747
|
+
if (event.detail.isNew) {
|
|
748
|
+
const ruleset = newRulesets === null || newRulesets === void 0 ? void 0 : newRulesets.find(r => r.id && r.id === event.detail.ruleset.id);
|
|
749
|
+
if (ruleset) {
|
|
750
|
+
(_c = this._rulesTree) === null || _c === void 0 ? void 0 : _c.deselectAllNodes();
|
|
751
|
+
(_d = this._rulesTree) === null || _d === void 0 ? void 0 : _d.selectRuleset(ruleset);
|
|
752
|
+
}
|
|
753
|
+
else {
|
|
754
|
+
console.warn("Could not select the new ruleset.");
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Utility function that updates the viewer with the latest changes.
|
|
762
|
+
* @param viewer - The or-rule-viewer HTML element
|
|
763
|
+
* @param updatedRulesets - List of new rulesets that have been changed
|
|
764
|
+
*/
|
|
765
|
+
_checkForViewerUpdate(viewer = this._viewer, updatedRulesets) {
|
|
766
|
+
const current = viewer === null || viewer === void 0 ? void 0 : viewer.ruleset;
|
|
767
|
+
if (current) {
|
|
768
|
+
const found = updatedRulesets === null || updatedRulesets === void 0 ? void 0 : updatedRulesets.find(r => { var _a; return r.id === ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.ruleset) === null || _a === void 0 ? void 0 : _a.id); });
|
|
769
|
+
if (found)
|
|
770
|
+
viewer.ruleset = found;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* HTML callback for {@link OrTreeDragEvent}, for when a rule is dragged into (or outside) a group.
|
|
775
|
+
* It handles updating of the 'meta groupId' in a rule, and persists the changes with the HTTP API.
|
|
776
|
+
* @param ev - Tree drag event with a payload of the moved nodes
|
|
777
|
+
*/
|
|
778
|
+
_onRuleDrag(ev) {
|
|
779
|
+
var _a, _b;
|
|
780
|
+
const isModified = (_a = this._viewer) === null || _a === void 0 ? void 0 : _a.modified;
|
|
781
|
+
const groupId = (_b = ev.detail.groupNode) === null || _b === void 0 ? void 0 : _b.label;
|
|
782
|
+
const moveAndSave = (ruleset, groupId) => {
|
|
783
|
+
move(ruleset, groupId);
|
|
784
|
+
return this._saveRuleset(ruleset);
|
|
785
|
+
};
|
|
786
|
+
const move = (ruleset, groupId) => {
|
|
787
|
+
if (!ruleset.meta)
|
|
788
|
+
ruleset.meta = {};
|
|
789
|
+
ruleset.meta.groupId = groupId;
|
|
790
|
+
if (Object.keys(ruleset.meta).length === 0) {
|
|
791
|
+
delete ruleset.meta;
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
// If the ruleset viewer has no changes, continue dragging the node, and apply changes.
|
|
795
|
+
if (!isModified) {
|
|
796
|
+
const promises = ev.detail.nodes.map(node => {
|
|
797
|
+
if (node.ruleset) {
|
|
798
|
+
return moveAndSave(node.ruleset, groupId);
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
Promise.all(promises)
|
|
802
|
+
.then(() => showSnackbar(undefined, "ruleDragSuccess"))
|
|
803
|
+
.catch(() => showSnackbar(undefined, "ruleDragFailed"))
|
|
804
|
+
.finally(() => { var _a; return (_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.refresh().then(rulesets => this._checkForViewerUpdate(undefined, rulesets)); });
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
console.debug("Prevented the default tree drag behavior.");
|
|
808
|
+
ev.preventDefault(); // prevent the initial rule dragging
|
|
809
|
+
// Prompt the user to "discard changes"
|
|
810
|
+
this._confirmContinue((ok) => {
|
|
811
|
+
if (ok) {
|
|
812
|
+
ev.detail.nodes.forEach(node => {
|
|
813
|
+
var _a;
|
|
814
|
+
if (node.ruleset) {
|
|
815
|
+
(_a = moveAndSave(node.ruleset, groupId)) === null || _a === void 0 ? void 0 : _a.then(() => {
|
|
816
|
+
var _a;
|
|
817
|
+
(_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.moveNodesToGroup([node], ev.detail.groupNode); // move the nodes again, as the initial event got cancelled.
|
|
818
|
+
showSnackbar(undefined, "ruleDragSuccess!");
|
|
819
|
+
}).catch(() => {
|
|
820
|
+
showSnackbar(undefined, "ruleDragFailed");
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
else if (node.ruleset) {
|
|
824
|
+
console.warn("Could not add rule to group; could not find ruleset.");
|
|
825
|
+
}
|
|
826
|
+
else if (groupId) {
|
|
827
|
+
console.warn("Could not add rule to group; could not find group ID.");
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Utility function that deselects the group by clearing {@link _selectedGroup}.
|
|
835
|
+
*/
|
|
836
|
+
_deselectGroup() {
|
|
837
|
+
this._selectedGroup = undefined;
|
|
838
|
+
return this.getUpdateComplete();
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* HTML callback for when a new group is added to the {@link OrRuleTree}.
|
|
842
|
+
* @param ev - Callback event
|
|
843
|
+
*/
|
|
844
|
+
_onGroupAddRequest(ev) {
|
|
845
|
+
this._selectedGroup = ev.detail.value;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* HTML callback for when the selected group name has changed.
|
|
849
|
+
* This function handles renaming the groupId of the rules within that group,
|
|
850
|
+
* together with persisting the changes using the HTTP API.
|
|
851
|
+
* @param ev - Callback event
|
|
852
|
+
*/
|
|
853
|
+
_onGroupNameChange(ev) {
|
|
854
|
+
var _a, _b, _c;
|
|
855
|
+
const oldValue = this._selectedGroup;
|
|
856
|
+
const newValue = ev.detail.value;
|
|
857
|
+
// If the group name already exists, we should prevent the event from happening
|
|
858
|
+
if ((_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.nodes.find(n => n.label === newValue)) {
|
|
859
|
+
console.warn(`The group '${newValue}' already exists. Please try again.`);
|
|
860
|
+
ev.preventDefault();
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
console.debug(`Renaming group '${oldValue}' to '${newValue}'`);
|
|
864
|
+
// Change the groupId for each child rule, and prepare an HTTP API update
|
|
865
|
+
const promises = [];
|
|
866
|
+
const groupRules = (_c = (_b = this._rulesTree) === null || _b === void 0 ? void 0 : _b.rules) === null || _c === void 0 ? void 0 : _c.filter(r => { var _a; return ((_a = r.meta) === null || _a === void 0 ? void 0 : _a.groupId) === oldValue; });
|
|
867
|
+
groupRules === null || groupRules === void 0 ? void 0 : groupRules.forEach((r) => {
|
|
868
|
+
r.meta.groupId = newValue;
|
|
869
|
+
promises.push(this._saveRuleset(r));
|
|
870
|
+
});
|
|
871
|
+
// If a rule has changed, execute the HTTP requests.
|
|
872
|
+
// After that, we fetch the list of rules again using .refresh();
|
|
873
|
+
if (promises.length > 0) {
|
|
874
|
+
Promise.all(promises)
|
|
875
|
+
.then(() => showSnackbar(undefined, "Saved!"))
|
|
876
|
+
.catch(() => showSnackbar(undefined, "Failed!"))
|
|
877
|
+
.finally(() => { var _a; return (_a = this._rulesTree) === null || _a === void 0 ? void 0 : _a.refresh(); });
|
|
878
|
+
// Otherwise, only update the group name in the tree.
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
if (this._rulesTree) {
|
|
882
|
+
const nodes = [...this._rulesTree.nodes];
|
|
883
|
+
const node = nodes.find(n => n.label === oldValue);
|
|
884
|
+
if (node) {
|
|
885
|
+
node.label = newValue;
|
|
886
|
+
this._rulesTree.nodes = nodes;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
this._rulesTree.nodes = [...nodes, { id: newValue, label: newValue, children: [] }];
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
this._selectedGroup = newValue;
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Utility function that returns a promise for saving the ruleset, based on its type.
|
|
897
|
+
* @param ruleset - The ruleset to be saved
|
|
898
|
+
*/
|
|
899
|
+
_saveRuleset(ruleset) {
|
|
900
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
901
|
+
let promise;
|
|
902
|
+
switch (ruleset.type) {
|
|
903
|
+
case "asset": {
|
|
904
|
+
promise = manager.rest.api.RulesResource.updateAssetRuleset(ruleset.id, ruleset);
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
case "global": {
|
|
908
|
+
promise = manager.rest.api.RulesResource.updateGlobalRuleset(ruleset.id, ruleset);
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
case "realm": {
|
|
912
|
+
promise = manager.rest.api.RulesResource.updateRealmRuleset(ruleset.id, ruleset);
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
915
|
+
default: {
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return promise;
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
;
|
|
923
|
+
};
|
|
924
|
+
OrRules.DEFAULT_RULESET_NAME = "";
|
|
925
|
+
__decorate([
|
|
926
|
+
property({ type: Boolean })
|
|
927
|
+
], OrRules.prototype, "readonly", void 0);
|
|
928
|
+
__decorate([
|
|
929
|
+
property({ type: Object })
|
|
930
|
+
], OrRules.prototype, "config", void 0);
|
|
931
|
+
__decorate([
|
|
932
|
+
property({ type: String })
|
|
933
|
+
], OrRules.prototype, "realm", void 0);
|
|
934
|
+
__decorate([
|
|
935
|
+
property({ type: String })
|
|
936
|
+
], OrRules.prototype, "language", void 0);
|
|
937
|
+
__decorate([
|
|
938
|
+
property({ attribute: false })
|
|
939
|
+
], OrRules.prototype, "_isValidRule", void 0);
|
|
940
|
+
__decorate([
|
|
941
|
+
state()
|
|
942
|
+
], OrRules.prototype, "_selectedGroup", void 0);
|
|
943
|
+
__decorate([
|
|
944
|
+
query("#rule-tree")
|
|
945
|
+
], OrRules.prototype, "_rulesTree", void 0);
|
|
946
|
+
__decorate([
|
|
947
|
+
query("#rule-viewer")
|
|
948
|
+
], OrRules.prototype, "_viewer", void 0);
|
|
949
|
+
OrRules = __decorate([
|
|
950
|
+
customElement("or-rules")
|
|
951
|
+
], OrRules);
|
|
952
|
+
export { OrRules };
|
|
953
|
+
//# sourceMappingURL=index.js.map
|