@openinc/node-red-contrib-openinc-parse 0.1.1

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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/dist/nodes/openinc-parse-changestream/modules/types.js +3 -0
  4. package/dist/nodes/openinc-parse-changestream/modules/types.js.map +1 -0
  5. package/dist/nodes/openinc-parse-changestream/openinc-parse-changestream.html +267 -0
  6. package/dist/nodes/openinc-parse-changestream/openinc-parse-changestream.js +147 -0
  7. package/dist/nodes/openinc-parse-changestream/openinc-parse-changestream.js.map +1 -0
  8. package/dist/nodes/openinc-parse-changestream/shared/types.js +3 -0
  9. package/dist/nodes/openinc-parse-changestream/shared/types.js.map +1 -0
  10. package/dist/nodes/openinc-parse-config/modules/types.js +3 -0
  11. package/dist/nodes/openinc-parse-config/modules/types.js.map +1 -0
  12. package/dist/nodes/openinc-parse-config/openinc-parse-config.html +416 -0
  13. package/dist/nodes/openinc-parse-config/openinc-parse-config.js +102 -0
  14. package/dist/nodes/openinc-parse-config/openinc-parse-config.js.map +1 -0
  15. package/dist/nodes/openinc-parse-config/shared/types.js +3 -0
  16. package/dist/nodes/openinc-parse-config/shared/types.js.map +1 -0
  17. package/dist/nodes/openinc-parse-delete/modules/types.js +3 -0
  18. package/dist/nodes/openinc-parse-delete/modules/types.js.map +1 -0
  19. package/dist/nodes/openinc-parse-delete/openinc-parse-delete.html +231 -0
  20. package/dist/nodes/openinc-parse-delete/openinc-parse-delete.js +60 -0
  21. package/dist/nodes/openinc-parse-delete/openinc-parse-delete.js.map +1 -0
  22. package/dist/nodes/openinc-parse-delete/shared/types.js +3 -0
  23. package/dist/nodes/openinc-parse-delete/shared/types.js.map +1 -0
  24. package/dist/nodes/openinc-parse-fetch-relation/modules/types.js +3 -0
  25. package/dist/nodes/openinc-parse-fetch-relation/modules/types.js.map +1 -0
  26. package/dist/nodes/openinc-parse-fetch-relation/openinc-parse-fetch-relation.html +306 -0
  27. package/dist/nodes/openinc-parse-fetch-relation/openinc-parse-fetch-relation.js +142 -0
  28. package/dist/nodes/openinc-parse-fetch-relation/openinc-parse-fetch-relation.js.map +1 -0
  29. package/dist/nodes/openinc-parse-fetch-relation/shared/types.js +3 -0
  30. package/dist/nodes/openinc-parse-fetch-relation/shared/types.js.map +1 -0
  31. package/dist/nodes/openinc-parse-find/modules/types.js +3 -0
  32. package/dist/nodes/openinc-parse-find/modules/types.js.map +1 -0
  33. package/dist/nodes/openinc-parse-find/openinc-parse-find.html +245 -0
  34. package/dist/nodes/openinc-parse-find/openinc-parse-find.js +63 -0
  35. package/dist/nodes/openinc-parse-find/openinc-parse-find.js.map +1 -0
  36. package/dist/nodes/openinc-parse-find/shared/types.js +3 -0
  37. package/dist/nodes/openinc-parse-find/shared/types.js.map +1 -0
  38. package/dist/nodes/openinc-parse-querybuilder/modules/types.js +3 -0
  39. package/dist/nodes/openinc-parse-querybuilder/modules/types.js.map +1 -0
  40. package/dist/nodes/openinc-parse-querybuilder/openinc-parse-querybuilder.html +440 -0
  41. package/dist/nodes/openinc-parse-querybuilder/openinc-parse-querybuilder.js +153 -0
  42. package/dist/nodes/openinc-parse-querybuilder/openinc-parse-querybuilder.js.map +1 -0
  43. package/dist/nodes/openinc-parse-querybuilder/shared/types.js +3 -0
  44. package/dist/nodes/openinc-parse-querybuilder/shared/types.js.map +1 -0
  45. package/dist/nodes/openinc-parse-save/modules/types.js +3 -0
  46. package/dist/nodes/openinc-parse-save/modules/types.js.map +1 -0
  47. package/dist/nodes/openinc-parse-save/openinc-parse-save.html +241 -0
  48. package/dist/nodes/openinc-parse-save/openinc-parse-save.js +75 -0
  49. package/dist/nodes/openinc-parse-save/openinc-parse-save.js.map +1 -0
  50. package/dist/nodes/openinc-parse-save/shared/types.js +3 -0
  51. package/dist/nodes/openinc-parse-save/shared/types.js.map +1 -0
  52. package/dist/nodes/openinc-parse-subscribe/modules/types.js +3 -0
  53. package/dist/nodes/openinc-parse-subscribe/modules/types.js.map +1 -0
  54. package/dist/nodes/openinc-parse-subscribe/openinc-parse-subscribe.html +260 -0
  55. package/dist/nodes/openinc-parse-subscribe/openinc-parse-subscribe.js +106 -0
  56. package/dist/nodes/openinc-parse-subscribe/openinc-parse-subscribe.js.map +1 -0
  57. package/dist/nodes/openinc-parse-subscribe/shared/types.js +3 -0
  58. package/dist/nodes/openinc-parse-subscribe/shared/types.js.map +1 -0
  59. package/dist/nodes/shared/helper.js +45 -0
  60. package/dist/nodes/shared/helper.js.map +1 -0
  61. package/dist/nodes/shared/livequery.js +171 -0
  62. package/dist/nodes/shared/livequery.js.map +1 -0
  63. package/dist/nodes/shared/parse-api.js +121 -0
  64. package/dist/nodes/shared/parse-api.js.map +1 -0
  65. package/dist/nodes/shared/schema-editor.js +163 -0
  66. package/dist/nodes/shared/schema-editor.js.map +1 -0
  67. package/dist/nodes/shared/types.js +3 -0
  68. package/dist/nodes/shared/types.js.map +1 -0
  69. package/package.json +61 -0
@@ -0,0 +1,231 @@
1
+ <script type="text/javascript">
2
+ (function () {
3
+ 'use strict';
4
+
5
+ /******************************************************************************
6
+ Copyright (c) Microsoft Corporation.
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ ***************************************************************************** */
19
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
20
+
21
+
22
+ var __assign = function() {
23
+ __assign = Object.assign || function __assign(t) {
24
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
25
+ s = arguments[i];
26
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
27
+ }
28
+ return t;
29
+ };
30
+ return __assign.apply(this, arguments);
31
+ };
32
+
33
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
34
+ var e = new Error(message);
35
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
36
+ };
37
+
38
+ // Editor-only helpers (run in the Node-RED editor, not the runtime). They parse
39
+ // a Parse schema — as exported from the dashboard or fetched from /schemas — and
40
+ // wire jQuery-UI autocomplete onto class-name / field-name inputs. Other node
41
+ // editors read the schema straight off the referenced config node, so no server
42
+ // round-trip is needed while editing.
43
+ var EMPTY = {
44
+ classNames: [],
45
+ fieldsByClass: {},
46
+ relationsByClass: {},
47
+ };
48
+ /**
49
+ * Parses a schema JSON string. Accepts the shapes the Parse dashboard / REST
50
+ * API produce: an array of class schemas, `{ results: [...] }`, or a single
51
+ * class object.
52
+ */
53
+ function parseSchema(raw) {
54
+ if (!raw || typeof raw !== "string" || raw.trim() === "")
55
+ return EMPTY;
56
+ var data;
57
+ try {
58
+ data = JSON.parse(raw);
59
+ }
60
+ catch (_a) {
61
+ return EMPTY;
62
+ }
63
+ var classes = [];
64
+ if (Array.isArray(data)) {
65
+ classes = data;
66
+ }
67
+ else if (data && Array.isArray(data.results)) {
68
+ classes = data.results;
69
+ }
70
+ else if (data && typeof data.className === "string") {
71
+ classes = [data];
72
+ }
73
+ else if (data && typeof data === "object") {
74
+ // Object keyed by class name -> schema.
75
+ classes = Object.keys(data).map(function (key) {
76
+ return data[key] && data[key].className ? data[key] : __assign({ className: key }, data[key]);
77
+ });
78
+ }
79
+ var result = {
80
+ classNames: [],
81
+ fieldsByClass: {},
82
+ relationsByClass: {},
83
+ };
84
+ var _loop_1 = function (cls) {
85
+ if (!cls || typeof cls !== "object")
86
+ return "continue";
87
+ var name_1 = cls.className;
88
+ if (typeof name_1 !== "string" || name_1 === "")
89
+ return "continue";
90
+ if (result.classNames.indexOf(name_1) === -1)
91
+ result.classNames.push(name_1);
92
+ var fields = cls.fields && typeof cls.fields === "object" ? cls.fields : {};
93
+ var fieldNames = Object.keys(fields);
94
+ result.fieldsByClass[name_1] = fieldNames;
95
+ result.relationsByClass[name_1] = fieldNames.filter(function (field) { return fields[field] && fields[field].type === "Relation"; });
96
+ };
97
+ for (var _i = 0, classes_1 = classes; _i < classes_1.length; _i++) {
98
+ var cls = classes_1[_i];
99
+ _loop_1(cls);
100
+ }
101
+ result.classNames.sort();
102
+ return result;
103
+ }
104
+ /** Parsed schema of the referenced config node (by its node id). */
105
+ function schemaForServer(serverId) {
106
+ if (!serverId)
107
+ return EMPTY;
108
+ var cfg = RED.nodes.node(serverId);
109
+ return parseSchema(cfg && cfg.schema);
110
+ }
111
+ var datalistCounter = 0;
112
+ function escapeAttr(value) {
113
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
114
+ }
115
+ /**
116
+ * Attaches suggestions to a text input via a native <datalist>. `sourceFn` is
117
+ * re-evaluated on focus/input so the options can depend on other live fields
118
+ * (e.g. the class currently typed). Native datalists work in every browser and
119
+ * need no editor framework support.
120
+ */
121
+ function attachAutocomplete(input, sourceFn) {
122
+ var el = input && input.jquery ? input[0] : input || null;
123
+ if (!el || !el.parentNode)
124
+ return;
125
+ var listId = el.getAttribute("list") || "";
126
+ var datalist = listId
127
+ ? document.getElementById(listId)
128
+ : null;
129
+ if (!datalist) {
130
+ listId = "openinc-parse-dl-" + ++datalistCounter;
131
+ datalist = document.createElement("datalist");
132
+ datalist.id = listId;
133
+ el.setAttribute("list", listId);
134
+ el.parentNode.appendChild(datalist);
135
+ }
136
+ var refresh = function () {
137
+ var items = sourceFn() || [];
138
+ datalist.innerHTML = items
139
+ .map(function (item) { return "<option value=\"".concat(escapeAttr(String(item)), "\"></option>"); })
140
+ .join("");
141
+ };
142
+ el.addEventListener("focus", refresh);
143
+ el.addEventListener("input", refresh);
144
+ refresh();
145
+ }
146
+ /** Class-name autocomplete sourced from the node's server (config) field. */
147
+ function attachClassAutocomplete(inputSelector, serverSelector) {
148
+ if (serverSelector === void 0) { serverSelector = "#node-input-server"; }
149
+ attachAutocomplete($(inputSelector), function () {
150
+ return schemaForServer($(serverSelector).val()).classNames;
151
+ });
152
+ }
153
+
154
+ RED.nodes.registerType("openinc-parse-delete", {
155
+ category: "parse",
156
+ color: "#61b7f2",
157
+ defaults: {
158
+ name: { value: "" },
159
+ server: { value: "", type: "openinc-parse-config" },
160
+ className: { value: "" },
161
+ objectId: { value: "" },
162
+ },
163
+ inputs: 1,
164
+ outputs: 1,
165
+ icon: "font-awesome/fa-trash-o",
166
+ paletteLabel: "parse delete",
167
+ label: function () {
168
+ return (this.name ||
169
+ (this.className ? "parse delete: ".concat(this.className) : "parse delete"));
170
+ },
171
+ oneditprepare: function () {
172
+ attachClassAutocomplete("#node-input-className");
173
+ },
174
+ });
175
+
176
+ })();
177
+ </script>
178
+ <script type="text/html" data-help-name="openinc-parse-delete">
179
+ <p>Deletes an object from a Parse Server class.</p>
180
+
181
+ <h2>Configuration</h2>
182
+
183
+ <ul>
184
+ <li><strong>Server:</strong> The Parse Server configuration node.</li>
185
+ <li>
186
+ <strong>Class:</strong> Name of the Parse class. Can be overridden per
187
+ message via <code>msg.payload.className</code>.
188
+ </li>
189
+ <li>
190
+ <strong>Object ID:</strong> Optional. If empty,
191
+ <code>msg.payload.objectId</code> (or a plain string
192
+ <code>msg.payload</code>) is used.
193
+ </li>
194
+ </ul>
195
+
196
+ <h2>Input</h2>
197
+
198
+ <ul>
199
+ <li>
200
+ <strong>msg.payload:</strong> Either a string containing the object id,
201
+ or an object with <code>objectId</code> and optionally
202
+ <code>className</code>.
203
+ </li>
204
+ </ul>
205
+
206
+ <h2>Output</h2>
207
+
208
+ <p>
209
+ <code>msg.payload</code> contains <code>className</code>,
210
+ <code>objectId</code> and <code>deleted: true</code> on success.
211
+ </p>
212
+ </script>
213
+
214
+ <script type="text/html" data-template-name="openinc-parse-delete">
215
+ <div class="form-row">
216
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
217
+ <input type="text" id="node-input-name" placeholder="Name">
218
+ </div>
219
+ <div class="form-row">
220
+ <label for="node-input-server"><i class="fa fa-server"></i> Server</label>
221
+ <input type="text" id="node-input-server" placeholder="Parse Server...">
222
+ </div>
223
+ <div class="form-row">
224
+ <label for="node-input-className"><i class="fa fa-table"></i> Class</label>
225
+ <input type="text" id="node-input-className" placeholder="(or msg.payload.className)">
226
+ </div>
227
+ <div class="form-row">
228
+ <label for="node-input-objectId"><i class="fa fa-hashtag"></i> Object ID</label>
229
+ <input type="text" id="node-input-objectId" placeholder="(optional, or msg.payload.objectId)">
230
+ </div>
231
+ </script>
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ const nodeInit = (RED) => {
3
+ function OpenincParseDeleteNodeConstructor(config) {
4
+ RED.nodes.createNode(this, config);
5
+ const node = this;
6
+ const server = RED.nodes.getNode(config.server);
7
+ node.on("input", async function (msg, send, done) {
8
+ var _a;
9
+ if (!server) {
10
+ node.status({
11
+ fill: "red",
12
+ shape: "ring",
13
+ text: "Select a Parse Server configuration.",
14
+ });
15
+ done();
16
+ return;
17
+ }
18
+ let className = config.className;
19
+ let objectId = config.objectId;
20
+ if (typeof msg.payload === "string" && msg.payload !== "") {
21
+ objectId = objectId || msg.payload;
22
+ }
23
+ else if (msg.payload && typeof msg.payload === "object") {
24
+ className = msg.payload.className || className;
25
+ objectId = objectId || msg.payload.objectId || "";
26
+ }
27
+ if (!className || !objectId) {
28
+ node.status({
29
+ fill: "red",
30
+ shape: "dot",
31
+ text: "className and objectId required",
32
+ });
33
+ node.error("Missing className or objectId (settings or msg.payload)", msg);
34
+ done();
35
+ return;
36
+ }
37
+ node.status({ fill: "blue", shape: "dot", text: "deleting..." });
38
+ const result = await server.api.destroy(className, objectId);
39
+ if (result.status === "error") {
40
+ node.status({
41
+ fill: "red",
42
+ shape: "dot",
43
+ text: "Error: " + ((_a = result.payload.error) !== null && _a !== void 0 ? _a : "unknown"),
44
+ });
45
+ node.error(result.payload, msg);
46
+ done();
47
+ return;
48
+ }
49
+ node.status({ fill: "green", shape: "dot", text: "deleted " + objectId });
50
+ send({
51
+ ...msg,
52
+ payload: { className, objectId, deleted: true },
53
+ });
54
+ done();
55
+ });
56
+ }
57
+ RED.nodes.registerType("openinc-parse-delete", OpenincParseDeleteNodeConstructor);
58
+ };
59
+ module.exports = nodeInit;
60
+ //# sourceMappingURL=openinc-parse-delete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openinc-parse-delete.js","sourceRoot":"","sources":["../../../src/nodes/openinc-parse-delete/openinc-parse-delete.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/nodes/openinc-parse-delete/shared/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/nodes/openinc-parse-fetch-relation/modules/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,306 @@
1
+ <script type="text/javascript">
2
+ (function () {
3
+ 'use strict';
4
+
5
+ /******************************************************************************
6
+ Copyright (c) Microsoft Corporation.
7
+
8
+ Permission to use, copy, modify, and/or distribute this software for any
9
+ purpose with or without fee is hereby granted.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ ***************************************************************************** */
19
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
20
+
21
+
22
+ var __assign = function() {
23
+ __assign = Object.assign || function __assign(t) {
24
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
25
+ s = arguments[i];
26
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
27
+ }
28
+ return t;
29
+ };
30
+ return __assign.apply(this, arguments);
31
+ };
32
+
33
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
34
+ var e = new Error(message);
35
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
36
+ };
37
+
38
+ // Editor-only helpers (run in the Node-RED editor, not the runtime). They parse
39
+ // a Parse schema — as exported from the dashboard or fetched from /schemas — and
40
+ // wire jQuery-UI autocomplete onto class-name / field-name inputs. Other node
41
+ // editors read the schema straight off the referenced config node, so no server
42
+ // round-trip is needed while editing.
43
+ var EMPTY = {
44
+ classNames: [],
45
+ fieldsByClass: {},
46
+ relationsByClass: {},
47
+ };
48
+ /**
49
+ * Parses a schema JSON string. Accepts the shapes the Parse dashboard / REST
50
+ * API produce: an array of class schemas, `{ results: [...] }`, or a single
51
+ * class object.
52
+ */
53
+ function parseSchema(raw) {
54
+ if (!raw || typeof raw !== "string" || raw.trim() === "")
55
+ return EMPTY;
56
+ var data;
57
+ try {
58
+ data = JSON.parse(raw);
59
+ }
60
+ catch (_a) {
61
+ return EMPTY;
62
+ }
63
+ var classes = [];
64
+ if (Array.isArray(data)) {
65
+ classes = data;
66
+ }
67
+ else if (data && Array.isArray(data.results)) {
68
+ classes = data.results;
69
+ }
70
+ else if (data && typeof data.className === "string") {
71
+ classes = [data];
72
+ }
73
+ else if (data && typeof data === "object") {
74
+ // Object keyed by class name -> schema.
75
+ classes = Object.keys(data).map(function (key) {
76
+ return data[key] && data[key].className ? data[key] : __assign({ className: key }, data[key]);
77
+ });
78
+ }
79
+ var result = {
80
+ classNames: [],
81
+ fieldsByClass: {},
82
+ relationsByClass: {},
83
+ };
84
+ var _loop_1 = function (cls) {
85
+ if (!cls || typeof cls !== "object")
86
+ return "continue";
87
+ var name_1 = cls.className;
88
+ if (typeof name_1 !== "string" || name_1 === "")
89
+ return "continue";
90
+ if (result.classNames.indexOf(name_1) === -1)
91
+ result.classNames.push(name_1);
92
+ var fields = cls.fields && typeof cls.fields === "object" ? cls.fields : {};
93
+ var fieldNames = Object.keys(fields);
94
+ result.fieldsByClass[name_1] = fieldNames;
95
+ result.relationsByClass[name_1] = fieldNames.filter(function (field) { return fields[field] && fields[field].type === "Relation"; });
96
+ };
97
+ for (var _i = 0, classes_1 = classes; _i < classes_1.length; _i++) {
98
+ var cls = classes_1[_i];
99
+ _loop_1(cls);
100
+ }
101
+ result.classNames.sort();
102
+ return result;
103
+ }
104
+ /** Parsed schema of the referenced config node (by its node id). */
105
+ function schemaForServer(serverId) {
106
+ if (!serverId)
107
+ return EMPTY;
108
+ var cfg = RED.nodes.node(serverId);
109
+ return parseSchema(cfg && cfg.schema);
110
+ }
111
+ var datalistCounter = 0;
112
+ function escapeAttr(value) {
113
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
114
+ }
115
+ /**
116
+ * Attaches suggestions to a text input via a native <datalist>. `sourceFn` is
117
+ * re-evaluated on focus/input so the options can depend on other live fields
118
+ * (e.g. the class currently typed). Native datalists work in every browser and
119
+ * need no editor framework support.
120
+ */
121
+ function attachAutocomplete(input, sourceFn) {
122
+ var el = input && input.jquery ? input[0] : input || null;
123
+ if (!el || !el.parentNode)
124
+ return;
125
+ var listId = el.getAttribute("list") || "";
126
+ var datalist = listId
127
+ ? document.getElementById(listId)
128
+ : null;
129
+ if (!datalist) {
130
+ listId = "openinc-parse-dl-" + ++datalistCounter;
131
+ datalist = document.createElement("datalist");
132
+ datalist.id = listId;
133
+ el.setAttribute("list", listId);
134
+ el.parentNode.appendChild(datalist);
135
+ }
136
+ var refresh = function () {
137
+ var items = sourceFn() || [];
138
+ datalist.innerHTML = items
139
+ .map(function (item) { return "<option value=\"".concat(escapeAttr(String(item)), "\"></option>"); })
140
+ .join("");
141
+ };
142
+ el.addEventListener("focus", refresh);
143
+ el.addEventListener("input", refresh);
144
+ refresh();
145
+ }
146
+ /** Class-name autocomplete sourced from the node's server (config) field. */
147
+ function attachClassAutocomplete(inputSelector, serverSelector) {
148
+ if (serverSelector === void 0) { serverSelector = "#node-input-server"; }
149
+ attachAutocomplete($(inputSelector), function () {
150
+ return schemaForServer($(serverSelector).val()).classNames;
151
+ });
152
+ }
153
+ /**
154
+ * Relation-key autocomplete: relation-type fields of the class in
155
+ * `classSelector`, falling back to all relation fields across classes.
156
+ */
157
+ function attachRelationKeyAutocomplete(input, classSelector, serverSelector) {
158
+ if (serverSelector === void 0) { serverSelector = "#node-input-server"; }
159
+ attachAutocomplete(input, function () {
160
+ var schema = schemaForServer($(serverSelector).val());
161
+ var cls = String($(classSelector).val() || "");
162
+ var rels = schema.relationsByClass[cls];
163
+ if (rels && rels.length)
164
+ return rels;
165
+ var all = [];
166
+ Object.keys(schema.relationsByClass).forEach(function (c) {
167
+ return schema.relationsByClass[c].forEach(function (f) {
168
+ if (all.indexOf(f) === -1)
169
+ all.push(f);
170
+ });
171
+ });
172
+ return all.sort();
173
+ });
174
+ }
175
+
176
+ RED.nodes.registerType("openinc-parse-fetch-relation", {
177
+ category: "parse",
178
+ color: "#61b7f2",
179
+ defaults: {
180
+ name: { value: "" },
181
+ server: { value: "", type: "openinc-parse-config" },
182
+ relationKey: { value: "" },
183
+ source: { value: "config" },
184
+ className: { value: "" },
185
+ objectId: { value: "" },
186
+ targetClassName: { value: "" },
187
+ limit: { value: "" },
188
+ order: { value: "" },
189
+ },
190
+ inputs: 1,
191
+ outputs: 1,
192
+ icon: "font-awesome/fa-link",
193
+ paletteLabel: "parse relation",
194
+ label: function () {
195
+ return (this.name ||
196
+ (this.relationKey
197
+ ? "parse relation: ".concat(this.relationKey)
198
+ : "parse relation"));
199
+ },
200
+ oneditprepare: function () {
201
+ var toggleRows = function () {
202
+ var source = String($("#node-input-source").val() || "config");
203
+ $(".node-row-config").toggle(source === "config");
204
+ };
205
+ $("#node-input-source").on("change", toggleRows);
206
+ toggleRows();
207
+ // Schema-driven autocomplete: parent class + target class from the class
208
+ // list, relation key from the parent class's relation fields.
209
+ attachClassAutocomplete("#node-input-className");
210
+ attachClassAutocomplete("#node-input-targetClassName");
211
+ attachRelationKeyAutocomplete($("#node-input-relationKey"), "#node-input-className");
212
+ },
213
+ });
214
+
215
+ })();
216
+ </script>
217
+ <script type="text/html" data-help-name="openinc-parse-fetch-relation">
218
+ <p>
219
+ Fetches the objects that are members of a Parse
220
+ <a href="https://docs.parseplatform.org/rest/guide/#relations">Relation</a>
221
+ field, using a <code>$relatedTo</code> query against the relation's target
222
+ class.
223
+ </p>
224
+
225
+ <h2>Relation key</h2>
226
+ <p>
227
+ The relation field to read is taken from <code>msg.relationKey</code> if
228
+ present, otherwise from the node's <strong>Relation</strong> setting.
229
+ </p>
230
+
231
+ <h2>Identifying the parent object</h2>
232
+ <p>Select one of two modes via <strong>Parent from</strong>:</p>
233
+ <ul>
234
+ <li>
235
+ <strong>Settings (class + id):</strong> the parent object is identified by
236
+ the configured <strong>Class</strong> and <strong>Object ID</strong>.
237
+ Both may also be supplied per message via <code>msg.className</code> and
238
+ <code>msg.objectId</code>.
239
+ </li>
240
+ <li>
241
+ <strong>msg.payload object:</strong> the parent is read from
242
+ <code>msg.payload</code> — a single Parse object as returned by the
243
+ <strong>parse find</strong> node. Its <code>className</code> and
244
+ <code>objectId</code> are used, and the relation's target class is read
245
+ from <code>msg.payload[relationKey]</code> when that field is populated.
246
+ </li>
247
+ </ul>
248
+
249
+ <h2>Target class</h2>
250
+ <p>
251
+ A <code>$relatedTo</code> query runs against the relation's target class.
252
+ Leave <strong>Target Class</strong> empty to auto-detect it: from the
253
+ relation field in the payload (payload mode), or by fetching the parent
254
+ object (settings mode). Set it explicitly to skip the extra lookup.
255
+ </p>
256
+
257
+ <h2>Output</h2>
258
+ <ul>
259
+ <li><strong>msg.payload:</strong> Array of related objects.</li>
260
+ <li><strong>msg.count:</strong> Total count (when the query requests it).</li>
261
+ <li><strong>msg.query:</strong> The <code>$relatedTo</code> query executed.</li>
262
+ <li><strong>msg.parent:</strong> The resolved <code>{ className, objectId }</code>.</li>
263
+ </ul>
264
+ </script>
265
+
266
+ <script type="text/html" data-template-name="openinc-parse-fetch-relation">
267
+ <div class="form-row">
268
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
269
+ <input type="text" id="node-input-name" placeholder="Name">
270
+ </div>
271
+ <div class="form-row">
272
+ <label for="node-input-server"><i class="fa fa-server"></i> Server</label>
273
+ <input type="text" id="node-input-server" placeholder="Parse Server...">
274
+ </div>
275
+ <div class="form-row">
276
+ <label for="node-input-relationKey"><i class="fa fa-link"></i> Relation</label>
277
+ <input type="text" id="node-input-relationKey" placeholder="relation field key (or msg.relationKey)">
278
+ </div>
279
+ <div class="form-row">
280
+ <label for="node-input-source"><i class="fa fa-sign-in"></i> Parent from</label>
281
+ <select id="node-input-source" style="width:70%">
282
+ <option value="config">Settings (class + id)</option>
283
+ <option value="payload">msg.payload object</option>
284
+ </select>
285
+ </div>
286
+ <div class="form-row node-row-config">
287
+ <label for="node-input-className"><i class="fa fa-table"></i> Class</label>
288
+ <input type="text" id="node-input-className" placeholder="parent class name">
289
+ </div>
290
+ <div class="form-row node-row-config">
291
+ <label for="node-input-objectId"><i class="fa fa-hashtag"></i> Object ID</label>
292
+ <input type="text" id="node-input-objectId" placeholder="parent objectId">
293
+ </div>
294
+ <div class="form-row">
295
+ <label for="node-input-targetClassName"><i class="fa fa-crosshairs"></i> Target Class</label>
296
+ <input type="text" id="node-input-targetClassName" placeholder="(optional, auto-detected)">
297
+ </div>
298
+ <div class="form-row">
299
+ <label for="node-input-limit"><i class="fa fa-list-ol"></i> Limit</label>
300
+ <input type="text" id="node-input-limit" placeholder="(optional, default 100)">
301
+ </div>
302
+ <div class="form-row">
303
+ <label for="node-input-order"><i class="fa fa-sort"></i> Order</label>
304
+ <input type="text" id="node-input-order" placeholder="(optional, e.g. -createdAt)">
305
+ </div>
306
+ </script>