@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,440 @@
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
+ * Field-name autocomplete for `input`, suggesting fields of the class named in
155
+ * `classSelector`; falls back to the union of all fields when no (matching)
156
+ * class is set.
157
+ */
158
+ function attachFieldAutocomplete(input, classSelector, serverSelector) {
159
+ if (serverSelector === void 0) { serverSelector = "#node-input-server"; }
160
+ attachAutocomplete(input, function () {
161
+ var schema = schemaForServer($(serverSelector).val());
162
+ var cls = String($(classSelector).val() || "");
163
+ var fields = schema.fieldsByClass[cls];
164
+ if (fields && fields.length)
165
+ return fields;
166
+ var all = [];
167
+ Object.keys(schema.fieldsByClass).forEach(function (c) {
168
+ return schema.fieldsByClass[c].forEach(function (f) {
169
+ if (all.indexOf(f) === -1)
170
+ all.push(f);
171
+ });
172
+ });
173
+ return all.sort();
174
+ });
175
+ }
176
+
177
+ var OPERATORS = [
178
+ ["equalTo", "=="],
179
+ ["notEqualTo", "!="],
180
+ ["greaterThan", ">"],
181
+ ["greaterThanOrEqualTo", ">="],
182
+ ["lessThan", "<"],
183
+ ["lessThanOrEqualTo", "<="],
184
+ ["containedIn", "in list"],
185
+ ["notContainedIn", "not in list"],
186
+ ["exists", "exists"],
187
+ ["doesNotExist", "does not exist"],
188
+ ["contains", "contains"],
189
+ ["startsWith", "starts with"],
190
+ ["matchesRegex", "matches regex"],
191
+ ];
192
+ var VALUE_TYPES = [
193
+ ["str", "string"],
194
+ ["num", "number"],
195
+ ["bool", "boolean"],
196
+ ["json", "JSON"],
197
+ ["date", "date"],
198
+ ["pointer", "pointer"],
199
+ ["msg", "msg property"],
200
+ ];
201
+ function escapeHtml(value) {
202
+ return value
203
+ .replace(/&/g, "&amp;")
204
+ .replace(/"/g, "&quot;")
205
+ .replace(/</g, "&lt;")
206
+ .replace(/>/g, "&gt;");
207
+ }
208
+ var OP_LABEL = Object.fromEntries(OPERATORS);
209
+ Object.fromEntries(VALUE_TYPES);
210
+ // Operators that ignore the value type — listed once under "General".
211
+ var TYPE_AGNOSTIC_OPS = ["exists", "doesNotExist"];
212
+ // The combined picker is the product of every operator and every datatype,
213
+ // grouped by datatype. Value-agnostic operators (exists / does not exist) are
214
+ // listed once at the top. Value format is "<op>|<valueType>" ("-" when the
215
+ // datatype is irrelevant), which oneditsave splits back into op + valueType.
216
+ function combinedOptionsHtml() {
217
+ var parts = [];
218
+ parts.push("<small>General</small>");
219
+ for (var _i = 0, TYPE_AGNOSTIC_OPS_1 = TYPE_AGNOSTIC_OPS; _i < TYPE_AGNOSTIC_OPS_1.length; _i++) {
220
+ var op = TYPE_AGNOSTIC_OPS_1[_i];
221
+ parts.push("<sl-option value=\"".concat(op, "|-\">").concat(OP_LABEL[op], "</sl-option>"));
222
+ }
223
+ var perTypeOps = OPERATORS.filter(function (_a) {
224
+ var op = _a[0];
225
+ return !TYPE_AGNOSTIC_OPS.includes(op);
226
+ });
227
+ for (var _a = 0, VALUE_TYPES_1 = VALUE_TYPES; _a < VALUE_TYPES_1.length; _a++) {
228
+ var _b = VALUE_TYPES_1[_a], valueType = _b[0], typeLabel = _b[1];
229
+ parts.push("<sl-divider></sl-divider>");
230
+ parts.push("<small>".concat(typeLabel, "</small>"));
231
+ for (var _c = 0, perTypeOps_1 = perTypeOps; _c < perTypeOps_1.length; _c++) {
232
+ var _d = perTypeOps_1[_c], op = _d[0], opLabel = _d[1];
233
+ parts.push("<sl-option value=\"".concat(op, "|").concat(valueType, "\">").concat(opLabel, " (").concat(typeLabel, ")</sl-option>"));
234
+ }
235
+ }
236
+ return parts.join("");
237
+ }
238
+ // Maps a stored condition to the combined picker value.
239
+ function conditionToOptValue(condition) {
240
+ var _a, _b;
241
+ var op = (_a = condition === null || condition === void 0 ? void 0 : condition.op) !== null && _a !== void 0 ? _a : "equalTo";
242
+ if (TYPE_AGNOSTIC_OPS.includes(op))
243
+ return "".concat(op, "|-");
244
+ return "".concat(op, "|").concat((_b = condition === null || condition === void 0 ? void 0 : condition.valueType) !== null && _b !== void 0 ? _b : "str");
245
+ }
246
+ function createConditionRow(container, condition) {
247
+ var _a, _b, _c;
248
+ var row = document.createElement("div");
249
+ row.className = "parse-qb-row";
250
+ row.style.cssText =
251
+ "display:flex;gap:4px;margin-bottom:6px;align-items:center;";
252
+ row.innerHTML = "\n <input type=\"text\" class=\"qb-field\" placeholder=\"field\" style=\"flex:1.1;min-width:0;height:30px;box-sizing:border-box;\" value=\"".concat(escapeHtml((_a = condition === null || condition === void 0 ? void 0 : condition.field) !== null && _a !== void 0 ? _a : ""), "\">\n <sl-select class=\"qb-opt\" size=\"small\" style=\"flex:2;min-width:220px;\" hoist value=\"").concat(conditionToOptValue(condition), "\">").concat(combinedOptionsHtml(), "</sl-select>\n <sl-input class=\"qb-value\" placeholder=\"value\" size=\"small\" style=\"flex:1.2;\" value=\"").concat(escapeHtml((_b = condition === null || condition === void 0 ? void 0 : condition.value) !== null && _b !== void 0 ? _b : ""), "\"></sl-input>\n <sl-button class=\"qb-remove\" size=\"small\" title=\"Remove condition\">&#10005;</sl-button>\n ");
253
+ (_c = row
254
+ .querySelector(".qb-remove")) === null || _c === void 0 ? void 0 : _c.addEventListener("click", function () { return row.remove(); });
255
+ // The value input is irrelevant for value-agnostic operators.
256
+ var opt = row.querySelector(".qb-opt");
257
+ var valueInput = row.querySelector(".qb-value");
258
+ var syncValueEnabled = function () {
259
+ var _a;
260
+ var op = String((_a = opt === null || opt === void 0 ? void 0 : opt.value) !== null && _a !== void 0 ? _a : "").split("|")[0];
261
+ if (valueInput)
262
+ valueInput.disabled = TYPE_AGNOSTIC_OPS.includes(op);
263
+ };
264
+ opt === null || opt === void 0 ? void 0 : opt.addEventListener("sl-change", syncValueEnabled);
265
+ syncValueEnabled();
266
+ container.appendChild(row);
267
+ // Field-name autocomplete from the schema of the selected class.
268
+ attachFieldAutocomplete($(row.querySelector(".qb-field")), "#node-input-className");
269
+ }
270
+ function collectConditions(container) {
271
+ var conditions = [];
272
+ container.querySelectorAll(".parse-qb-row").forEach(function (row) {
273
+ var _a, _b, _c, _d, _e, _f;
274
+ var field = (_b = (_a = row.querySelector(".qb-field")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
275
+ var combined = String((_d = (_c = row.querySelector(".qb-opt")) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : "equalTo|str");
276
+ var value = (_f = (_e = row.querySelector(".qb-value")) === null || _e === void 0 ? void 0 : _e.value) !== null && _f !== void 0 ? _f : "";
277
+ // Combined value is "<op>|<valueType>"; "-" means the datatype is unused.
278
+ var _g = combined.split("|"), opPart = _g[0], typePart = _g[1];
279
+ var op = (opPart || "equalTo");
280
+ var valueType = (typePart && typePart !== "-"
281
+ ? typePart
282
+ : "str");
283
+ if (field !== "") {
284
+ conditions.push({ field: field, op: op, value: value, valueType: valueType });
285
+ }
286
+ });
287
+ return conditions;
288
+ }
289
+ RED.nodes.registerType("openinc-parse-querybuilder", {
290
+ category: "parse",
291
+ color: "#61b7f2",
292
+ defaults: {
293
+ name: { value: "" },
294
+ server: { value: "", type: "openinc-parse-config" },
295
+ className: { value: "" },
296
+ conditions: { value: "[]" },
297
+ limit: { value: "" },
298
+ skip: { value: "" },
299
+ order: { value: "" },
300
+ keys: { value: "" },
301
+ include: { value: "" },
302
+ count: { value: false },
303
+ },
304
+ inputs: 1,
305
+ outputs: 1,
306
+ icon: "font-awesome/fa-filter",
307
+ paletteLabel: "parse querybuilder",
308
+ label: function () {
309
+ return (this.name ||
310
+ (this.className
311
+ ? "parse query: ".concat(this.className)
312
+ : "parse querybuilder"));
313
+ },
314
+ oneditprepare: function () {
315
+ var _a;
316
+ attachClassAutocomplete("#node-input-className");
317
+ var container = document.getElementById("parse-qb-conditions");
318
+ if (!container)
319
+ return;
320
+ container.innerHTML = "";
321
+ var conditions = [];
322
+ try {
323
+ conditions = JSON.parse(this.conditions || "[]");
324
+ }
325
+ catch (error) {
326
+ conditions = [];
327
+ }
328
+ conditions.forEach(function (condition) {
329
+ return createConditionRow(container, condition);
330
+ });
331
+ (_a = document
332
+ .getElementById("parse-qb-add")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", function () { return createConditionRow(container); });
333
+ },
334
+ oneditsave: function () {
335
+ var container = document.getElementById("parse-qb-conditions");
336
+ if (!container)
337
+ return;
338
+ $("#node-input-conditions").val(JSON.stringify(collectConditions(container)));
339
+ },
340
+ });
341
+
342
+ })();
343
+ </script>
344
+ <script type="text/html" data-help-name="openinc-parse-querybuilder">
345
+ <p>
346
+ Builds a Parse query object from configurable conditions. The result is
347
+ written to <code>msg.payload</code> and can be fed directly into the
348
+ <strong>parse find</strong> or <strong>parse subscribe</strong> node.
349
+ </p>
350
+
351
+ <h2>Configuration</h2>
352
+
353
+ <ul>
354
+ <li><strong>Class:</strong> Name of the Parse class to query.</li>
355
+ <li>
356
+ <strong>Conditions:</strong> A list of constraints. Each condition
357
+ consists of a field name, an operator, a value and a value type:
358
+ <ul>
359
+ <li><strong>string / number / boolean / JSON:</strong> The value is
360
+ converted accordingly.</li>
361
+ <li><strong>date:</strong> The value is parsed as a date and sent as
362
+ a Parse <code>Date</code>.</li>
363
+ <li><strong>pointer:</strong> The value
364
+ <code>ClassName:objectId</code> is sent as a Parse
365
+ <code>Pointer</code>.</li>
366
+ <li><strong>msg property:</strong> The value is read from the
367
+ incoming message at runtime (e.g. <code>payload.userId</code>).</li>
368
+ </ul>
369
+ </li>
370
+ <li><strong>Limit / Skip / Order / Keys / Include / Count:</strong>
371
+ Optional query parameters.</li>
372
+ </ul>
373
+
374
+ <h2>Input</h2>
375
+
376
+ <p>
377
+ Any message triggers the node. If <code>msg.payload.where</code> already
378
+ contains constraints, they are combined with the configured conditions
379
+ using <code>$and</code>.
380
+ </p>
381
+
382
+ <h2>Output</h2>
383
+
384
+ <p>
385
+ <code>msg.payload</code> contains the query object:
386
+ <code>{ className, where, limit, skip, order, keys, include, count }</code>
387
+ </p>
388
+ </script>
389
+
390
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.17.0/cdn/themes/light.css" />
391
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.17.0/cdn/shoelace-autoloader.js"></script>
392
+
393
+ <script type="text/html" data-template-name="openinc-parse-querybuilder">
394
+ <div class="form-row">
395
+ <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
396
+ <input type="text" id="node-input-name" placeholder="Name">
397
+ </div>
398
+ <div class="form-row">
399
+ <label for="node-input-server"><i class="fa fa-server"></i> Schema</label>
400
+ <input type="text" id="node-input-server" placeholder="(optional) Parse Server — for field autocomplete only">
401
+ </div>
402
+ <div class="form-row">
403
+ <label for="node-input-className"><i class="fa fa-table"></i> Class</label>
404
+ <input type="text" id="node-input-className" placeholder="Parse class name">
405
+ </div>
406
+
407
+ <div class="form-row">
408
+ <label><i class="fa fa-filter"></i> Conditions</label>
409
+ <div style="display:inline-block;width:78%;vertical-align:top;">
410
+ <div id="parse-qb-conditions"></div>
411
+ <sl-button id="parse-qb-add" size="small">+ Add condition</sl-button>
412
+ </div>
413
+ <input type="hidden" id="node-input-conditions">
414
+ </div>
415
+
416
+ <div class="form-row">
417
+ <label for="node-input-limit"><i class="fa fa-list-ol"></i> Limit</label>
418
+ <input type="text" id="node-input-limit" placeholder="(optional)">
419
+ </div>
420
+ <div class="form-row">
421
+ <label for="node-input-skip"><i class="fa fa-forward"></i> Skip</label>
422
+ <input type="text" id="node-input-skip" placeholder="(optional)">
423
+ </div>
424
+ <div class="form-row">
425
+ <label for="node-input-order"><i class="fa fa-sort"></i> Order</label>
426
+ <input type="text" id="node-input-order" placeholder="(optional, e.g. -createdAt)">
427
+ </div>
428
+ <div class="form-row">
429
+ <label for="node-input-keys"><i class="fa fa-columns"></i> Keys</label>
430
+ <input type="text" id="node-input-keys" placeholder="(optional, comma separated)">
431
+ </div>
432
+ <div class="form-row">
433
+ <label for="node-input-include"><i class="fa fa-link"></i> Include</label>
434
+ <input type="text" id="node-input-include" placeholder="(optional, comma separated pointers)">
435
+ </div>
436
+ <div class="form-row">
437
+ <label for="node-input-count"><i class="fa fa-calculator"></i> Count</label>
438
+ <input type="checkbox" id="node-input-count" style="display:inline-block;width:auto;vertical-align:top;">
439
+ </div>
440
+ </script>
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ function escapeRegex(value) {
3
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
+ }
5
+ const nodeInit = (RED) => {
6
+ function coerceValue(condition, msg) {
7
+ const raw = condition.value;
8
+ switch (condition.valueType) {
9
+ case "num":
10
+ return Number(raw);
11
+ case "bool":
12
+ return raw === "true" || raw === "1";
13
+ case "json":
14
+ return JSON.parse(raw);
15
+ case "date":
16
+ return { __type: "Date", iso: new Date(raw).toISOString() };
17
+ case "pointer": {
18
+ const [className, objectId] = raw.split(":");
19
+ return { __type: "Pointer", className, objectId };
20
+ }
21
+ case "msg":
22
+ return RED.util.getMessageProperty(msg, raw);
23
+ case "str":
24
+ default:
25
+ return raw;
26
+ }
27
+ }
28
+ function applyCondition(where, condition, msg) {
29
+ const field = condition.field;
30
+ if (!field)
31
+ return;
32
+ if (condition.op === "exists") {
33
+ where[field] = { ...where[field], $exists: true };
34
+ return;
35
+ }
36
+ if (condition.op === "doesNotExist") {
37
+ where[field] = { ...where[field], $exists: false };
38
+ return;
39
+ }
40
+ const value = coerceValue(condition, msg);
41
+ const setConstraint = (key, constraint) => {
42
+ if (where[field] &&
43
+ typeof where[field] === "object" &&
44
+ !Array.isArray(where[field]) &&
45
+ !("__type" in where[field])) {
46
+ where[field][key] = constraint;
47
+ }
48
+ else {
49
+ where[field] = { [key]: constraint };
50
+ }
51
+ };
52
+ const asArray = Array.isArray(value)
53
+ ? value
54
+ : String(value)
55
+ .split(",")
56
+ .map((entry) => entry.trim());
57
+ switch (condition.op) {
58
+ case "equalTo":
59
+ where[field] = value;
60
+ break;
61
+ case "notEqualTo":
62
+ setConstraint("$ne", value);
63
+ break;
64
+ case "greaterThan":
65
+ setConstraint("$gt", value);
66
+ break;
67
+ case "greaterThanOrEqualTo":
68
+ setConstraint("$gte", value);
69
+ break;
70
+ case "lessThan":
71
+ setConstraint("$lt", value);
72
+ break;
73
+ case "lessThanOrEqualTo":
74
+ setConstraint("$lte", value);
75
+ break;
76
+ case "containedIn":
77
+ setConstraint("$in", asArray);
78
+ break;
79
+ case "notContainedIn":
80
+ setConstraint("$nin", asArray);
81
+ break;
82
+ case "contains":
83
+ setConstraint("$regex", escapeRegex(String(value)));
84
+ break;
85
+ case "startsWith":
86
+ setConstraint("$regex", "^" + escapeRegex(String(value)));
87
+ break;
88
+ case "matchesRegex":
89
+ setConstraint("$regex", String(value));
90
+ break;
91
+ }
92
+ }
93
+ function OpenincParseQuerybuilderNodeConstructor(config) {
94
+ RED.nodes.createNode(this, config);
95
+ const node = this;
96
+ let conditions = [];
97
+ try {
98
+ conditions = JSON.parse(config.conditions || "[]");
99
+ }
100
+ catch (error) {
101
+ node.error("Invalid conditions configuration: " + String(error));
102
+ }
103
+ node.on("input", function (msg, send, done) {
104
+ let where = {};
105
+ try {
106
+ for (const condition of conditions) {
107
+ applyCondition(where, condition, msg);
108
+ }
109
+ }
110
+ catch (error) {
111
+ node.status({ fill: "red", shape: "dot", text: String(error) });
112
+ node.error("Failed to build query: " + String(error), msg);
113
+ done();
114
+ return;
115
+ }
116
+ // Merge an incoming where (e.g. from a chained querybuilder) via $and.
117
+ const incoming = msg.payload;
118
+ if (incoming &&
119
+ typeof incoming === "object" &&
120
+ incoming.where &&
121
+ typeof incoming.where === "object" &&
122
+ Object.keys(incoming.where).length > 0) {
123
+ where =
124
+ Object.keys(where).length > 0
125
+ ? { $and: [incoming.where, where] }
126
+ : incoming.where;
127
+ }
128
+ const query = { className: config.className };
129
+ if (Object.keys(where).length > 0)
130
+ query.where = where;
131
+ const limit = parseInt(config.limit, 10);
132
+ if (!isNaN(limit))
133
+ query.limit = limit;
134
+ const skip = parseInt(config.skip, 10);
135
+ if (!isNaN(skip))
136
+ query.skip = skip;
137
+ if (config.order)
138
+ query.order = config.order;
139
+ if (config.keys)
140
+ query.keys = config.keys;
141
+ if (config.include)
142
+ query.include = config.include;
143
+ if (config.count)
144
+ query.count = true;
145
+ node.status({});
146
+ send({ ...msg, payload: query });
147
+ done();
148
+ });
149
+ }
150
+ RED.nodes.registerType("openinc-parse-querybuilder", OpenincParseQuerybuilderNodeConstructor);
151
+ };
152
+ module.exports = nodeInit;
153
+ //# sourceMappingURL=openinc-parse-querybuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openinc-parse-querybuilder.js","sourceRoot":"","sources":["../../../src/nodes/openinc-parse-querybuilder/openinc-parse-querybuilder.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-querybuilder/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-save/modules/types.ts"],"names":[],"mappings":""}