@magda/org-tree 1.3.0-rc.0 → 2.0.0-alpha.2
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/bin/org-tree/NestedSetModelQueryer.js +2547 -1167
- package/bin/org-tree/getNodeIdFromNameOrId.js +2 -2
- package/bin/org-tree/getUserIdFromNameOrId.js +2 -2
- package/bin/org-tree/org-tree-assign.js +10 -5
- package/bin/org-tree/org-tree-create.js +25 -30
- package/bin/org-tree/org-tree-delete.js +32 -36
- package/bin/org-tree/org-tree-insert.js +32 -37
- package/bin/org-tree/org-tree-move.js +32 -38
- package/bin/org-tree/org-tree-unassign.js +40 -45
- package/bin/org-tree/org-tree-view.js +2 -2
- package/bin/org-tree/org-tree.js +16 -16
- package/package.json +4 -4
|
@@ -92,7 +92,7 @@ return /******/ (function(modules) { // webpackBootstrap
|
|
|
92
92
|
/******/
|
|
93
93
|
/******/
|
|
94
94
|
/******/ // Load entry module and return exports
|
|
95
|
-
/******/ return __webpack_require__(__webpack_require__.s =
|
|
95
|
+
/******/ return __webpack_require__(__webpack_require__.s = 13);
|
|
96
96
|
/******/ })
|
|
97
97
|
/************************************************************************/
|
|
98
98
|
/******/ ([
|
|
@@ -102,977 +102,520 @@ return /******/ (function(modules) { // webpackBootstrap
|
|
|
102
102
|
/* 3 */,
|
|
103
103
|
/* 4 */,
|
|
104
104
|
/* 5 */,
|
|
105
|
-
/* 6
|
|
106
|
-
/* 7 */,
|
|
107
|
-
/* 8 */,
|
|
108
|
-
/* 9 */
|
|
105
|
+
/* 6 */
|
|
109
106
|
/***/ (function(module, exports, __webpack_require__) {
|
|
110
107
|
|
|
111
108
|
"use strict";
|
|
112
109
|
|
|
113
|
-
var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
|
|
114
|
-
function adopt(value) {return value instanceof P ? value : new P(function (resolve) {resolve(value);});}
|
|
115
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
116
|
-
function fulfilled(value) {try {step(generator.next(value));} catch (e) {reject(e);}}
|
|
117
|
-
function rejected(value) {try {step(generator["throw"](value));} catch (e) {reject(e);}}
|
|
118
|
-
function step(result) {result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);}
|
|
119
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
120
|
-
});
|
|
121
|
-
};
|
|
122
|
-
var __importDefault = this && this.__importDefault || function (mod) {
|
|
123
|
-
return mod && mod.__esModule ? mod : { "default": mod };
|
|
124
|
-
};
|
|
125
110
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
class NodeNotFoundError extends Error {}
|
|
111
|
+
const uuidRegEx = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
112
|
+
const isUuid = (id) => typeof id === "string" && uuidRegEx.test(id);
|
|
113
|
+
exports.default = isUuid;
|
|
114
|
+
//# sourceMappingURL=isUuid.js.map
|
|
131
115
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
class
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
116
|
+
/***/ }),
|
|
117
|
+
/* 7 */
|
|
118
|
+
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
119
|
+
|
|
120
|
+
"use strict";
|
|
121
|
+
__webpack_require__.r(__webpack_exports__);
|
|
122
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SQLSyntax", function() { return SQLSyntax; });
|
|
123
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return SQLSyntax; });
|
|
124
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "escapeIdentifier", function() { return escapeIdentifier; });
|
|
125
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getTableColumnName", function() { return getTableColumnName; });
|
|
126
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sqls", function() { return sqls; });
|
|
127
|
+
/**
|
|
128
|
+
* An class represents a SQL query piece / fragment.
|
|
129
|
+
* The provided `sqls` tagged template literals will output an instance of SQLSyntax class.
|
|
130
|
+
* All properties of SQLSyntax are readonly. Thus is immutable.
|
|
131
|
+
* An instance of SQLSyntax class will record
|
|
132
|
+
*
|
|
133
|
+
* @class SQLSyntax
|
|
134
|
+
*/
|
|
135
|
+
class SQLSyntax {
|
|
136
|
+
constructor(isEmpty, rawSqlParts, rawValues, sqlParts, values) {
|
|
137
|
+
this.values = [];
|
|
138
|
+
this.sqlParts = [];
|
|
139
|
+
this.isEmpty = isEmpty ? true : false;
|
|
140
|
+
if (this.isEmpty) {
|
|
141
|
+
this.isEmpty = true;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!(rawSqlParts === null || rawSqlParts === void 0 ? void 0 : rawSqlParts.length) && !(sqlParts === null || sqlParts === void 0 ? void 0 : sqlParts.length)) {
|
|
145
|
+
throw new Error("SQLSyntax: `rawSqlParts` & `sqlParts` can't be both empty for a non-empty SQLSyntax.");
|
|
146
|
+
}
|
|
147
|
+
if (!(rawSqlParts === null || rawSqlParts === void 0 ? void 0 : rawSqlParts.length)) {
|
|
148
|
+
// allow to set this.sqlParts & this.values directly.
|
|
149
|
+
// an interface for SQLSyntax.createUnsafely
|
|
150
|
+
this.sqlParts.push(...sqlParts);
|
|
151
|
+
if (values === null || values === void 0 ? void 0 : values.length) {
|
|
152
|
+
this.values.push(...values);
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
this.rawSqlParts = rawSqlParts;
|
|
157
|
+
this.rawValues = rawValues;
|
|
158
|
+
// in case it was called with empty value (e.g. undefined ) `rawValues` parameter
|
|
159
|
+
rawValues = (rawValues === null || rawValues === void 0 ? void 0 : rawValues.length) ? rawValues : [];
|
|
160
|
+
if (rawSqlParts.length !== rawValues.length + 1) {
|
|
161
|
+
throw new Error("SQLSyntax: `rawSqlParts.length` should be equal to `rawValues.length` + 1.");
|
|
162
|
+
}
|
|
163
|
+
// if (rawSqlParts.length === 1) {
|
|
164
|
+
// this.sqlParts.push(rawSqlParts[0]);
|
|
165
|
+
// return;
|
|
166
|
+
// }
|
|
167
|
+
rawSqlParts.forEach((part, idx) => {
|
|
168
|
+
if (idx === 0) {
|
|
169
|
+
this.sqlParts.push(part);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const previousValue = rawValues === null || rawValues === void 0 ? void 0 : rawValues[idx - 1];
|
|
173
|
+
if (previousValue instanceof SQLSyntax) {
|
|
174
|
+
this.sqlParts[this.sqlParts.length - 1] += part;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
this.sqlParts.push(part);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (idx < rawValues.length) {
|
|
181
|
+
const currentVal = rawValues[idx];
|
|
182
|
+
if (currentVal instanceof SQLSyntax) {
|
|
183
|
+
if (!currentVal.isEmpty) {
|
|
184
|
+
this.sqlParts[this.sqlParts.length - 1] +=
|
|
185
|
+
currentVal.sqlParts[0];
|
|
186
|
+
this.values.push(...currentVal.values);
|
|
187
|
+
this.sqlParts.push(...currentVal.sqlParts.slice(1));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
this.values.push(currentVal);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
180
195
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
196
|
+
toQuery() {
|
|
197
|
+
let query;
|
|
198
|
+
if (SQLSyntax.customToQueryFunc) {
|
|
199
|
+
query = SQLSyntax.customToQueryFunc(this);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
query = SQLSyntax.defaultToQueryFunc(this);
|
|
203
|
+
}
|
|
204
|
+
if (SQLSyntax.isDebugMode) {
|
|
205
|
+
const [sqlQuery, params] = query;
|
|
206
|
+
console.log("SQL Query: ", sqlQuery);
|
|
207
|
+
console.log("SQL Query params: ", params);
|
|
208
|
+
}
|
|
209
|
+
return query;
|
|
187
210
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
211
|
+
static defaultToQueryFunc(sql) {
|
|
212
|
+
if (sql.isEmpty) {
|
|
213
|
+
return ["", []];
|
|
214
|
+
}
|
|
215
|
+
const finalParts = [];
|
|
216
|
+
sql.values.forEach((value, idx) => {
|
|
217
|
+
finalParts.push(sql.sqlParts[idx], `$${idx + 1}`);
|
|
218
|
+
});
|
|
219
|
+
if (sql.sqlParts.length > sql.values.length) {
|
|
220
|
+
finalParts.push(...sql.sqlParts.slice(sql.values.length, sql.sqlParts.length));
|
|
221
|
+
}
|
|
222
|
+
return [finalParts.join(""), sql.values];
|
|
192
223
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
throw new Error("defaultInsertFieldList should be an array");
|
|
196
|
-
this.defaultInsertFieldList = defaultInsertFieldList;
|
|
224
|
+
append(syntax) {
|
|
225
|
+
return SQLSyntax.sqls `${this} ${syntax}`;
|
|
197
226
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
227
|
+
groupBy(...columns) {
|
|
228
|
+
columns = SQLSyntax.filterEmpty(columns);
|
|
229
|
+
if (!(columns === null || columns === void 0 ? void 0 : columns.length)) {
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
return SQLSyntax.sqls `${this} GROUP BY ${SQLSyntax.csv(...columns)}`;
|
|
234
|
+
}
|
|
205
235
|
}
|
|
206
|
-
|
|
207
|
-
|
|
236
|
+
having(condition) {
|
|
237
|
+
return SQLSyntax.sqls `${this} HAVING ${condition}`;
|
|
208
238
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
239
|
+
orderBy(...columns) {
|
|
240
|
+
columns = SQLSyntax.filterEmpty(columns);
|
|
241
|
+
if (!(columns === null || columns === void 0 ? void 0 : columns.length)) {
|
|
242
|
+
return this;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
return SQLSyntax.sqls `${this} ORDER BY ${SQLSyntax.csv(...columns)}`;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
asc() {
|
|
249
|
+
return SQLSyntax.sqls `${this} ASC`;
|
|
250
|
+
}
|
|
251
|
+
desc() {
|
|
252
|
+
return SQLSyntax.sqls `${this} DESC`;
|
|
253
|
+
}
|
|
254
|
+
limit(n) {
|
|
255
|
+
return SQLSyntax.sqls `${this} LIMIT ${n}`;
|
|
256
|
+
}
|
|
257
|
+
offset(n) {
|
|
258
|
+
return SQLSyntax.sqls `${this} OFFSET ${n}`;
|
|
259
|
+
}
|
|
260
|
+
where(conditions) {
|
|
261
|
+
if (!conditions || conditions.isEmpty) {
|
|
262
|
+
return this;
|
|
263
|
+
}
|
|
264
|
+
return SQLSyntax.sqls `${this} WHERE ${conditions}`;
|
|
265
|
+
}
|
|
266
|
+
and(condition) {
|
|
267
|
+
if (!condition || condition.isEmpty) {
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
return SQLSyntax.sqls `${this} AND ${condition}`;
|
|
271
|
+
}
|
|
272
|
+
or(condition) {
|
|
273
|
+
if (!condition || condition.isEmpty) {
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
276
|
+
return SQLSyntax.sqls `${this} OR ${condition}`;
|
|
277
|
+
}
|
|
278
|
+
roundBracket() {
|
|
279
|
+
if (this.isEmpty) {
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
return SQLSyntax.sqls `(${this})`;
|
|
283
|
+
}
|
|
284
|
+
eq(value) {
|
|
285
|
+
if (typeof value === "undefined" || value == null) {
|
|
286
|
+
return SQLSyntax.sqls `${this} IS NULL`;
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
return SQLSyntax.sqls `${this} = ${value}`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
gt(value) {
|
|
293
|
+
return SQLSyntax.sqls `${this} > ${value}`;
|
|
294
|
+
}
|
|
295
|
+
ge(value) {
|
|
296
|
+
return SQLSyntax.sqls `${this} >= ${value}`;
|
|
297
|
+
}
|
|
298
|
+
lt(value) {
|
|
299
|
+
return SQLSyntax.sqls `${this} < ${value}`;
|
|
300
|
+
}
|
|
301
|
+
le(value) {
|
|
302
|
+
return SQLSyntax.sqls `${this} <= ${value}`;
|
|
303
|
+
}
|
|
304
|
+
isNull() {
|
|
305
|
+
return SQLSyntax.sqls `${this} IS NULL`;
|
|
306
|
+
}
|
|
307
|
+
isNotNull() {
|
|
308
|
+
return SQLSyntax.sqls `${this} IS NOT NULL`;
|
|
309
|
+
}
|
|
310
|
+
in(valueOrSubQuery) {
|
|
311
|
+
if (valueOrSubQuery instanceof SQLSyntax) {
|
|
312
|
+
if (valueOrSubQuery.isEmpty) {
|
|
313
|
+
throw new Error("empty SQLSyntax is not allowed for `in`.");
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
return SQLSyntax.sqls `${this} IN (${valueOrSubQuery})`;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
if (!(valueOrSubQuery === null || valueOrSubQuery === void 0 ? void 0 : valueOrSubQuery.length)) {
|
|
321
|
+
throw new Error("empty value list is not allowed for `in`.");
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
return SQLSyntax.sqls `${this} IN ${SQLSyntax.csv(...valueOrSubQuery.map((v) => SQLSyntax.sqls `${v}`)).roundBracket()}`;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
notIn(valueOrSubQuery) {
|
|
329
|
+
if (valueOrSubQuery instanceof SQLSyntax) {
|
|
330
|
+
if (valueOrSubQuery.isEmpty) {
|
|
331
|
+
throw new Error("empty SQLSyntax is not allowed for `notIn`.");
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
return SQLSyntax.sqls `${this} NOT IN (${valueOrSubQuery})`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
if (!(valueOrSubQuery === null || valueOrSubQuery === void 0 ? void 0 : valueOrSubQuery.length)) {
|
|
339
|
+
throw new Error("empty SQLSyntax is not allowed for `notIn`.");
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
return SQLSyntax.sqls `${this} NOT IN ${SQLSyntax.csv(...valueOrSubQuery.map((v) => SQLSyntax.sqls `${v}`)).roundBracket()}`;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
like(value) {
|
|
347
|
+
return SQLSyntax.sqls `${this} LIKE ${value}`;
|
|
348
|
+
}
|
|
349
|
+
notLike(value) {
|
|
350
|
+
return SQLSyntax.sqls `${this} NOT LIKE ${value}`;
|
|
351
|
+
}
|
|
352
|
+
exists(part) {
|
|
353
|
+
if (!part || !part.isEmpty) {
|
|
354
|
+
return SQLSyntax.empty;
|
|
355
|
+
}
|
|
356
|
+
return SQLSyntax.sqls `${this} EXISTS ${part}`;
|
|
357
|
+
}
|
|
358
|
+
notExists(part) {
|
|
359
|
+
if (!part || !part.isEmpty) {
|
|
360
|
+
return SQLSyntax.empty;
|
|
361
|
+
}
|
|
362
|
+
return SQLSyntax.sqls `${this} NOT EXISTS ${part}`;
|
|
363
|
+
}
|
|
364
|
+
static sqls(sqlParts, ...values) {
|
|
365
|
+
if (sqlParts.length === 1 && sqlParts[0] === "") {
|
|
366
|
+
return SQLSyntax.empty;
|
|
367
|
+
}
|
|
368
|
+
return new SQLSyntax(false, sqlParts, values);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Allow create an instance of SQLSyntax from a string variable `sqlStr`.
|
|
372
|
+
* The content of the string variable `sqlStr` will become the SQL query string without any escaping.
|
|
373
|
+
* You might need this method to create indentifier (e.g. tbale or column names) as SQLSyntax.
|
|
374
|
+
* Make sure you process the string input well to avoid SQL injection vulnerability.
|
|
320
375
|
*
|
|
321
|
-
* @
|
|
322
|
-
* @param {
|
|
323
|
-
* @
|
|
324
|
-
* @
|
|
325
|
-
* @returns {Promise<NodeRecord[]>}
|
|
326
|
-
* @memberof NestedSetModelQueryer
|
|
376
|
+
* @static
|
|
377
|
+
* @param {string} sqlStr
|
|
378
|
+
* @return {SQLSyntax}
|
|
379
|
+
* @memberof SQLSyntax
|
|
327
380
|
*/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const tbl = this.tableName;
|
|
331
|
-
const result = yield (client ? client : this.pool).query(`SELECT ${this.selectFields("Parents", fields)}
|
|
332
|
-
FROM "${tbl}" AS Parents, "${tbl}" AS Children
|
|
333
|
-
WHERE Children."left" ${includeMyself ? ">=" : ">"} Parents."left" AND Children."left" ${includeMyself ? "<=" : "<"} Parents."right" AND Children."id" = $1`, [childNodeId]);
|
|
334
|
-
if (!result || !result.rows || !result.rows.length)
|
|
335
|
-
return [];
|
|
336
|
-
return result.rows;
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Get Immediate Children of a Node
|
|
341
|
-
* If the node has no child (i.e. a leaf node), an empty array will be returned
|
|
342
|
-
*
|
|
343
|
-
* @param {string} parentNodeId
|
|
344
|
-
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
345
|
-
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
346
|
-
* @returns {Promise<NodeRecord[]>}
|
|
347
|
-
* @memberof NestedSetModelQueryer
|
|
348
|
-
*/
|
|
349
|
-
getImmediateChildren(parentNodeId, fields = null, client = null) {
|
|
350
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
351
|
-
const tbl = this.tableName;
|
|
352
|
-
const result = yield (client ? client : this.pool).query(`SELECT ${this.selectFields("Children", fields)}
|
|
353
|
-
FROM "${tbl}" AS Parents, "${tbl}" AS Children
|
|
354
|
-
WHERE Children."left" BETWEEN Parents."left" AND Parents."right"
|
|
355
|
-
AND Parents."left" = (
|
|
356
|
-
SELECT MAX(S."left") FROM "${tbl}" AS S
|
|
357
|
-
WHERE S."left" < Children."left" AND S."right" > Children."right"
|
|
358
|
-
)
|
|
359
|
-
AND Parents."id" = $1
|
|
360
|
-
ORDER BY Children."left" ASC`, [parentNodeId]);
|
|
361
|
-
if (!result || !result.rows || !result.rows.length)
|
|
362
|
-
return [];
|
|
363
|
-
return result.rows;
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Get Immediate Parent of a Node
|
|
368
|
-
* If the node has no parent (i.e. a root node), null will be returned
|
|
369
|
-
*
|
|
370
|
-
* @param {string} childNodeId
|
|
371
|
-
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
372
|
-
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
373
|
-
* @returns {Promise<NodeRecord>}
|
|
374
|
-
* @memberof NestedSetModelQueryer
|
|
375
|
-
*/
|
|
376
|
-
getImmediateParent(childNodeId, fields = null, client = null) {
|
|
377
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
378
|
-
const tbl = this.tableName;
|
|
379
|
-
const result = yield (client ? client : this.pool).query(`SELECT ${this.selectFields("Parents", fields)}
|
|
380
|
-
FROM "${tbl}" AS Parents, "${tbl}" AS Children
|
|
381
|
-
WHERE Children.left BETWEEN Parents.left AND Parents.right
|
|
382
|
-
AND Parents.left = (
|
|
383
|
-
SELECT MAX(S.left) FROM "${tbl}" AS S
|
|
384
|
-
WHERE S.left < Children.left AND S.right > Children.right
|
|
385
|
-
)
|
|
386
|
-
AND Children.id = $1`, [childNodeId]);
|
|
387
|
-
if (!result || !result.rows || !result.rows.length)
|
|
388
|
-
return tsmonad_1.Maybe.nothing();
|
|
389
|
-
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Get all nodes at level n from top
|
|
394
|
-
* e.g. get all nodes at level 3:
|
|
395
|
-
* this.getAllNodesAtLevel(3)
|
|
396
|
-
* Root node is at level 1
|
|
397
|
-
*
|
|
398
|
-
* @param {number} level
|
|
399
|
-
* @returns {Promise<NodeRecord[]>}
|
|
400
|
-
* @memberof NestedSetModelQueryer
|
|
401
|
-
*/
|
|
402
|
-
getAllNodesAtLevel(level) {
|
|
403
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
404
|
-
const tbl = this.tableName;
|
|
405
|
-
const result = yield this.pool.query(`SELECT ${this.selectFields("t2")}
|
|
406
|
-
FROM "${tbl}" AS t1, "${tbl}" AS t2
|
|
407
|
-
WHERE t2.left BETWEEN t1.left AND t1.right
|
|
408
|
-
GROUP BY t2.id
|
|
409
|
-
HAVING COUNT(t1.id) = $1`, [level]);
|
|
410
|
-
if (!result || !result.rows || !result.rows.length)
|
|
411
|
-
return [];
|
|
412
|
-
return result.rows;
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Get level no. of a given node
|
|
417
|
-
* Starts from 1. i.e. The root node is 1
|
|
418
|
-
*
|
|
419
|
-
* @param {string} nodeId
|
|
420
|
-
* @returns {Promise<number>}
|
|
421
|
-
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
422
|
-
* @memberof NestedSetModelQueryer
|
|
423
|
-
*/
|
|
424
|
-
getLevelOfNode(nodeId) {
|
|
425
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
426
|
-
const tbl = this.tableName;
|
|
427
|
-
const result = yield this.pool.query(`SELECT COUNT(Parents.id) AS level
|
|
428
|
-
FROM "${tbl}" AS Parents, "${tbl}" AS Children
|
|
429
|
-
WHERE Children.left BETWEEN Parents.left AND Parents.right AND Children.id = $1`, [nodeId]);
|
|
430
|
-
if (!result || !result.rows || !result.rows.length)
|
|
431
|
-
throw new NodeNotFoundError();
|
|
432
|
-
const level = parseInt(result.rows[0]["level"]);
|
|
433
|
-
if (!lodash_1.default.isNumber(level) || lodash_1.default.isNaN(level) || level < 1)
|
|
434
|
-
throw new Error(`Could find a valid level for node ${nodeId}: ${level}`);
|
|
435
|
-
return level;
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* Get total height (no. of the levels) of the tree
|
|
440
|
-
* Starts with 1 level.
|
|
441
|
-
*
|
|
442
|
-
* @returns {Promise<number>}
|
|
443
|
-
* @throws NodeNotFoundError If the root node can't be found in the tree
|
|
444
|
-
* @memberof NestedSetModelQueryer
|
|
445
|
-
*/
|
|
446
|
-
getTreeHeight() {
|
|
447
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
448
|
-
const tbl = this.tableName;
|
|
449
|
-
const result = yield this.pool.query(`SELECT MAX(level) AS height
|
|
450
|
-
FROM(
|
|
451
|
-
SELECT COUNT(t1.id)
|
|
452
|
-
FROM "${tbl}" AS t1, "${tbl}" AS t2
|
|
453
|
-
WHERE t2.left BETWEEN t1.left AND t1.right
|
|
454
|
-
GROUP BY t2.id
|
|
455
|
-
) AS L(level)`);
|
|
456
|
-
if (!result || !result.rows || !result.rows.length)
|
|
457
|
-
throw new NodeNotFoundError();
|
|
458
|
-
const height = parseInt(result.rows[0]["height"]);
|
|
459
|
-
if (!lodash_1.default.isNumber(height) || lodash_1.default.isNaN(height) || height < 0)
|
|
460
|
-
throw new Error(`Invalid height for tree: ${height}`);
|
|
461
|
-
return height;
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Get left most immediate child of a node
|
|
466
|
-
*
|
|
467
|
-
* @param {string} parentNodeId
|
|
468
|
-
* @returns {Promise<Maybe<NodeRecord>>}
|
|
469
|
-
* @memberof NestedSetModelQueryer
|
|
470
|
-
*/
|
|
471
|
-
getLeftMostImmediateChild(parentNodeId) {
|
|
472
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
473
|
-
const tbl = this.tableName;
|
|
474
|
-
const result = yield this.pool.query(`SELECT ${this.selectFields("Children")}
|
|
475
|
-
FROM "${tbl}" AS Parents, "${tbl}" AS Children
|
|
476
|
-
WHERE Children.left = Parents.left + 1 AND Parents.id = $1`, [parentNodeId]);
|
|
477
|
-
if (!result || !result.rows || !result.rows.length)
|
|
478
|
-
return tsmonad_1.Maybe.nothing();
|
|
479
|
-
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Get right most immediate child of a node
|
|
484
|
-
*
|
|
485
|
-
* @param {string} parentNodeId
|
|
486
|
-
* @returns {Promise<Maybe<NodeRecord>>}
|
|
487
|
-
* @memberof NestedSetModelQueryer
|
|
488
|
-
*/
|
|
489
|
-
getRightMostImmediateChild(parentNodeId) {
|
|
490
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
491
|
-
const tbl = this.tableName;
|
|
492
|
-
const result = yield this.pool.query(`SELECT ${this.selectFields("Children")}
|
|
493
|
-
FROM "${tbl}" AS Parents, "${tbl}" AS Children
|
|
494
|
-
WHERE Children.right = Parents.right - 1 AND Parents.id = $1`, [parentNodeId]);
|
|
495
|
-
if (!result || !result.rows || !result.rows.length)
|
|
496
|
-
return tsmonad_1.Maybe.nothing();
|
|
497
|
-
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Get all nodes on the top to down path between the `higherNode` to the `lowerNode`
|
|
502
|
-
* Sort from higher level nodes to lower level node
|
|
503
|
-
* If a path doesn't exist, null will be returned
|
|
504
|
-
* If you pass a lower node to the `higherNodeId` and a higher node to `lowerNodeId`, null will be returned
|
|
505
|
-
*
|
|
506
|
-
* @param {string} higherNodeId
|
|
507
|
-
* @param {string} lowerNodeId
|
|
508
|
-
* @returns {Promise<Maybe<NodeRecord[]>}
|
|
509
|
-
* @memberof NestedSetModelQueryer
|
|
510
|
-
*/
|
|
511
|
-
getTopDownPathBetween(higherNodeId, lowerNodeId) {
|
|
512
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
513
|
-
const tbl = this.tableName;
|
|
514
|
-
const result = yield this.pool.query(`SELECT ${this.selectFields("t2")}
|
|
515
|
-
FROM "${tbl}" AS t1, "${tbl}" AS t2, "${tbl}" AS t3
|
|
516
|
-
WHERE t1.id = $1 AND t3.id = $2
|
|
517
|
-
AND t2.left BETWEEN t1.left AND t1.right
|
|
518
|
-
AND t3.left BETWEEN t2.left AND t2.right
|
|
519
|
-
ORDER BY (t2.right-t2.left) DESC`, [higherNodeId, lowerNodeId]);
|
|
520
|
-
if (!result || !result.rows || !result.rows.length)
|
|
521
|
-
return tsmonad_1.Maybe.nothing();
|
|
522
|
-
return tsmonad_1.Maybe.just(result.rows);
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* Compare the relative position of the two nodes
|
|
527
|
-
* If node1 is superior to node2, return "ancestor"
|
|
528
|
-
* if node1 is the subordinate of node2, return "descendant"
|
|
529
|
-
* If node1 = node2 return "equal"
|
|
530
|
-
* If there is no path can be found between Node1 and Node2 return "unrelated"
|
|
531
|
-
*
|
|
532
|
-
* @param {string} node1Id
|
|
533
|
-
* @param {string} node2Id
|
|
534
|
-
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
535
|
-
* @returns {Promise<CompareNodeResult>}
|
|
536
|
-
* @memberof NestedSetModelQueryer
|
|
537
|
-
*/
|
|
538
|
-
compareNodes(node1Id, node2Id, client = null) {
|
|
539
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
540
|
-
const tbl = this.tableName;
|
|
541
|
-
const result = yield (client ? client : this.pool).query(`SELECT (
|
|
542
|
-
CASE
|
|
543
|
-
WHEN CAST($1 AS varchar) = CAST($2 AS varchar)
|
|
544
|
-
THEN 0
|
|
545
|
-
WHEN t1.left BETWEEN t2.left AND t2.right
|
|
546
|
-
THEN -1
|
|
547
|
-
WHEN t2.left BETWEEN t1.left AND t1.right
|
|
548
|
-
THEN 1
|
|
549
|
-
ELSE null
|
|
550
|
-
END
|
|
551
|
-
) AS "result"
|
|
552
|
-
FROM "${tbl}" AS t1, "${tbl}" AS t2
|
|
553
|
-
WHERE t1.id = CAST($1 AS uuid) AND t2.id = CAST($2 AS uuid)`, [node1Id, node2Id]);
|
|
554
|
-
if (!result || !result.rows || !result.rows.length)
|
|
555
|
-
return "unrelated";
|
|
556
|
-
const comparisonResult = result.rows[0]["result"];
|
|
557
|
-
if (typeof comparisonResult === "number") {
|
|
558
|
-
switch (comparisonResult) {
|
|
559
|
-
case 1:
|
|
560
|
-
return "ancestor";
|
|
561
|
-
case -1:
|
|
562
|
-
return "descendant";
|
|
563
|
-
case 0:
|
|
564
|
-
return "equal";}
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
return "unrelated";
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
getInsertFields(insertFieldList = null) {
|
|
571
|
-
const fieldList = isNonEmptyArray(insertFieldList) ?
|
|
572
|
-
insertFieldList :
|
|
573
|
-
this.defaultInsertFieldList;
|
|
574
|
-
if (!isNonEmptyArray(fieldList)) {
|
|
575
|
-
throw new Error("Insert fields must be an non-empty array!");
|
|
381
|
+
static createUnsafely(sqlStr) {
|
|
382
|
+
return new SQLSyntax(false, undefined, undefined, [sqlStr]);
|
|
576
383
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
384
|
+
static filterEmpty(parts) {
|
|
385
|
+
if (!(parts === null || parts === void 0 ? void 0 : parts.length)) {
|
|
386
|
+
return [];
|
|
387
|
+
}
|
|
388
|
+
return parts.filter((item) => item && !item.isEmpty);
|
|
582
389
|
}
|
|
583
|
-
|
|
584
|
-
|
|
390
|
+
static join(parts, delimiter, spaceBeforeDelimier = true) {
|
|
391
|
+
parts = SQLSyntax.filterEmpty(parts);
|
|
392
|
+
if (!(parts === null || parts === void 0 ? void 0 : parts.length)) {
|
|
393
|
+
return SQLSyntax.empty;
|
|
394
|
+
}
|
|
395
|
+
const sep = spaceBeforeDelimier
|
|
396
|
+
? SQLSyntax.sqls ` ${delimiter}`
|
|
397
|
+
: delimiter;
|
|
398
|
+
let result = parts[0];
|
|
399
|
+
if (parts.length > 1) {
|
|
400
|
+
for (let i = 1; i < parts.length; i++) {
|
|
401
|
+
result = SQLSyntax.sqls `${result}${sep}${parts[i]}`;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return result;
|
|
585
405
|
}
|
|
586
|
-
|
|
587
|
-
|
|
406
|
+
static csv(...parts) {
|
|
407
|
+
parts = SQLSyntax.filterEmpty(parts);
|
|
408
|
+
if (!(parts === null || parts === void 0 ? void 0 : parts.length)) {
|
|
409
|
+
return SQLSyntax.empty;
|
|
410
|
+
}
|
|
411
|
+
return SQLSyntax.join(parts, SQLSyntax.sqls `,`, false);
|
|
588
412
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
throw new Error(`column name: ${f} contains invalid characters!`);
|
|
595
|
-
}
|
|
596
|
-
return tableAliasOrName == "" ?
|
|
597
|
-
`"${f}"` :
|
|
598
|
-
`${tableAliasOrName}."${f}"`;
|
|
599
|
-
}).
|
|
600
|
-
join(", ");
|
|
601
|
-
const valuesList = nodes.
|
|
602
|
-
map(node => "(" +
|
|
603
|
-
fieldList.
|
|
604
|
-
map(f => {
|
|
605
|
-
sqlValues.push(node[f]);
|
|
606
|
-
return `$${sqlValues.length}`;
|
|
607
|
-
}).
|
|
608
|
-
join(", ") +
|
|
609
|
-
")").
|
|
610
|
-
join(", ");
|
|
611
|
-
return `INSERT INTO "${tbl}" (${columnsList}) VALUES ${valuesList}`;
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* Create the root node of the tree.
|
|
615
|
-
* If a root node already exists, an error will be thrown.
|
|
616
|
-
*
|
|
617
|
-
* @param {NodeRecord} node
|
|
618
|
-
* @param {pg.Client} [existingClient=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
619
|
-
* @returns {Promise<string>} newly created node ID
|
|
620
|
-
* @memberof NestedSetModelQueryer
|
|
621
|
-
*/
|
|
622
|
-
createRootNode(node, existingClient = null) {
|
|
623
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
624
|
-
const tbl = this.tableName;
|
|
625
|
-
const client = existingClient ?
|
|
626
|
-
existingClient :
|
|
627
|
-
yield this.pool.connect();
|
|
628
|
-
const fields = Object.keys(node);
|
|
629
|
-
if (!fields.length) {
|
|
630
|
-
throw new Error("`node` parameter cannot be an empty object with no key.");
|
|
631
|
-
}
|
|
632
|
-
let nodeId;
|
|
633
|
-
try {
|
|
634
|
-
yield client.query("BEGIN");
|
|
635
|
-
let result = yield client.query(`SELECT "id" FROM "${tbl}" WHERE "left" = 1 LIMIT 1`);
|
|
636
|
-
if (result && isNonEmptyArray(result.rows)) {
|
|
637
|
-
throw new Error(`A root node with id: ${result.rows[0]["id"]} already exists`);
|
|
413
|
+
static hasAndOr(s) {
|
|
414
|
+
let [sqlStr] = s.toQuery();
|
|
415
|
+
sqlStr = sqlStr.toLowerCase();
|
|
416
|
+
if (sqlStr.indexOf(" and ") == -1 && sqlStr.indexOf(" or ") == -1) {
|
|
417
|
+
return false;
|
|
638
418
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
parseInt(countResult.rows[0].num) :
|
|
642
|
-
0;
|
|
643
|
-
countNum = isNaN(countNum) ? 0 : countNum;
|
|
644
|
-
const right = countNum ? (countNum + 1) * 2 : 2;
|
|
645
|
-
const sqlValues = [];
|
|
646
|
-
result = yield client.query(this.getNodesInsertSql([
|
|
647
|
-
Object.assign(Object.assign({}, node), { left: 1, right })],
|
|
648
|
-
sqlValues, fields.concat(["left", "right"])) + " RETURNING id", sqlValues);
|
|
649
|
-
if (!result || !isNonEmptyArray(result.rows)) {
|
|
650
|
-
throw new Error("Cannot locate create root node ID!");
|
|
419
|
+
else {
|
|
420
|
+
return true;
|
|
651
421
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
throw e;
|
|
658
|
-
} finally
|
|
659
|
-
{
|
|
660
|
-
if (!existingClient) {
|
|
661
|
-
client.release();
|
|
422
|
+
}
|
|
423
|
+
static joinWithAnd(conditions) {
|
|
424
|
+
conditions = SQLSyntax.filterEmpty(conditions);
|
|
425
|
+
if (!(conditions === null || conditions === void 0 ? void 0 : conditions.length)) {
|
|
426
|
+
return SQLSyntax.empty;
|
|
662
427
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
just: node => node,
|
|
672
|
-
nothing: () => {
|
|
673
|
-
throw new NodeNotFoundError(`Cannot locate tree node record with id: ${nodeId}`);
|
|
674
|
-
} });
|
|
675
|
-
|
|
676
|
-
});
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Insert a node to the tree under a parent node
|
|
680
|
-
*
|
|
681
|
-
* @param {string} parentNodeId
|
|
682
|
-
* @param {NodeRecord} node
|
|
683
|
-
* @returns {Promise<string>}
|
|
684
|
-
* @throws NodeNotFoundError if parent node not found
|
|
685
|
-
* @memberof NestedSetModelQueryer
|
|
686
|
-
*/
|
|
687
|
-
insertNode(parentNodeId, node) {
|
|
688
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
689
|
-
if (!parentNodeId) {
|
|
690
|
-
throw new Error("`parentNodeId` cannot be empty!");
|
|
691
|
-
}
|
|
692
|
-
const fields = Object.keys(node);
|
|
693
|
-
if (!fields.length) {
|
|
694
|
-
throw new Error("`node` parameter cannot be an empty object with no key.");
|
|
695
|
-
}
|
|
696
|
-
const tbl = this.tableName;
|
|
697
|
-
const client = yield this.pool.connect();
|
|
698
|
-
let nodeId;
|
|
699
|
-
try {
|
|
700
|
-
yield client.query("BEGIN");
|
|
701
|
-
const { right: parentRight } = yield this.getNodeDataWithinTx(client, parentNodeId, ["right"]);
|
|
702
|
-
yield client.query(`UPDATE "${tbl}"
|
|
703
|
-
SET
|
|
704
|
-
"left" = CASE WHEN "left" > $1 THEN "left" + 2 ELSE "left" END,
|
|
705
|
-
"right" = CASE WHEN "right" >= $1 THEN "right" + 2 ELSE "right" END
|
|
706
|
-
WHERE "right" >= $1`, [parentRight]);
|
|
707
|
-
const sqlValues = [];
|
|
708
|
-
const result = yield client.query(this.getNodesInsertSql([
|
|
709
|
-
Object.assign(Object.assign({}, node), { left: parentRight, right: parentRight + 1 })],
|
|
710
|
-
sqlValues, fields.concat(["left", "right"])) + " RETURNING id", sqlValues);
|
|
711
|
-
if (!result || !isNonEmptyArray(result.rows)) {
|
|
712
|
-
throw new Error("Cannot locate created node ID!");
|
|
428
|
+
return SQLSyntax.join(conditions
|
|
429
|
+
.filter((s) => s && !s.isEmpty)
|
|
430
|
+
.map((s) => (SQLSyntax.hasAndOr(s) ? s.roundBracket() : s)), SQLSyntax.sqls ` AND `, false);
|
|
431
|
+
}
|
|
432
|
+
static joinWithOr(conditions) {
|
|
433
|
+
conditions = SQLSyntax.filterEmpty(conditions);
|
|
434
|
+
if (!(conditions === null || conditions === void 0 ? void 0 : conditions.length)) {
|
|
435
|
+
return SQLSyntax.empty;
|
|
713
436
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
if (!siblingNodeId) {
|
|
740
|
-
throw new Error("`siblingNodeId` cannot be empty!");
|
|
741
|
-
}
|
|
742
|
-
const fields = Object.keys(node);
|
|
743
|
-
if (!fields.length) {
|
|
744
|
-
throw new Error("`node` parameter cannot be an empty object with no key.");
|
|
745
|
-
}
|
|
746
|
-
const tbl = this.tableName;
|
|
747
|
-
const client = yield this.pool.connect();
|
|
748
|
-
let nodeId;
|
|
749
|
-
try {
|
|
750
|
-
yield client.query("BEGIN");
|
|
751
|
-
const { left: siblingLeft, right: siblingRight } = yield this.getNodeDataWithinTx(client, siblingNodeId, [
|
|
752
|
-
"left",
|
|
753
|
-
"right"]);
|
|
754
|
-
|
|
755
|
-
if (siblingLeft === 1) {
|
|
756
|
-
throw new Error("Cannot add sibling to the Root node!");
|
|
437
|
+
return SQLSyntax.join(conditions
|
|
438
|
+
.filter((s) => s && !s.isEmpty)
|
|
439
|
+
.map((s) => (SQLSyntax.hasAndOr(s) ? s.roundBracket() : s)), SQLSyntax.sqls ` OR `, false);
|
|
440
|
+
}
|
|
441
|
+
static groupBy(...columns) {
|
|
442
|
+
return SQLSyntax.empty.groupBy(...columns);
|
|
443
|
+
}
|
|
444
|
+
static having(condition) {
|
|
445
|
+
return SQLSyntax.empty.having(condition);
|
|
446
|
+
}
|
|
447
|
+
static orderBy(...columns) {
|
|
448
|
+
return SQLSyntax.empty.orderBy(...columns);
|
|
449
|
+
}
|
|
450
|
+
static limit(n) {
|
|
451
|
+
return SQLSyntax.empty.limit(n);
|
|
452
|
+
}
|
|
453
|
+
static offset(n) {
|
|
454
|
+
return SQLSyntax.empty.offset(n);
|
|
455
|
+
}
|
|
456
|
+
static where(condition) {
|
|
457
|
+
return SQLSyntax.empty.where(condition);
|
|
458
|
+
}
|
|
459
|
+
static eq(column, value) {
|
|
460
|
+
if (typeof value === "undefined" || value == null) {
|
|
461
|
+
return SQLSyntax.sqls `${column} IS NULL`;
|
|
757
462
|
}
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
"left" = CASE WHEN "left" < $1 THEN "left" ELSE "left" + 2 END,
|
|
761
|
-
"right" = CASE WHEN "right" < $1 THEN "right" ELSE "right" + 2 END
|
|
762
|
-
WHERE "right" > $1`, [siblingRight]);
|
|
763
|
-
const sqlValues = [];
|
|
764
|
-
const result = yield client.query(this.getNodesInsertSql([
|
|
765
|
-
Object.assign(Object.assign({}, node), { left: siblingRight + 1, right: siblingRight + 2 })],
|
|
766
|
-
sqlValues, fields.concat(["left", "right"])) + " RETURNING id", sqlValues);
|
|
767
|
-
if (!result || !isNonEmptyArray(result.rows)) {
|
|
768
|
-
throw new Error("Cannot locate created node ID!");
|
|
463
|
+
else {
|
|
464
|
+
return SQLSyntax.sqls `${column} = ${value}`;
|
|
769
465
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
798
|
-
if (!subTreeRootNodeId) {
|
|
799
|
-
throw new Error("`subTreeRootNodeId` cannot be empty!");
|
|
800
|
-
}
|
|
801
|
-
if (!newParentId) {
|
|
802
|
-
throw new Error("`newParentId` cannot be empty!");
|
|
803
|
-
}
|
|
804
|
-
const tbl = this.tableName;
|
|
805
|
-
const client = yield this.pool.connect();
|
|
806
|
-
try {
|
|
807
|
-
yield client.query("BEGIN");
|
|
808
|
-
const comparisonResult = yield this.compareNodes(subTreeRootNodeId, newParentId, client);
|
|
809
|
-
if (comparisonResult === "ancestor") {
|
|
810
|
-
throw new Error(`Cannot move a higher level node (id: ${subTreeRootNodeId})to its subordinate (id: ${newParentId})`);
|
|
466
|
+
}
|
|
467
|
+
static gt(column, value) {
|
|
468
|
+
return SQLSyntax.sqls `${column} > ${value}`;
|
|
469
|
+
}
|
|
470
|
+
static ge(column, value) {
|
|
471
|
+
return SQLSyntax.sqls `${column} >= ${value}`;
|
|
472
|
+
}
|
|
473
|
+
static lt(column, value) {
|
|
474
|
+
return SQLSyntax.sqls `${column} < ${value}`;
|
|
475
|
+
}
|
|
476
|
+
static le(column, value) {
|
|
477
|
+
return SQLSyntax.sqls `${column} <= ${value}`;
|
|
478
|
+
}
|
|
479
|
+
static isNull(column) {
|
|
480
|
+
return SQLSyntax.sqls `${column} IS NULL`;
|
|
481
|
+
}
|
|
482
|
+
static isNotNull(column) {
|
|
483
|
+
return SQLSyntax.sqls `${column} IS NOT NULL`;
|
|
484
|
+
}
|
|
485
|
+
static in(column, valueOrSubQuery) {
|
|
486
|
+
if (valueOrSubQuery instanceof SQLSyntax) {
|
|
487
|
+
if (valueOrSubQuery.isEmpty) {
|
|
488
|
+
return SQLSyntax.sqls `FALSE`;
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
return SQLSyntax.sqls `${column} IN (${valueOrSubQuery})`;
|
|
492
|
+
}
|
|
811
493
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
494
|
+
else {
|
|
495
|
+
if (!(valueOrSubQuery === null || valueOrSubQuery === void 0 ? void 0 : valueOrSubQuery.length)) {
|
|
496
|
+
return SQLSyntax.sqls `FALSE`;
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
return SQLSyntax.sqls `${column} IN ${SQLSyntax.csv(...valueOrSubQuery.map((v) => SQLSyntax.sqls `${v}`)).roundBracket()}`;
|
|
500
|
+
}
|
|
818
501
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
WHEN "left" BETWEEN $3 AND ($1 - 1)
|
|
829
|
-
THEN $2 - $1 + 1
|
|
830
|
-
ELSE 0 END
|
|
831
|
-
WHEN $3::int4 > $2::int4
|
|
832
|
-
THEN CASE
|
|
833
|
-
WHEN "left" BETWEEN $1 AND $2
|
|
834
|
-
THEN $3 - $2 - 1
|
|
835
|
-
WHEN "left" BETWEEN ($2 + 1) AND ($3 - 1)
|
|
836
|
-
THEN $1 - $2 - 1
|
|
837
|
-
ELSE 0 END
|
|
838
|
-
ELSE 0 END,
|
|
839
|
-
"right" = "right" + CASE
|
|
840
|
-
WHEN $3::int4 < $1::int4
|
|
841
|
-
THEN CASE
|
|
842
|
-
WHEN "right" BETWEEN $1 AND $2
|
|
843
|
-
THEN $3 - $1
|
|
844
|
-
WHEN "right" BETWEEN $3 AND ($1 - 1)
|
|
845
|
-
THEN $2 - $1 + 1
|
|
846
|
-
ELSE 0 END
|
|
847
|
-
WHEN $3::int4 > $2::int4
|
|
848
|
-
THEN CASE
|
|
849
|
-
WHEN "right" BETWEEN $1 AND $2
|
|
850
|
-
THEN $3 - $2 - 1
|
|
851
|
-
WHEN "right" BETWEEN ($2 + 1) AND ($3 - 1)
|
|
852
|
-
THEN $1 - $2 - 1
|
|
853
|
-
ELSE 0 END
|
|
854
|
-
ELSE 0 END
|
|
855
|
-
`, [originRootLeft, originRootRight, newParentRight]);
|
|
856
|
-
yield client.query("COMMIT");
|
|
857
|
-
}
|
|
858
|
-
catch (e) {
|
|
859
|
-
yield client.query("ROLLBACK");
|
|
860
|
-
throw e;
|
|
861
|
-
} finally
|
|
862
|
-
{
|
|
863
|
-
client.release();
|
|
864
|
-
}
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* Delete a subtree (and all its dependents)
|
|
869
|
-
* If you sent in a root node id (and `allowRootNodeId` is true), the whole tree will be removed
|
|
870
|
-
* When `allowRootNodeId` is false and you passed a root node id, an error will be thrown
|
|
871
|
-
*
|
|
872
|
-
* @param {string} subTreeRootNodeId
|
|
873
|
-
* @param {boolean} [allowRootNodeId=false]
|
|
874
|
-
* @returns {Promise<void>}
|
|
875
|
-
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
876
|
-
* @memberof NestedSetModelQueryer
|
|
877
|
-
*/
|
|
878
|
-
deleteSubTree(subTreeRootNodeId, allowRootNodeId = false) {
|
|
879
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
880
|
-
if (!subTreeRootNodeId) {
|
|
881
|
-
throw new Error("`subTreeRootNodeId` cannot be empty!");
|
|
882
|
-
}
|
|
883
|
-
const tbl = this.tableName;
|
|
884
|
-
const client = yield this.pool.connect();
|
|
885
|
-
try {
|
|
886
|
-
yield client.query("BEGIN");
|
|
887
|
-
const { left: subTreeRootLeft, right: subTreeRootRight } = yield this.getNodeDataWithinTx(client, subTreeRootNodeId, [
|
|
888
|
-
"left",
|
|
889
|
-
"right"]);
|
|
890
|
-
|
|
891
|
-
if (subTreeRootLeft === 1 && !allowRootNodeId) {
|
|
892
|
-
throw new Error("Root node id is not allowed!");
|
|
893
|
-
}
|
|
894
|
-
// --- delete the sub tree nodes
|
|
895
|
-
yield client.query(`DELETE FROM "${tbl}" WHERE "left" BETWEEN $1 AND $2`, [subTreeRootLeft, subTreeRootRight]);
|
|
896
|
-
// --- closing the gap after deletion
|
|
897
|
-
yield client.query(`
|
|
898
|
-
UPDATE "${tbl}"
|
|
899
|
-
SET "left" = CASE
|
|
900
|
-
WHEN "left" > $1
|
|
901
|
-
THEN "left" - ($2 - $1 + 1)
|
|
902
|
-
ELSE "left" END,
|
|
903
|
-
"right" = CASE
|
|
904
|
-
WHEN "right" > $1
|
|
905
|
-
THEN "right" - ($2 - $1 + 1)
|
|
906
|
-
ELSE "right" END
|
|
907
|
-
WHERE "left" > $1 OR "right" > $1
|
|
908
|
-
`, [subTreeRootLeft, subTreeRootRight]);
|
|
909
|
-
yield client.query("COMMIT");
|
|
910
|
-
}
|
|
911
|
-
catch (e) {
|
|
912
|
-
yield client.query("ROLLBACK");
|
|
913
|
-
throw e;
|
|
914
|
-
} finally
|
|
915
|
-
{
|
|
916
|
-
client.release();
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
/**
|
|
921
|
-
* Delete a single node from the tree
|
|
922
|
-
* Its childrens will become its parent's children
|
|
923
|
-
* Deleting a root node is not allowed.
|
|
924
|
-
* You can, however, delete the whole sub tree from root node or update the root node, instead.
|
|
925
|
-
*
|
|
926
|
-
* @param {string} nodeId
|
|
927
|
-
* @returns {Promise<void>}
|
|
928
|
-
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
929
|
-
* @memberof NestedSetModelQueryer
|
|
930
|
-
*/
|
|
931
|
-
deleteNode(nodeId) {
|
|
932
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
933
|
-
if (!nodeId) {
|
|
934
|
-
throw new Error("`nodeId` cannot be empty!");
|
|
935
|
-
}
|
|
936
|
-
const tbl = this.tableName;
|
|
937
|
-
const client = yield this.pool.connect();
|
|
938
|
-
try {
|
|
939
|
-
yield client.query("BEGIN");
|
|
940
|
-
const { left: subTreeRootLeft, right: subTreeRootRight } = yield this.getNodeDataWithinTx(client, nodeId, [
|
|
941
|
-
"left",
|
|
942
|
-
"right"]);
|
|
943
|
-
|
|
944
|
-
if (subTreeRootLeft === 1) {
|
|
945
|
-
throw new Error("Delete a root node is not allowed!");
|
|
502
|
+
}
|
|
503
|
+
static notIn(column, valueOrSubQuery) {
|
|
504
|
+
if (valueOrSubQuery instanceof SQLSyntax) {
|
|
505
|
+
if (valueOrSubQuery.isEmpty) {
|
|
506
|
+
return SQLSyntax.sqls `TRUE`;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
return SQLSyntax.sqls `${column} NOT IN (${valueOrSubQuery})`;
|
|
510
|
+
}
|
|
946
511
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
UPDATE "${tbl}"
|
|
955
|
-
SET "left" = CASE
|
|
956
|
-
WHEN "left" > $1 AND "right" < $2
|
|
957
|
-
THEN "left" - 1
|
|
958
|
-
WHEN "left" > $1 AND "right" > $2
|
|
959
|
-
THEN "left" - 2
|
|
960
|
-
ELSE "left" END,
|
|
961
|
-
"right" = CASE
|
|
962
|
-
WHEN "left" > $1 AND "right" < $2
|
|
963
|
-
THEN "right" - 1
|
|
964
|
-
ELSE ("right" - 2) END
|
|
965
|
-
WHERE "left" > $1 OR "right" > $1
|
|
966
|
-
`, [subTreeRootLeft, subTreeRootRight]);
|
|
967
|
-
yield client.query("COMMIT");
|
|
968
|
-
}
|
|
969
|
-
catch (e) {
|
|
970
|
-
yield client.query("ROLLBACK");
|
|
971
|
-
throw e;
|
|
972
|
-
} finally
|
|
973
|
-
{
|
|
974
|
-
client.release();
|
|
975
|
-
}
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* Update node data of the node specified by the nodeId
|
|
980
|
-
* The followings fields will be ignored (as they should be generated by program):
|
|
981
|
-
* - `left`
|
|
982
|
-
* - `right`
|
|
983
|
-
* - `id`
|
|
984
|
-
*
|
|
985
|
-
* @param {string} nodeId
|
|
986
|
-
* @param {NodeRecord} nodeData
|
|
987
|
-
* @param {pg.Client} [client=null]
|
|
988
|
-
* @returns {Promise<void>}
|
|
989
|
-
* @memberof NestedSetModelQueryer
|
|
990
|
-
*/
|
|
991
|
-
updateNode(nodeId, nodeData, client = null) {
|
|
992
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
993
|
-
if (nodeId.trim() === "") {
|
|
994
|
-
throw new Error("nodeId can't be empty!");
|
|
995
|
-
}
|
|
996
|
-
const sqlValues = [nodeId];
|
|
997
|
-
const updateFields = Object.keys(nodeData).filter(k => k !== "left" && k !== "right" && k !== "id");
|
|
998
|
-
if (!updateFields.length) {
|
|
999
|
-
throw new Error("No valid node data passed for updating.");
|
|
1000
|
-
}
|
|
1001
|
-
const setFieldList = updateFields.
|
|
1002
|
-
map(f => {
|
|
1003
|
-
if (!isValidSqlIdentifier(f)) {
|
|
1004
|
-
throw new Error(`field name: ${f} contains invalid characters!`);
|
|
512
|
+
else {
|
|
513
|
+
if (!(valueOrSubQuery === null || valueOrSubQuery === void 0 ? void 0 : valueOrSubQuery.length)) {
|
|
514
|
+
return SQLSyntax.sqls `TRUE`;
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
return SQLSyntax.sqls `${column} NOT IN ${SQLSyntax.csv(...valueOrSubQuery.map((v) => SQLSyntax.sqls `${v}`)).roundBracket()}`;
|
|
518
|
+
}
|
|
1005
519
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
|
|
520
|
+
}
|
|
521
|
+
static like(column, value) {
|
|
522
|
+
return SQLSyntax.sqls `${column} LIKE ${value}`;
|
|
523
|
+
}
|
|
524
|
+
static notLike(column, value) {
|
|
525
|
+
return SQLSyntax.sqls `${column} NOT LIKE ${value}`;
|
|
526
|
+
}
|
|
527
|
+
static exists(part) {
|
|
528
|
+
return SQLSyntax.empty.exists(part);
|
|
529
|
+
}
|
|
530
|
+
static notExists(part) {
|
|
531
|
+
return SQLSyntax.empty.notExists(part);
|
|
532
|
+
}
|
|
533
|
+
static distinct(...columns) {
|
|
534
|
+
return SQLSyntax.sqls `DISTINCT ${SQLSyntax.csv(...columns)}`;
|
|
535
|
+
}
|
|
536
|
+
static count(column) {
|
|
537
|
+
return SQLSyntax.sqls `COUNT(${column})`;
|
|
538
|
+
}
|
|
539
|
+
static min(column) {
|
|
540
|
+
return SQLSyntax.sqls `MIN(${column})`;
|
|
541
|
+
}
|
|
542
|
+
static max(column) {
|
|
543
|
+
return SQLSyntax.sqls `MAX(${column})`;
|
|
544
|
+
}
|
|
545
|
+
static avg(column) {
|
|
546
|
+
return SQLSyntax.sqls `AVG(${column})`;
|
|
547
|
+
}
|
|
548
|
+
static sum(column) {
|
|
549
|
+
return SQLSyntax.sqls `SUM(${column})`;
|
|
550
|
+
}
|
|
551
|
+
static roundBracket(inner) {
|
|
552
|
+
if (!inner || !(inner === null || inner === void 0 ? void 0 : inner.isEmpty)) {
|
|
553
|
+
return SQLSyntax.empty;
|
|
1029
554
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
555
|
+
return inner.roundBracket();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
SQLSyntax.isDebugMode = false;
|
|
559
|
+
SQLSyntax.empty = new SQLSyntax(true);
|
|
560
|
+
SQLSyntax.asc = SQLSyntax.empty.asc();
|
|
561
|
+
SQLSyntax.desc = SQLSyntax.empty.desc();
|
|
562
|
+
function sqls(sqlParts, ...values) {
|
|
563
|
+
return SQLSyntax.sqls(sqlParts, ...values);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Escape postgreSQL SQL identifier string
|
|
567
|
+
* Although postgreSQL does allow non-ASCII characters in identifiers, to make it simple, we will remove any non-ASCII characters.
|
|
568
|
+
*
|
|
569
|
+
* @export
|
|
570
|
+
* @param {string} idStr
|
|
571
|
+
* @return {*} {string}
|
|
572
|
+
*/
|
|
573
|
+
function escapeIdentifierStr(idStr) {
|
|
574
|
+
return '"' + idStr.replace(/[^\x20-\x7e]/g, "").replace(/"/g, "\"'") + '"';
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Escape postgreSQL SQL identifier (e.g. column names, or table names).
|
|
578
|
+
* `xxx."ss.dd` will be escaped as `"xxx"."""ss"."dd"`
|
|
579
|
+
* Although postgreSQL does allow non-ASCII characters in identifiers, to make it simple, we will remove any non-ASCII characters.
|
|
580
|
+
*
|
|
581
|
+
* @export
|
|
582
|
+
* @param {string} id
|
|
583
|
+
* @return {*} {SQLSyntax}
|
|
584
|
+
*/
|
|
585
|
+
function escapeIdentifier(id) {
|
|
586
|
+
const sanitisedIdStr = id.replace(/[^\x20-\x7e]/g, "");
|
|
587
|
+
const parts = sanitisedIdStr.split(".");
|
|
588
|
+
const escapedIdStr = parts.length > 1
|
|
589
|
+
? parts.map((item) => escapeIdentifierStr(item)).join(".")
|
|
590
|
+
: escapeIdentifierStr(sanitisedIdStr);
|
|
591
|
+
return SQLSyntax.createUnsafely(escapedIdStr);
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Make a postgreSQL identifier in SQLSyntax from tableRef (optional) & column name.
|
|
595
|
+
*
|
|
596
|
+
* @export
|
|
597
|
+
* @param {String} columnName
|
|
598
|
+
* @param {String} [tableRef=""]
|
|
599
|
+
* @param {Boolean} [useLowerCaseColumnName=true]
|
|
600
|
+
* @return {*} {SQLSyntax}
|
|
601
|
+
*/
|
|
602
|
+
function getTableColumnName(columnName, tableRef = "", useLowerCaseColumnName = false) {
|
|
603
|
+
const id = [
|
|
604
|
+
tableRef,
|
|
605
|
+
useLowerCaseColumnName ? columnName.toLowerCase : useLowerCaseColumnName
|
|
606
|
+
]
|
|
607
|
+
.filter((item) => item)
|
|
608
|
+
.join(".");
|
|
609
|
+
return escapeIdentifier(id);
|
|
610
|
+
}
|
|
1060
611
|
|
|
1061
|
-
} else
|
|
1062
|
-
{
|
|
1063
|
-
tree.push(rootNode.name);
|
|
1064
|
-
}
|
|
1065
|
-
return textTree(tree);
|
|
1066
|
-
}),
|
|
1067
|
-
nothing: () => __awaiter(this, void 0, void 0, function* () {return "Empty Tree";}) });
|
|
1068
612
|
|
|
1069
|
-
|
|
1070
|
-
}}
|
|
613
|
+
//# sourceMappingURL=index.js.map
|
|
1071
614
|
|
|
1072
|
-
exports.default = NestedSetModelQueryer;
|
|
1073
615
|
|
|
1074
616
|
/***/ }),
|
|
1075
|
-
/*
|
|
617
|
+
/* 8 */,
|
|
618
|
+
/* 9 */
|
|
1076
619
|
/***/ (function(module, exports, __webpack_require__) {
|
|
1077
620
|
|
|
1078
621
|
/* WEBPACK VAR INJECTION */(function(module) {var __WEBPACK_AMD_DEFINE_RESULT__;/**
|
|
@@ -18025,262 +17568,1851 @@ exports.default = NestedSetModelQueryer;
|
|
|
18025
17568
|
|
|
18026
17569
|
/*------------------------------------------------------------------------*/
|
|
18027
17570
|
|
|
18028
|
-
/**
|
|
18029
|
-
* The semantic version number.
|
|
17571
|
+
/**
|
|
17572
|
+
* The semantic version number.
|
|
17573
|
+
*
|
|
17574
|
+
* @static
|
|
17575
|
+
* @memberOf _
|
|
17576
|
+
* @type {string}
|
|
17577
|
+
*/
|
|
17578
|
+
lodash.VERSION = VERSION;
|
|
17579
|
+
|
|
17580
|
+
// Assign default placeholders.
|
|
17581
|
+
arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) {
|
|
17582
|
+
lodash[methodName].placeholder = lodash;
|
|
17583
|
+
});
|
|
17584
|
+
|
|
17585
|
+
// Add `LazyWrapper` methods for `_.drop` and `_.take` variants.
|
|
17586
|
+
arrayEach(['drop', 'take'], function(methodName, index) {
|
|
17587
|
+
LazyWrapper.prototype[methodName] = function(n) {
|
|
17588
|
+
n = n === undefined ? 1 : nativeMax(toInteger(n), 0);
|
|
17589
|
+
|
|
17590
|
+
var result = (this.__filtered__ && !index)
|
|
17591
|
+
? new LazyWrapper(this)
|
|
17592
|
+
: this.clone();
|
|
17593
|
+
|
|
17594
|
+
if (result.__filtered__) {
|
|
17595
|
+
result.__takeCount__ = nativeMin(n, result.__takeCount__);
|
|
17596
|
+
} else {
|
|
17597
|
+
result.__views__.push({
|
|
17598
|
+
'size': nativeMin(n, MAX_ARRAY_LENGTH),
|
|
17599
|
+
'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
|
|
17600
|
+
});
|
|
17601
|
+
}
|
|
17602
|
+
return result;
|
|
17603
|
+
};
|
|
17604
|
+
|
|
17605
|
+
LazyWrapper.prototype[methodName + 'Right'] = function(n) {
|
|
17606
|
+
return this.reverse()[methodName](n).reverse();
|
|
17607
|
+
};
|
|
17608
|
+
});
|
|
17609
|
+
|
|
17610
|
+
// Add `LazyWrapper` methods that accept an `iteratee` value.
|
|
17611
|
+
arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
|
|
17612
|
+
var type = index + 1,
|
|
17613
|
+
isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
|
|
17614
|
+
|
|
17615
|
+
LazyWrapper.prototype[methodName] = function(iteratee) {
|
|
17616
|
+
var result = this.clone();
|
|
17617
|
+
result.__iteratees__.push({
|
|
17618
|
+
'iteratee': getIteratee(iteratee, 3),
|
|
17619
|
+
'type': type
|
|
17620
|
+
});
|
|
17621
|
+
result.__filtered__ = result.__filtered__ || isFilter;
|
|
17622
|
+
return result;
|
|
17623
|
+
};
|
|
17624
|
+
});
|
|
17625
|
+
|
|
17626
|
+
// Add `LazyWrapper` methods for `_.head` and `_.last`.
|
|
17627
|
+
arrayEach(['head', 'last'], function(methodName, index) {
|
|
17628
|
+
var takeName = 'take' + (index ? 'Right' : '');
|
|
17629
|
+
|
|
17630
|
+
LazyWrapper.prototype[methodName] = function() {
|
|
17631
|
+
return this[takeName](1).value()[0];
|
|
17632
|
+
};
|
|
17633
|
+
});
|
|
17634
|
+
|
|
17635
|
+
// Add `LazyWrapper` methods for `_.initial` and `_.tail`.
|
|
17636
|
+
arrayEach(['initial', 'tail'], function(methodName, index) {
|
|
17637
|
+
var dropName = 'drop' + (index ? '' : 'Right');
|
|
17638
|
+
|
|
17639
|
+
LazyWrapper.prototype[methodName] = function() {
|
|
17640
|
+
return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
|
|
17641
|
+
};
|
|
17642
|
+
});
|
|
17643
|
+
|
|
17644
|
+
LazyWrapper.prototype.compact = function() {
|
|
17645
|
+
return this.filter(identity);
|
|
17646
|
+
};
|
|
17647
|
+
|
|
17648
|
+
LazyWrapper.prototype.find = function(predicate) {
|
|
17649
|
+
return this.filter(predicate).head();
|
|
17650
|
+
};
|
|
17651
|
+
|
|
17652
|
+
LazyWrapper.prototype.findLast = function(predicate) {
|
|
17653
|
+
return this.reverse().find(predicate);
|
|
17654
|
+
};
|
|
17655
|
+
|
|
17656
|
+
LazyWrapper.prototype.invokeMap = baseRest(function(path, args) {
|
|
17657
|
+
if (typeof path == 'function') {
|
|
17658
|
+
return new LazyWrapper(this);
|
|
17659
|
+
}
|
|
17660
|
+
return this.map(function(value) {
|
|
17661
|
+
return baseInvoke(value, path, args);
|
|
17662
|
+
});
|
|
17663
|
+
});
|
|
17664
|
+
|
|
17665
|
+
LazyWrapper.prototype.reject = function(predicate) {
|
|
17666
|
+
return this.filter(negate(getIteratee(predicate)));
|
|
17667
|
+
};
|
|
17668
|
+
|
|
17669
|
+
LazyWrapper.prototype.slice = function(start, end) {
|
|
17670
|
+
start = toInteger(start);
|
|
17671
|
+
|
|
17672
|
+
var result = this;
|
|
17673
|
+
if (result.__filtered__ && (start > 0 || end < 0)) {
|
|
17674
|
+
return new LazyWrapper(result);
|
|
17675
|
+
}
|
|
17676
|
+
if (start < 0) {
|
|
17677
|
+
result = result.takeRight(-start);
|
|
17678
|
+
} else if (start) {
|
|
17679
|
+
result = result.drop(start);
|
|
17680
|
+
}
|
|
17681
|
+
if (end !== undefined) {
|
|
17682
|
+
end = toInteger(end);
|
|
17683
|
+
result = end < 0 ? result.dropRight(-end) : result.take(end - start);
|
|
17684
|
+
}
|
|
17685
|
+
return result;
|
|
17686
|
+
};
|
|
17687
|
+
|
|
17688
|
+
LazyWrapper.prototype.takeRightWhile = function(predicate) {
|
|
17689
|
+
return this.reverse().takeWhile(predicate).reverse();
|
|
17690
|
+
};
|
|
17691
|
+
|
|
17692
|
+
LazyWrapper.prototype.toArray = function() {
|
|
17693
|
+
return this.take(MAX_ARRAY_LENGTH);
|
|
17694
|
+
};
|
|
17695
|
+
|
|
17696
|
+
// Add `LazyWrapper` methods to `lodash.prototype`.
|
|
17697
|
+
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|
17698
|
+
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
|
|
17699
|
+
isTaker = /^(?:head|last)$/.test(methodName),
|
|
17700
|
+
lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
|
|
17701
|
+
retUnwrapped = isTaker || /^find/.test(methodName);
|
|
17702
|
+
|
|
17703
|
+
if (!lodashFunc) {
|
|
17704
|
+
return;
|
|
17705
|
+
}
|
|
17706
|
+
lodash.prototype[methodName] = function() {
|
|
17707
|
+
var value = this.__wrapped__,
|
|
17708
|
+
args = isTaker ? [1] : arguments,
|
|
17709
|
+
isLazy = value instanceof LazyWrapper,
|
|
17710
|
+
iteratee = args[0],
|
|
17711
|
+
useLazy = isLazy || isArray(value);
|
|
17712
|
+
|
|
17713
|
+
var interceptor = function(value) {
|
|
17714
|
+
var result = lodashFunc.apply(lodash, arrayPush([value], args));
|
|
17715
|
+
return (isTaker && chainAll) ? result[0] : result;
|
|
17716
|
+
};
|
|
17717
|
+
|
|
17718
|
+
if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {
|
|
17719
|
+
// Avoid lazy use if the iteratee has a "length" value other than `1`.
|
|
17720
|
+
isLazy = useLazy = false;
|
|
17721
|
+
}
|
|
17722
|
+
var chainAll = this.__chain__,
|
|
17723
|
+
isHybrid = !!this.__actions__.length,
|
|
17724
|
+
isUnwrapped = retUnwrapped && !chainAll,
|
|
17725
|
+
onlyLazy = isLazy && !isHybrid;
|
|
17726
|
+
|
|
17727
|
+
if (!retUnwrapped && useLazy) {
|
|
17728
|
+
value = onlyLazy ? value : new LazyWrapper(this);
|
|
17729
|
+
var result = func.apply(value, args);
|
|
17730
|
+
result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
|
|
17731
|
+
return new LodashWrapper(result, chainAll);
|
|
17732
|
+
}
|
|
17733
|
+
if (isUnwrapped && onlyLazy) {
|
|
17734
|
+
return func.apply(this, args);
|
|
17735
|
+
}
|
|
17736
|
+
result = this.thru(interceptor);
|
|
17737
|
+
return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;
|
|
17738
|
+
};
|
|
17739
|
+
});
|
|
17740
|
+
|
|
17741
|
+
// Add `Array` methods to `lodash.prototype`.
|
|
17742
|
+
arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
|
|
17743
|
+
var func = arrayProto[methodName],
|
|
17744
|
+
chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
|
|
17745
|
+
retUnwrapped = /^(?:pop|shift)$/.test(methodName);
|
|
17746
|
+
|
|
17747
|
+
lodash.prototype[methodName] = function() {
|
|
17748
|
+
var args = arguments;
|
|
17749
|
+
if (retUnwrapped && !this.__chain__) {
|
|
17750
|
+
var value = this.value();
|
|
17751
|
+
return func.apply(isArray(value) ? value : [], args);
|
|
17752
|
+
}
|
|
17753
|
+
return this[chainName](function(value) {
|
|
17754
|
+
return func.apply(isArray(value) ? value : [], args);
|
|
17755
|
+
});
|
|
17756
|
+
};
|
|
17757
|
+
});
|
|
17758
|
+
|
|
17759
|
+
// Map minified method names to their real names.
|
|
17760
|
+
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|
17761
|
+
var lodashFunc = lodash[methodName];
|
|
17762
|
+
if (lodashFunc) {
|
|
17763
|
+
var key = lodashFunc.name + '';
|
|
17764
|
+
if (!hasOwnProperty.call(realNames, key)) {
|
|
17765
|
+
realNames[key] = [];
|
|
17766
|
+
}
|
|
17767
|
+
realNames[key].push({ 'name': methodName, 'func': lodashFunc });
|
|
17768
|
+
}
|
|
17769
|
+
});
|
|
17770
|
+
|
|
17771
|
+
realNames[createHybrid(undefined, WRAP_BIND_KEY_FLAG).name] = [{
|
|
17772
|
+
'name': 'wrapper',
|
|
17773
|
+
'func': undefined
|
|
17774
|
+
}];
|
|
17775
|
+
|
|
17776
|
+
// Add methods to `LazyWrapper`.
|
|
17777
|
+
LazyWrapper.prototype.clone = lazyClone;
|
|
17778
|
+
LazyWrapper.prototype.reverse = lazyReverse;
|
|
17779
|
+
LazyWrapper.prototype.value = lazyValue;
|
|
17780
|
+
|
|
17781
|
+
// Add chain sequence methods to the `lodash` wrapper.
|
|
17782
|
+
lodash.prototype.at = wrapperAt;
|
|
17783
|
+
lodash.prototype.chain = wrapperChain;
|
|
17784
|
+
lodash.prototype.commit = wrapperCommit;
|
|
17785
|
+
lodash.prototype.next = wrapperNext;
|
|
17786
|
+
lodash.prototype.plant = wrapperPlant;
|
|
17787
|
+
lodash.prototype.reverse = wrapperReverse;
|
|
17788
|
+
lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
|
|
17789
|
+
|
|
17790
|
+
// Add lazy aliases.
|
|
17791
|
+
lodash.prototype.first = lodash.prototype.head;
|
|
17792
|
+
|
|
17793
|
+
if (symIterator) {
|
|
17794
|
+
lodash.prototype[symIterator] = wrapperToIterator;
|
|
17795
|
+
}
|
|
17796
|
+
return lodash;
|
|
17797
|
+
});
|
|
17798
|
+
|
|
17799
|
+
/*--------------------------------------------------------------------------*/
|
|
17800
|
+
|
|
17801
|
+
// Export lodash.
|
|
17802
|
+
var _ = runInContext();
|
|
17803
|
+
|
|
17804
|
+
// Some AMD build optimizers, like r.js, check for condition patterns like:
|
|
17805
|
+
if (true) {
|
|
17806
|
+
// Expose Lodash on the global object to prevent errors when Lodash is
|
|
17807
|
+
// loaded by a script tag in the presence of an AMD loader.
|
|
17808
|
+
// See http://requirejs.org/docs/errors.html#mismatch for more details.
|
|
17809
|
+
// Use `_.noConflict` to remove Lodash from the global object.
|
|
17810
|
+
root._ = _;
|
|
17811
|
+
|
|
17812
|
+
// Define as an anonymous module so, through path mapping, it can be
|
|
17813
|
+
// referenced as the "underscore" module.
|
|
17814
|
+
!(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
|
|
17815
|
+
return _;
|
|
17816
|
+
}).call(exports, __webpack_require__, exports, module),
|
|
17817
|
+
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
|
|
17818
|
+
}
|
|
17819
|
+
// Check for `exports` after `define` in case a build optimizer adds it.
|
|
17820
|
+
else {}
|
|
17821
|
+
}.call(this));
|
|
17822
|
+
|
|
17823
|
+
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(14)(module)))
|
|
17824
|
+
|
|
17825
|
+
/***/ }),
|
|
17826
|
+
/* 10 */
|
|
17827
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
17828
|
+
|
|
17829
|
+
"use strict";
|
|
17830
|
+
|
|
17831
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17832
|
+
if (k2 === undefined) k2 = k;
|
|
17833
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
17834
|
+
}) : (function(o, m, k, k2) {
|
|
17835
|
+
if (k2 === undefined) k2 = k;
|
|
17836
|
+
o[k2] = m[k];
|
|
17837
|
+
}));
|
|
17838
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17839
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17840
|
+
}) : function(o, v) {
|
|
17841
|
+
o["default"] = v;
|
|
17842
|
+
});
|
|
17843
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
17844
|
+
if (mod && mod.__esModule) return mod;
|
|
17845
|
+
var result = {};
|
|
17846
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
17847
|
+
__setModuleDefault(result, mod);
|
|
17848
|
+
return result;
|
|
17849
|
+
};
|
|
17850
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
17851
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
17852
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
17853
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17854
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17855
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17856
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17857
|
+
});
|
|
17858
|
+
};
|
|
17859
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17860
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17861
|
+
};
|
|
17862
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17863
|
+
exports.countTableRecord = exports.getTableRecord = exports.searchTableRecord = exports.MAX_PAGE_RECORD_NUMBER = exports.parseIntParam = exports.deleteTableRecord = exports.updateTableRecord = exports.createTableRecord = exports.getTableColumnName = exports.escapeIdentifier = exports.escapeIdentifierStr = void 0;
|
|
17864
|
+
const sql_syntax_1 = __importStar(__webpack_require__(7));
|
|
17865
|
+
const AuthDecision_1 = __webpack_require__(11);
|
|
17866
|
+
const lodash_1 = __webpack_require__(9);
|
|
17867
|
+
const ServerError_1 = __importDefault(__webpack_require__(17));
|
|
17868
|
+
/**
|
|
17869
|
+
* Escape SQL identifier string
|
|
17870
|
+
* Although postgreSQL does allow non-ASCII characters in identifiers, to make it simple, we will remove any non-ASCII characters.
|
|
17871
|
+
*
|
|
17872
|
+
* @export
|
|
17873
|
+
* @param {string} idStr
|
|
17874
|
+
* @return {*} {string}
|
|
17875
|
+
*/
|
|
17876
|
+
function escapeIdentifierStr(idStr) {
|
|
17877
|
+
return '"' + idStr.replace(/[^\x20-\x7e]/g, "").replace(/"/g, "\"'") + '"';
|
|
17878
|
+
}
|
|
17879
|
+
exports.escapeIdentifierStr = escapeIdentifierStr;
|
|
17880
|
+
/**
|
|
17881
|
+
* Escape SQL identifier (e.g. column names, or table names).
|
|
17882
|
+
* `xxx."ss.dd` will be escaped as `"xxx"."""ss"."dd"`
|
|
17883
|
+
* Although postgreSQL does allow non-ASCII characters in identifiers, to make it simple, we will remove any non-ASCII characters.
|
|
17884
|
+
*
|
|
17885
|
+
* @export
|
|
17886
|
+
* @param {string} id
|
|
17887
|
+
* @return {*} {SQLSyntax}
|
|
17888
|
+
*/
|
|
17889
|
+
function escapeIdentifier(id) {
|
|
17890
|
+
const sanitisedIdStr = id.replace(/[^\x20-\x7e]/g, "");
|
|
17891
|
+
const parts = sanitisedIdStr.split(".");
|
|
17892
|
+
const escapedIdStr = parts.length > 1
|
|
17893
|
+
? parts.map((item) => escapeIdentifierStr(item)).join(".")
|
|
17894
|
+
: escapeIdentifierStr(sanitisedIdStr);
|
|
17895
|
+
return sql_syntax_1.default.createUnsafely(escapedIdStr);
|
|
17896
|
+
}
|
|
17897
|
+
exports.escapeIdentifier = escapeIdentifier;
|
|
17898
|
+
/**
|
|
17899
|
+
* Make a postgreSQL identifier in SQLSyntax from tableRef (optional) & column name.
|
|
17900
|
+
*
|
|
17901
|
+
* @export
|
|
17902
|
+
* @param {String} columnName
|
|
17903
|
+
* @param {String} [tableRef=""]
|
|
17904
|
+
* @param {Boolean} [useLowerCaseColumnName=true]
|
|
17905
|
+
* @return {*} {SQLSyntax}
|
|
17906
|
+
*/
|
|
17907
|
+
function getTableColumnName(columnName, tableRef = "", useLowerCaseColumnName = false) {
|
|
17908
|
+
const id = [
|
|
17909
|
+
tableRef,
|
|
17910
|
+
useLowerCaseColumnName ? columnName.toLowerCase() : columnName
|
|
17911
|
+
]
|
|
17912
|
+
.filter((item) => item)
|
|
17913
|
+
.join(".");
|
|
17914
|
+
return escapeIdentifier(id);
|
|
17915
|
+
}
|
|
17916
|
+
exports.getTableColumnName = getTableColumnName;
|
|
17917
|
+
/**
|
|
17918
|
+
* Create a record for given table with given data object.
|
|
17919
|
+
* This method will use the key / value pairs of the object as column name / value of the new record.
|
|
17920
|
+
* It will return the newly created record
|
|
17921
|
+
*
|
|
17922
|
+
* @export
|
|
17923
|
+
* @param {pg.Client | pg.Pool} poolOrClient
|
|
17924
|
+
* @param {string} table
|
|
17925
|
+
* @param {{ [key: string]: Value }} data
|
|
17926
|
+
* @return {*}
|
|
17927
|
+
*/
|
|
17928
|
+
function createTableRecord(poolOrClient, table, data, allowFieldList, autoGenerateUuid = true) {
|
|
17929
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17930
|
+
if (!table.trim()) {
|
|
17931
|
+
throw new Error("invalid empty table name is supplied.");
|
|
17932
|
+
}
|
|
17933
|
+
if (allowFieldList === null || allowFieldList === void 0 ? void 0 : allowFieldList.length) {
|
|
17934
|
+
const keys = Object.keys(data);
|
|
17935
|
+
const diff = lodash_1.difference(keys, allowFieldList);
|
|
17936
|
+
if (diff === null || diff === void 0 ? void 0 : diff.length) {
|
|
17937
|
+
throw new ServerError_1.default(`Failed to create record, the following fields are not allowed: ${diff.join(",")}`, 400);
|
|
17938
|
+
}
|
|
17939
|
+
}
|
|
17940
|
+
if (autoGenerateUuid) {
|
|
17941
|
+
data["id"] = sql_syntax_1.sqls `uuid_generate_v4()`;
|
|
17942
|
+
}
|
|
17943
|
+
const [fieldList, valueList] = Object.keys(data).reduce((result, currentKey) => {
|
|
17944
|
+
const currentValue = data[currentKey];
|
|
17945
|
+
result[0].push(escapeIdentifier(currentKey));
|
|
17946
|
+
result[1].push(sql_syntax_1.sqls `${currentValue}`);
|
|
17947
|
+
return result;
|
|
17948
|
+
}, [[], []]);
|
|
17949
|
+
const sqlSyntax = sql_syntax_1.sqls `INSERT INTO ${escapeIdentifier(table)}
|
|
17950
|
+
(${sql_syntax_1.default.csv(...fieldList)})
|
|
17951
|
+
VALUES
|
|
17952
|
+
(${sql_syntax_1.default.csv(...valueList)})
|
|
17953
|
+
RETURNING *`;
|
|
17954
|
+
const result = yield poolOrClient.query(...sqlSyntax.toQuery());
|
|
17955
|
+
return result.rows[0];
|
|
17956
|
+
});
|
|
17957
|
+
}
|
|
17958
|
+
exports.createTableRecord = createTableRecord;
|
|
17959
|
+
function updateTableRecord(poolOrClient, table, id, data, allowFieldList) {
|
|
17960
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17961
|
+
if (!id.trim()) {
|
|
17962
|
+
throw new ServerError_1.default("Failed to delete the record: empty id was provided.", 400);
|
|
17963
|
+
}
|
|
17964
|
+
if (!table.trim()) {
|
|
17965
|
+
throw new ServerError_1.default("invalid empty table name is supplied.", 500);
|
|
17966
|
+
}
|
|
17967
|
+
if (allowFieldList === null || allowFieldList === void 0 ? void 0 : allowFieldList.length) {
|
|
17968
|
+
const keys = Object.keys(data);
|
|
17969
|
+
const diff = lodash_1.difference(keys, allowFieldList);
|
|
17970
|
+
if (diff === null || diff === void 0 ? void 0 : diff.length) {
|
|
17971
|
+
throw new ServerError_1.default(`Failed to update record, the following fields are not allowed: ${diff.join(",")}`, 400);
|
|
17972
|
+
}
|
|
17973
|
+
}
|
|
17974
|
+
const updates = Object.keys(data).reduce((result, currentKey) => {
|
|
17975
|
+
const currentValue = data[currentKey];
|
|
17976
|
+
result.push(sql_syntax_1.sqls `${escapeIdentifier(currentKey)} = ${sql_syntax_1.sqls `${currentValue}`}`);
|
|
17977
|
+
return result;
|
|
17978
|
+
}, []);
|
|
17979
|
+
const sqlSyntax = sql_syntax_1.sqls `UPDATE ${escapeIdentifier(table)}
|
|
17980
|
+
SET ${sql_syntax_1.default.csv(...updates)}
|
|
17981
|
+
WHERE id = ${id}
|
|
17982
|
+
RETURNING *`;
|
|
17983
|
+
const result = yield poolOrClient.query(...sqlSyntax.toQuery());
|
|
17984
|
+
return result.rows[0];
|
|
17985
|
+
});
|
|
17986
|
+
}
|
|
17987
|
+
exports.updateTableRecord = updateTableRecord;
|
|
17988
|
+
function deleteTableRecord(poolOrClient, table, id) {
|
|
17989
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17990
|
+
if (!id.trim()) {
|
|
17991
|
+
throw new ServerError_1.default("Failed to delete the record: empty id was provided.", 400);
|
|
17992
|
+
}
|
|
17993
|
+
if (!table.trim()) {
|
|
17994
|
+
throw new ServerError_1.default("invalid empty table name is supplied.", 500);
|
|
17995
|
+
}
|
|
17996
|
+
const sqlSyntax = sql_syntax_1.sqls `DELETE FROM ${escapeIdentifier(table)} WHERE id = ${id}`;
|
|
17997
|
+
yield poolOrClient.query(...sqlSyntax.toQuery());
|
|
17998
|
+
});
|
|
17999
|
+
}
|
|
18000
|
+
exports.deleteTableRecord = deleteTableRecord;
|
|
18001
|
+
function parseIntParam(p) {
|
|
18002
|
+
if (!p) {
|
|
18003
|
+
return 0;
|
|
18004
|
+
}
|
|
18005
|
+
const result = parseInt(p === null || p === void 0 ? void 0 : p.toString());
|
|
18006
|
+
if (isNaN(result)) {
|
|
18007
|
+
return 0;
|
|
18008
|
+
}
|
|
18009
|
+
return result;
|
|
18010
|
+
}
|
|
18011
|
+
exports.parseIntParam = parseIntParam;
|
|
18012
|
+
exports.MAX_PAGE_RECORD_NUMBER = 500;
|
|
18013
|
+
function searchTableRecord(poolOrClient, table, contiditions = [], queryConfig) {
|
|
18014
|
+
var _a, _b, _c;
|
|
18015
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18016
|
+
if (!table.trim()) {
|
|
18017
|
+
throw new ServerError_1.default("invalid empty table name is supplied.");
|
|
18018
|
+
}
|
|
18019
|
+
const objectKind = (queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.objectKind) ? queryConfig.objectKind
|
|
18020
|
+
: "authObject";
|
|
18021
|
+
const authDecision = (queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.authDecision) ? queryConfig.authDecision
|
|
18022
|
+
: AuthDecision_1.UnconditionalTrueDecision;
|
|
18023
|
+
let limit = parseIntParam(queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.limit);
|
|
18024
|
+
const offset = parseIntParam(queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.offset);
|
|
18025
|
+
if (limit > exports.MAX_PAGE_RECORD_NUMBER) {
|
|
18026
|
+
limit = exports.MAX_PAGE_RECORD_NUMBER;
|
|
18027
|
+
}
|
|
18028
|
+
const config = (queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.toSqlConfig) ? queryConfig.toSqlConfig
|
|
18029
|
+
: {
|
|
18030
|
+
prefixes: [
|
|
18031
|
+
`input.${objectKind}.${lodash_1.camelCase(table.replace(/s$/, ""))}`
|
|
18032
|
+
]
|
|
18033
|
+
};
|
|
18034
|
+
const authConditions = authDecision.toSql(config);
|
|
18035
|
+
const where = sql_syntax_1.default.where(sql_syntax_1.default.joinWithAnd([...contiditions, authConditions]));
|
|
18036
|
+
const sqlSyntax = sql_syntax_1.sqls `SELECT ${(queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.selectedFields) ? sql_syntax_1.default.csv(...queryConfig.selectedFields)
|
|
18037
|
+
: sql_syntax_1.sqls `*`}
|
|
18038
|
+
FROM ${escapeIdentifier(table)}
|
|
18039
|
+
${((_a = queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.leftJoins) === null || _a === void 0 ? void 0 : _a.length) ? sql_syntax_1.default.join(queryConfig.leftJoins.map((joinItem) => sql_syntax_1.sqls `LEFT JOIN ${escapeIdentifier(joinItem.table)} ON ${joinItem.joinCondition}`), sql_syntax_1.sqls `\n`)
|
|
18040
|
+
: sql_syntax_1.default.empty}
|
|
18041
|
+
${where}
|
|
18042
|
+
${(queryConfig === null || queryConfig === void 0 ? void 0 : queryConfig.groupBy) ? sql_syntax_1.sqls `GROUP BY ${typeof ((_b = queryConfig.groupBy) === null || _b === void 0 ? void 0 : _b.length) === "number"
|
|
18043
|
+
? sql_syntax_1.default.csv(...queryConfig.groupBy)
|
|
18044
|
+
: queryConfig.groupBy}`
|
|
18045
|
+
: sql_syntax_1.default.empty}
|
|
18046
|
+
${offset ? sql_syntax_1.sqls `OFFSET ${offset}` : sql_syntax_1.default.empty}
|
|
18047
|
+
${limit ? sql_syntax_1.sqls `LIMIT ${limit}` : sql_syntax_1.default.empty}
|
|
18048
|
+
`;
|
|
18049
|
+
const result = yield poolOrClient.query(...sqlSyntax.toQuery());
|
|
18050
|
+
if (!((_c = result === null || result === void 0 ? void 0 : result.rows) === null || _c === void 0 ? void 0 : _c.length)) {
|
|
18051
|
+
return [];
|
|
18052
|
+
}
|
|
18053
|
+
else {
|
|
18054
|
+
return result.rows;
|
|
18055
|
+
}
|
|
18056
|
+
});
|
|
18057
|
+
}
|
|
18058
|
+
exports.searchTableRecord = searchTableRecord;
|
|
18059
|
+
function getTableRecord(poolOrClient, table, id, authDecision = AuthDecision_1.UnconditionalTrueDecision, objectKind = "authObject", toSqlConfig) {
|
|
18060
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18061
|
+
const records = yield searchTableRecord(poolOrClient, table, [sql_syntax_1.sqls `id = ${id}`], {
|
|
18062
|
+
authDecision,
|
|
18063
|
+
objectKind,
|
|
18064
|
+
toSqlConfig
|
|
18065
|
+
});
|
|
18066
|
+
if (!records.length) {
|
|
18067
|
+
return null;
|
|
18068
|
+
}
|
|
18069
|
+
else {
|
|
18070
|
+
return records[0];
|
|
18071
|
+
}
|
|
18072
|
+
});
|
|
18073
|
+
}
|
|
18074
|
+
exports.getTableRecord = getTableRecord;
|
|
18075
|
+
function countTableRecord(poolOrClient, table, contiditions = [], authDecision, objectKind, toSqlConfig) {
|
|
18076
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18077
|
+
const records = yield searchTableRecord(poolOrClient, table, contiditions, {
|
|
18078
|
+
authDecision,
|
|
18079
|
+
objectKind,
|
|
18080
|
+
toSqlConfig,
|
|
18081
|
+
selectedFields: [sql_syntax_1.sqls `COUNT(*) AS total`]
|
|
18082
|
+
});
|
|
18083
|
+
if (!records.length) {
|
|
18084
|
+
return 0;
|
|
18085
|
+
}
|
|
18086
|
+
else {
|
|
18087
|
+
return records[0]["total"];
|
|
18088
|
+
}
|
|
18089
|
+
});
|
|
18090
|
+
}
|
|
18091
|
+
exports.countTableRecord = countTableRecord;
|
|
18092
|
+
//# sourceMappingURL=SQLUtils.js.map
|
|
18093
|
+
|
|
18094
|
+
/***/ }),
|
|
18095
|
+
/* 11 */
|
|
18096
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
18097
|
+
|
|
18098
|
+
"use strict";
|
|
18099
|
+
|
|
18100
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18101
|
+
if (k2 === undefined) k2 = k;
|
|
18102
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
18103
|
+
}) : (function(o, m, k, k2) {
|
|
18104
|
+
if (k2 === undefined) k2 = k;
|
|
18105
|
+
o[k2] = m[k];
|
|
18106
|
+
}));
|
|
18107
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18108
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18109
|
+
}) : function(o, v) {
|
|
18110
|
+
o["default"] = v;
|
|
18111
|
+
});
|
|
18112
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
18113
|
+
if (mod && mod.__esModule) return mod;
|
|
18114
|
+
var result = {};
|
|
18115
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18116
|
+
__setModuleDefault(result, mod);
|
|
18117
|
+
return result;
|
|
18118
|
+
};
|
|
18119
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18120
|
+
exports.ConciseOperand = exports.ConciseExpression = exports.ConciseRule = exports.UnconditionalFalseDecision = exports.UnconditionalTrueDecision = exports.isTrueEquivalent = void 0;
|
|
18121
|
+
const AspectQuery_1 = __webpack_require__(16);
|
|
18122
|
+
const sql_syntax_1 = __importStar(__webpack_require__(7));
|
|
18123
|
+
class AuthDecision {
|
|
18124
|
+
constructor(hasResidualRules, residualRules, result, hasWarns = false, warns = [], unknowns) {
|
|
18125
|
+
if (typeof hasResidualRules !== "boolean") {
|
|
18126
|
+
throw new Error("Failed to create AuthDecision: invalid hasResidualRules type");
|
|
18127
|
+
}
|
|
18128
|
+
if (hasResidualRules && !(residualRules === null || residualRules === void 0 ? void 0 : residualRules.length)) {
|
|
18129
|
+
throw new Error("Failed to create AuthDecision: residualRules must have at least one item when hasResidualRules == true");
|
|
18130
|
+
}
|
|
18131
|
+
this.hasResidualRules = hasResidualRules;
|
|
18132
|
+
this.result = result;
|
|
18133
|
+
this.residualRules = residualRules;
|
|
18134
|
+
this.hasWarns = hasWarns;
|
|
18135
|
+
this.warns = warns;
|
|
18136
|
+
this.unknowns = unknowns;
|
|
18137
|
+
}
|
|
18138
|
+
static fromJson(data) {
|
|
18139
|
+
var _a;
|
|
18140
|
+
return new AuthDecision(data === null || data === void 0 ? void 0 : data.hasResidualRules, (_a = data === null || data === void 0 ? void 0 : data.residualRules) === null || _a === void 0 ? void 0 : _a.map((item) => ConciseRule.fromJson(item)), data === null || data === void 0 ? void 0 : data.result, (data === null || data === void 0 ? void 0 : data.hasWarns) ? true : false, data === null || data === void 0 ? void 0 : data.warns, data === null || data === void 0 ? void 0 : data.unknowns);
|
|
18141
|
+
}
|
|
18142
|
+
toAspectQueryGroups(prefixes) {
|
|
18143
|
+
if (this.hasResidualRules) {
|
|
18144
|
+
return this.residualRules.map((item) => item.toAspectQueryGroup(prefixes));
|
|
18145
|
+
}
|
|
18146
|
+
else {
|
|
18147
|
+
if (isTrueEquivalent(this.result)) {
|
|
18148
|
+
// unconditional true
|
|
18149
|
+
return [new AspectQuery_1.AspectQueryGroup([new AspectQuery_1.AspectQueryTrue()])];
|
|
18150
|
+
}
|
|
18151
|
+
else {
|
|
18152
|
+
return [new AspectQuery_1.AspectQueryGroup([new AspectQuery_1.AspectQueryFalse()])];
|
|
18153
|
+
}
|
|
18154
|
+
}
|
|
18155
|
+
}
|
|
18156
|
+
toSql(config) {
|
|
18157
|
+
return sql_syntax_1.default.joinWithOr(this.toAspectQueryGroups(config.prefixes).map((item) => item.toSql(config))).roundBracket();
|
|
18158
|
+
}
|
|
18159
|
+
}
|
|
18160
|
+
exports.default = AuthDecision;
|
|
18161
|
+
function isTrueEquivalent(value) {
|
|
18162
|
+
const typeStr = typeof value;
|
|
18163
|
+
if (typeStr === "boolean") {
|
|
18164
|
+
return value;
|
|
18165
|
+
}
|
|
18166
|
+
else if (typeStr === "undefined") {
|
|
18167
|
+
return false;
|
|
18168
|
+
}
|
|
18169
|
+
else if (typeof (value === null || value === void 0 ? void 0 : value.length) !== "undefined") {
|
|
18170
|
+
return !!value.length;
|
|
18171
|
+
}
|
|
18172
|
+
else {
|
|
18173
|
+
return !!value;
|
|
18174
|
+
}
|
|
18175
|
+
}
|
|
18176
|
+
exports.isTrueEquivalent = isTrueEquivalent;
|
|
18177
|
+
exports.UnconditionalTrueDecision = new AuthDecision(false, undefined, true);
|
|
18178
|
+
exports.UnconditionalFalseDecision = new AuthDecision(false, undefined, false);
|
|
18179
|
+
class ConciseRule {
|
|
18180
|
+
constructor(fullName, name, value, expressions, isDefault = false) {
|
|
18181
|
+
if (!isDefault && !(expressions === null || expressions === void 0 ? void 0 : expressions.length)) {
|
|
18182
|
+
throw new Error("Invalid ConciseRule data: it must contain at least one ConciseExpression item unless it's a default rule.");
|
|
18183
|
+
}
|
|
18184
|
+
this.default = isDefault ? true : false;
|
|
18185
|
+
this.fullName = fullName;
|
|
18186
|
+
this.name = name;
|
|
18187
|
+
this.value = value;
|
|
18188
|
+
this.expressions = (expressions === null || expressions === void 0 ? void 0 : expressions.length) ? expressions : [];
|
|
18189
|
+
}
|
|
18190
|
+
static fromJson(data) {
|
|
18191
|
+
var _a;
|
|
18192
|
+
return new ConciseRule(data === null || data === void 0 ? void 0 : data.fullName, data === null || data === void 0 ? void 0 : data.name, data === null || data === void 0 ? void 0 : data.value, (_a = data === null || data === void 0 ? void 0 : data.expressions) === null || _a === void 0 ? void 0 : _a.map((item) => ConciseExpression.fromJson(item)));
|
|
18193
|
+
}
|
|
18194
|
+
toAspectQueryGroup(prefixes) {
|
|
18195
|
+
var _a;
|
|
18196
|
+
return new AspectQuery_1.AspectQueryGroup((_a = this.expressions) === null || _a === void 0 ? void 0 : _a.map((item) => item.toAspectQuery(prefixes)), true, !isTrueEquivalent(this.value));
|
|
18197
|
+
}
|
|
18198
|
+
}
|
|
18199
|
+
exports.ConciseRule = ConciseRule;
|
|
18200
|
+
class ConciseExpression {
|
|
18201
|
+
constructor(operands, operator, negated = false) {
|
|
18202
|
+
var _a, _b;
|
|
18203
|
+
this.negated = negated ? true : false;
|
|
18204
|
+
this.operands = operands;
|
|
18205
|
+
this.operator = operator;
|
|
18206
|
+
if (!((_a = this.operands) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
18207
|
+
throw new Error("invalid ConciseExpression data: it must have at least one operand.");
|
|
18208
|
+
}
|
|
18209
|
+
if (((_b = this.operands) === null || _b === void 0 ? void 0 : _b.length) > 1 && typeof operator !== "string") {
|
|
18210
|
+
throw new Error("invalid ConciseExpression data: when operands number > 1, operator must be a valid string value.");
|
|
18211
|
+
}
|
|
18212
|
+
}
|
|
18213
|
+
static fromJson(data) {
|
|
18214
|
+
var _a;
|
|
18215
|
+
return new ConciseExpression((_a = data === null || data === void 0 ? void 0 : data.operands) === null || _a === void 0 ? void 0 : _a.map((item) => ConciseOperand.fromJson(item)), data === null || data === void 0 ? void 0 : data.operator, data === null || data === void 0 ? void 0 : data.negated);
|
|
18216
|
+
}
|
|
18217
|
+
getSqlOperator() {
|
|
18218
|
+
switch (this.operator) {
|
|
18219
|
+
case "=":
|
|
18220
|
+
return sql_syntax_1.sqls `=`;
|
|
18221
|
+
case ">":
|
|
18222
|
+
return sql_syntax_1.sqls `>`;
|
|
18223
|
+
case "<":
|
|
18224
|
+
return sql_syntax_1.sqls `<`;
|
|
18225
|
+
case ">=":
|
|
18226
|
+
return sql_syntax_1.sqls `>=`;
|
|
18227
|
+
case "<=":
|
|
18228
|
+
return sql_syntax_1.sqls `<=`;
|
|
18229
|
+
default:
|
|
18230
|
+
throw new Error(`Failed to convert auth decision operator to SQL operator: unsupported operator: ${this.operator}`);
|
|
18231
|
+
}
|
|
18232
|
+
}
|
|
18233
|
+
toAspectQuery(prefixes) {
|
|
18234
|
+
var _a, _b;
|
|
18235
|
+
if (((_a = this.operands) === null || _a === void 0 ? void 0 : _a.length) == 1) {
|
|
18236
|
+
const [aspectId, path, isCollection] = this.operands[0].extractAspectIdAndPath(prefixes);
|
|
18237
|
+
if (isCollection) {
|
|
18238
|
+
return new AspectQuery_1.AspectQueryArrayNotEmpty(aspectId, path, this.negated);
|
|
18239
|
+
}
|
|
18240
|
+
else {
|
|
18241
|
+
return new AspectQuery_1.AspectQueryExists(aspectId, path, this.negated);
|
|
18242
|
+
}
|
|
18243
|
+
}
|
|
18244
|
+
else if (((_b = this.operands) === null || _b === void 0 ? void 0 : _b.length) == 2) {
|
|
18245
|
+
const refOperand = this.operands.find((item) => item.isRef);
|
|
18246
|
+
const valOperand = this.operands.find((item) => !item.isRef);
|
|
18247
|
+
if (!valOperand) {
|
|
18248
|
+
throw new Error("Failed to convert auth decision expression to AspectQuery: " +
|
|
18249
|
+
`expression with both terms are references is currently not supported. Expression: ${this}`);
|
|
18250
|
+
}
|
|
18251
|
+
if (!refOperand) {
|
|
18252
|
+
// it's unlikely both terms are values as our decision API has already done the evaluation for this case.
|
|
18253
|
+
throw new Error("Failed to convert auth decision expression to AspectQuery: " +
|
|
18254
|
+
`Terms shouldn't be both value. Expression: ${this}`);
|
|
18255
|
+
}
|
|
18256
|
+
const [aspectId, path, isCollection] = refOperand.extractAspectIdAndPath(prefixes);
|
|
18257
|
+
if (isCollection && this.operator != "=") {
|
|
18258
|
+
throw new Error("Failed to convert auth decision expression to AspectQuery: " +
|
|
18259
|
+
`Only \`=\` operator is supported for collection reference. Expression: ${this}`);
|
|
18260
|
+
}
|
|
18261
|
+
if (isCollection && this.operator == "=") {
|
|
18262
|
+
return new AspectQuery_1.AspectQueryValueInArray(valOperand.toAspectQueryValue(), aspectId, path, this.negated);
|
|
18263
|
+
}
|
|
18264
|
+
else {
|
|
18265
|
+
return new AspectQuery_1.AspectQueryWithValue(valOperand.toAspectQueryValue(), this.getSqlOperator(), this.operands[0].isRef, aspectId, path, this.negated);
|
|
18266
|
+
}
|
|
18267
|
+
}
|
|
18268
|
+
else {
|
|
18269
|
+
throw new Error(`Failed to convert auth decision expression to AspectQuery: more than 2 operands found. Expression: ${this}`);
|
|
18270
|
+
}
|
|
18271
|
+
}
|
|
18272
|
+
}
|
|
18273
|
+
exports.ConciseExpression = ConciseExpression;
|
|
18274
|
+
class ConciseOperand {
|
|
18275
|
+
constructor(isRef, value) {
|
|
18276
|
+
if (typeof isRef !== "boolean") {
|
|
18277
|
+
throw new Error("Invalid ConciseOperand data: isRef must be a boolean value.");
|
|
18278
|
+
}
|
|
18279
|
+
this.isRef = isRef;
|
|
18280
|
+
if (isRef && typeof value !== "string") {
|
|
18281
|
+
throw new Error("Invalid ConciseOperand data: when `isRef`== true, `value` must be a string value (ref string).");
|
|
18282
|
+
}
|
|
18283
|
+
this.value = value;
|
|
18284
|
+
}
|
|
18285
|
+
static fromJson(data) {
|
|
18286
|
+
return new ConciseOperand(data === null || data === void 0 ? void 0 : data.isRef, data === null || data === void 0 ? void 0 : data.value);
|
|
18287
|
+
}
|
|
18288
|
+
refString() {
|
|
18289
|
+
if (!this.isRef) {
|
|
18290
|
+
throw new Error("Cannot convert non-ref term to a ref string");
|
|
18291
|
+
}
|
|
18292
|
+
else {
|
|
18293
|
+
if (typeof this.value === "string") {
|
|
18294
|
+
return this.value;
|
|
18295
|
+
}
|
|
18296
|
+
else {
|
|
18297
|
+
throw new Error(`ref term has non-string type value: ${this.value}`);
|
|
18298
|
+
}
|
|
18299
|
+
}
|
|
18300
|
+
}
|
|
18301
|
+
isCollectionRef() {
|
|
18302
|
+
return this.refString().endsWith("[_]");
|
|
18303
|
+
}
|
|
18304
|
+
refStringWithoutPrefixes(prefixes) {
|
|
18305
|
+
const sortedPrefixes = prefixes.sort((a, b) => b.length - a.length);
|
|
18306
|
+
return sortedPrefixes.reduce((ref, prefix) => {
|
|
18307
|
+
if (ref.startsWith(prefix)) {
|
|
18308
|
+
return ref.substring(prefix.length);
|
|
18309
|
+
}
|
|
18310
|
+
else {
|
|
18311
|
+
return ref;
|
|
18312
|
+
}
|
|
18313
|
+
}, this.refString());
|
|
18314
|
+
}
|
|
18315
|
+
extractAspectIdAndPath(prefixes) {
|
|
18316
|
+
// make it work for both "input.object.record" & "input.object.record." prefixe input
|
|
18317
|
+
// we remove the first leading `.` char (if any)
|
|
18318
|
+
let ref = this.refStringWithoutPrefixes(prefixes).replace(/^\./, "");
|
|
18319
|
+
const isCollection = this.isCollectionRef();
|
|
18320
|
+
if (isCollection) {
|
|
18321
|
+
ref = ref.replace(/\[_\]$/, "");
|
|
18322
|
+
}
|
|
18323
|
+
const parts = ref.split(".").filter((item) => item);
|
|
18324
|
+
if (parts.length < 2) {
|
|
18325
|
+
return [ref, [], isCollection];
|
|
18326
|
+
}
|
|
18327
|
+
else {
|
|
18328
|
+
return [parts[0], parts.slice(1, parts.length), isCollection];
|
|
18329
|
+
}
|
|
18330
|
+
}
|
|
18331
|
+
toAspectQueryValue() {
|
|
18332
|
+
if (this.isRef) {
|
|
18333
|
+
throw new Error(`Attempt to covert reference \`Operand\` to \`AspectQueryValue\`: ${this}`);
|
|
18334
|
+
}
|
|
18335
|
+
return new AspectQuery_1.AspectQueryValue(this.value);
|
|
18336
|
+
}
|
|
18337
|
+
}
|
|
18338
|
+
exports.ConciseOperand = ConciseOperand;
|
|
18339
|
+
//# sourceMappingURL=AuthDecision.js.map
|
|
18340
|
+
|
|
18341
|
+
/***/ }),
|
|
18342
|
+
/* 12 */,
|
|
18343
|
+
/* 13 */
|
|
18344
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
18345
|
+
|
|
18346
|
+
"use strict";
|
|
18347
|
+
|
|
18348
|
+
var __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {
|
|
18349
|
+
if (k2 === undefined) k2 = k;
|
|
18350
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function () {return m[k];} });
|
|
18351
|
+
} : function (o, m, k, k2) {
|
|
18352
|
+
if (k2 === undefined) k2 = k;
|
|
18353
|
+
o[k2] = m[k];
|
|
18354
|
+
});
|
|
18355
|
+
var __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (o, v) {
|
|
18356
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18357
|
+
} : function (o, v) {
|
|
18358
|
+
o["default"] = v;
|
|
18359
|
+
});
|
|
18360
|
+
var __importStar = this && this.__importStar || function (mod) {
|
|
18361
|
+
if (mod && mod.__esModule) return mod;
|
|
18362
|
+
var result = {};
|
|
18363
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18364
|
+
__setModuleDefault(result, mod);
|
|
18365
|
+
return result;
|
|
18366
|
+
};
|
|
18367
|
+
var __awaiter = this && this.__awaiter || function (thisArg, _arguments, P, generator) {
|
|
18368
|
+
function adopt(value) {return value instanceof P ? value : new P(function (resolve) {resolve(value);});}
|
|
18369
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
18370
|
+
function fulfilled(value) {try {step(generator.next(value));} catch (e) {reject(e);}}
|
|
18371
|
+
function rejected(value) {try {step(generator["throw"](value));} catch (e) {reject(e);}}
|
|
18372
|
+
function step(result) {result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);}
|
|
18373
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18374
|
+
});
|
|
18375
|
+
};
|
|
18376
|
+
var __importDefault = this && this.__importDefault || function (mod) {
|
|
18377
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
18378
|
+
};
|
|
18379
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18380
|
+
exports.NodeNotFoundError = void 0;
|
|
18381
|
+
const lodash_1 = __importDefault(__webpack_require__(9));
|
|
18382
|
+
const tsmonad_1 = __webpack_require__(15);
|
|
18383
|
+
const sql_syntax_1 = __importStar(__webpack_require__(7));
|
|
18384
|
+
const SQLUtils_1 = __webpack_require__(10);
|
|
18385
|
+
const AuthDecision_1 = __webpack_require__(11);
|
|
18386
|
+
const isUuid_1 = __importDefault(__webpack_require__(6));
|
|
18387
|
+
const textTree = __webpack_require__(18);
|
|
18388
|
+
class NodeNotFoundError extends Error {}
|
|
18389
|
+
|
|
18390
|
+
exports.NodeNotFoundError = NodeNotFoundError;
|
|
18391
|
+
function isNonEmptyArray(v) {
|
|
18392
|
+
if (!v || !lodash_1.default.isArray(v) || !v.length)
|
|
18393
|
+
return false;
|
|
18394
|
+
return true;
|
|
18395
|
+
}
|
|
18396
|
+
const INVALID_CHAR_REGEX = /[^a-z_\d]/i;
|
|
18397
|
+
function isValidSqlIdentifier(id) {
|
|
18398
|
+
if (INVALID_CHAR_REGEX.test(id))
|
|
18399
|
+
return false;
|
|
18400
|
+
return true;
|
|
18401
|
+
}
|
|
18402
|
+
class NestedSetModelQueryer {
|
|
18403
|
+
/**
|
|
18404
|
+
* Creates an instance of NestedSetModelQueryer.
|
|
18405
|
+
* @param {pg.Pool} dbPool
|
|
18406
|
+
* @param {string} tableName
|
|
18407
|
+
* @param {string[]} [defaultSelectFieldList=null] default select fields; If null, all fields (i.e. `SELECT "id", "name"`) will be returned
|
|
18408
|
+
* @memberof NestedSetModelQueryer
|
|
18409
|
+
*/
|
|
18410
|
+
constructor(dbPool, tableName, defaultSelectFieldList = null, defaultInsertFieldList = null) {
|
|
18411
|
+
/**
|
|
18412
|
+
* default select fields if [], all fields (i.e. `SELECT "id", "name"`) will be returned
|
|
18413
|
+
*
|
|
18414
|
+
* @type {string[]}
|
|
18415
|
+
* @memberof NestedSetModelQueryer
|
|
18416
|
+
*/
|
|
18417
|
+
this.defaultSelectFieldList = ["id", "name"];
|
|
18418
|
+
/**
|
|
18419
|
+
* Default field list that will be used when insert nodes into tree.
|
|
18420
|
+
* By default, only `name` field will be saved to database
|
|
18421
|
+
* e.g. If your tree nodes have three properties (besides `id`, `left`, `right` --- they auto generated):
|
|
18422
|
+
* - name
|
|
18423
|
+
* - description
|
|
18424
|
+
* - fullName
|
|
18425
|
+
*
|
|
18426
|
+
* Then you should set `defaultInsertFieldList` to ["name", "description", "fullName"]
|
|
18427
|
+
*
|
|
18428
|
+
* @type {string[]}
|
|
18429
|
+
* @memberof NestedSetModelQueryer
|
|
18430
|
+
*/
|
|
18431
|
+
this.defaultInsertFieldList = ["name"];
|
|
18432
|
+
if (!dbPool)
|
|
18433
|
+
throw new Error("dbPool cannot be empty!");
|
|
18434
|
+
if (!tableName)
|
|
18435
|
+
throw new Error("tableName cannot be empty!");
|
|
18436
|
+
if (!isValidSqlIdentifier(tableName)) {
|
|
18437
|
+
throw new Error(`tableName: ${tableName} contains invalid characters!`);
|
|
18438
|
+
}
|
|
18439
|
+
this.pool = dbPool;
|
|
18440
|
+
this.tableName = tableName;
|
|
18441
|
+
if (defaultSelectFieldList) {
|
|
18442
|
+
if (!lodash_1.default.isArray(defaultSelectFieldList))
|
|
18443
|
+
throw new Error("defaultSelectFieldList should be an array");
|
|
18444
|
+
this.defaultSelectFieldList = defaultSelectFieldList;
|
|
18445
|
+
}
|
|
18446
|
+
if (defaultSelectFieldList) {
|
|
18447
|
+
if (!lodash_1.default.isArray(defaultSelectFieldList))
|
|
18448
|
+
throw new Error("defaultSelectFieldList should be an array");
|
|
18449
|
+
this.defaultSelectFieldList = defaultSelectFieldList;
|
|
18450
|
+
}
|
|
18451
|
+
if (defaultInsertFieldList) {
|
|
18452
|
+
if (!lodash_1.default.isArray(defaultInsertFieldList))
|
|
18453
|
+
throw new Error("defaultInsertFieldList should be an array");
|
|
18454
|
+
this.defaultInsertFieldList = defaultInsertFieldList;
|
|
18455
|
+
}
|
|
18456
|
+
}
|
|
18457
|
+
selectFields(tableAliasOrName = "", fields = null) {
|
|
18458
|
+
const fieldList = isNonEmptyArray(fields) ?
|
|
18459
|
+
fields :
|
|
18460
|
+
this.defaultSelectFieldList;
|
|
18461
|
+
if (!isNonEmptyArray(fieldList)) {
|
|
18462
|
+
return sql_syntax_1.sqls`*`;
|
|
18463
|
+
}
|
|
18464
|
+
if (!isValidSqlIdentifier(tableAliasOrName)) {
|
|
18465
|
+
throw new Error(`'tableAliasOrName' ${tableAliasOrName} contains invalid characters.`);
|
|
18466
|
+
}
|
|
18467
|
+
// --- do not double quote `tableAliasOrName`
|
|
18468
|
+
// --- or you will get missing FROM-clause entry for table error
|
|
18469
|
+
return sql_syntax_1.default.createUnsafely(fieldList.
|
|
18470
|
+
map(f => {
|
|
18471
|
+
if (!isValidSqlIdentifier(f)) {
|
|
18472
|
+
throw new Error(`Field name ${f} contains invalid characters.`);
|
|
18473
|
+
}
|
|
18474
|
+
return tableAliasOrName === "" ?
|
|
18475
|
+
`"${f}"` :
|
|
18476
|
+
`${tableAliasOrName}."${f}"`;
|
|
18477
|
+
}).
|
|
18478
|
+
join(", "));
|
|
18479
|
+
}
|
|
18480
|
+
/**
|
|
18481
|
+
* Get nodes by name
|
|
18482
|
+
* You hardly need this one --- only for write test case (you can get a id from name)
|
|
18483
|
+
*
|
|
18484
|
+
* @param {string} name
|
|
18485
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18486
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18487
|
+
* @returns {Promise<NodeRecord[]>}
|
|
18488
|
+
* @memberof NestedSetModelQueryer
|
|
18489
|
+
*/
|
|
18490
|
+
getNodes(nodesQuery = {}, fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18491
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18492
|
+
const authConditions = authDecision.toSql({
|
|
18493
|
+
prefixes: ["input.authObject.orgUnit"] });
|
|
18494
|
+
|
|
18495
|
+
const clauses = [
|
|
18496
|
+
authConditions,
|
|
18497
|
+
nodesQuery.name ?
|
|
18498
|
+
sql_syntax_1.sqls`"name" = ${nodesQuery.name}` :
|
|
18499
|
+
sql_syntax_1.default.empty,
|
|
18500
|
+
nodesQuery.leafNodesOnly ?
|
|
18501
|
+
sql_syntax_1.sqls`"left" = ( "right" - 1 )` :
|
|
18502
|
+
sql_syntax_1.default.empty];
|
|
18503
|
+
|
|
18504
|
+
const whereClause = sql_syntax_1.default.where(sql_syntax_1.default.joinWithAnd(clauses));
|
|
18505
|
+
const query = sql_syntax_1.sqls`SELECT ${this.selectFields("", fields)} FROM ${SQLUtils_1.escapeIdentifier(this.tableName)} ${whereClause}`;
|
|
18506
|
+
const result = yield (client ? client : this.pool).query(...query.toQuery());
|
|
18507
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18508
|
+
return [];
|
|
18509
|
+
return result.rows;
|
|
18510
|
+
});
|
|
18511
|
+
}
|
|
18512
|
+
/**
|
|
18513
|
+
*
|
|
18514
|
+
* Get a node by its id
|
|
18515
|
+
* @param {string} id
|
|
18516
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18517
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18518
|
+
* @returns {Promise<NodeRecord>}
|
|
18519
|
+
* @memberof NestedSetModelQueryer
|
|
18520
|
+
*/
|
|
18521
|
+
getNodeById(id, fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18522
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18523
|
+
const authConditions = authDecision.toSql({
|
|
18524
|
+
prefixes: ["input.authObject.orgUnit"] });
|
|
18525
|
+
|
|
18526
|
+
const result = yield (client ? client : this.pool).query(...sql_syntax_1.sqls`SELECT ${this.selectFields("", fields)} FROM ${SQLUtils_1.escapeIdentifier(this.tableName)} WHERE ${sql_syntax_1.default.joinWithAnd([
|
|
18527
|
+
sql_syntax_1.sqls`"id" = ${id}`,
|
|
18528
|
+
authConditions])
|
|
18529
|
+
}`.toQuery());
|
|
18530
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18531
|
+
return tsmonad_1.Maybe.nothing();
|
|
18532
|
+
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
18533
|
+
});
|
|
18534
|
+
}
|
|
18535
|
+
/**
|
|
18536
|
+
* Get the root node of the tree
|
|
18537
|
+
* Return null if empty tree or the user has no access to the root node
|
|
18538
|
+
*
|
|
18539
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18540
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18541
|
+
* @returns {Promise<NodeRecord>}
|
|
18542
|
+
* @memberof NestedSetModelQueryer
|
|
18543
|
+
*/
|
|
18544
|
+
getRootNode(fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18545
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18546
|
+
const authConditions = authDecision.toSql({
|
|
18547
|
+
prefixes: ["input.authObject.orgUnit"] });
|
|
18548
|
+
|
|
18549
|
+
const result = yield (client ? client : this.pool).query(...sql_syntax_1.sqls`SELECT ${this.selectFields("", fields)} FROM ${SQLUtils_1.escapeIdentifier(this.tableName)} WHERE ${sql_syntax_1.default.joinWithAnd([
|
|
18550
|
+
sql_syntax_1.sqls`"left" = 1`,
|
|
18551
|
+
authConditions])
|
|
18552
|
+
}`.toQuery());
|
|
18553
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18554
|
+
return tsmonad_1.Maybe.nothing();
|
|
18555
|
+
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
18556
|
+
});
|
|
18557
|
+
}
|
|
18558
|
+
/**
|
|
18559
|
+
* Get All children of a given node
|
|
18560
|
+
* (including immediate children and children of immediate children etc.)
|
|
18561
|
+
* If the node has no child (i.e. a leaf node), an empty array will be returned
|
|
18562
|
+
*
|
|
18563
|
+
* @param {string} parentNodeId
|
|
18564
|
+
* @param {boolean} [includeMyself=false]
|
|
18565
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18566
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18567
|
+
* @returns {Promise<NodeRecord[]>}
|
|
18568
|
+
* @memberof NestedSetModelQueryer
|
|
18569
|
+
*/
|
|
18570
|
+
getAllChildren(parentNodeId, includeMyself = false, fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18571
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18572
|
+
const authConditions = authDecision.toSql({
|
|
18573
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18574
|
+
tableRef: "children" });
|
|
18575
|
+
|
|
18576
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18577
|
+
const conditions = [
|
|
18578
|
+
sql_syntax_1.sqls`Children."left" ${includeMyself ? sql_syntax_1.sqls`>=` : sql_syntax_1.sqls`>`} Parents."left"`,
|
|
18579
|
+
sql_syntax_1.sqls`Children."left" ${includeMyself ? sql_syntax_1.sqls`<=` : sql_syntax_1.sqls`<`} Parents."right"`,
|
|
18580
|
+
sql_syntax_1.sqls`Parents."id" = ${parentNodeId}`,
|
|
18581
|
+
authConditions];
|
|
18582
|
+
|
|
18583
|
+
const result = yield (client ? client : this.pool).query(...sql_syntax_1.sqls`SELECT ${this.selectFields("Children", fields)}
|
|
18584
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18585
|
+
WHERE ${sql_syntax_1.default.joinWithAnd(conditions)}`.toQuery());
|
|
18586
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18587
|
+
return [];
|
|
18588
|
+
return result.rows;
|
|
18589
|
+
});
|
|
18590
|
+
}
|
|
18591
|
+
/**
|
|
18592
|
+
* Get All parents of a given node
|
|
18593
|
+
* (including immediate parent and parents of immediate parent etc.)
|
|
18594
|
+
* If the node has no parent (i.e. a root node), an empty array will be returned (unless `includeMyself` = true)
|
|
18595
|
+
*
|
|
18596
|
+
* @param {string} childNodeId
|
|
18597
|
+
* @param {boolean} [includeMyself=false]
|
|
18598
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18599
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18600
|
+
* @returns {Promise<NodeRecord[]>}
|
|
18601
|
+
* @memberof NestedSetModelQueryer
|
|
18602
|
+
*/
|
|
18603
|
+
getAllParents(childNodeId, includeMyself = false, fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18604
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18605
|
+
const authConditions = authDecision.toSql({
|
|
18606
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18607
|
+
tableRef: "parents" });
|
|
18608
|
+
|
|
18609
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18610
|
+
const conditions = [
|
|
18611
|
+
sql_syntax_1.sqls`Children."left" ${includeMyself ? sql_syntax_1.sqls`>=` : sql_syntax_1.sqls`>`} Parents."left"`,
|
|
18612
|
+
sql_syntax_1.sqls`Children."left" ${includeMyself ? sql_syntax_1.sqls`<=` : sql_syntax_1.sqls`<`} Parents."right"`,
|
|
18613
|
+
sql_syntax_1.sqls`Children."id" = ${childNodeId}`,
|
|
18614
|
+
authConditions];
|
|
18615
|
+
|
|
18616
|
+
const result = yield (client ? client : this.pool).query(...sql_syntax_1.sqls`SELECT ${this.selectFields("Parents", fields)}
|
|
18617
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18618
|
+
WHERE ${sql_syntax_1.default.joinWithAnd(conditions)}`.toQuery());
|
|
18619
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18620
|
+
return [];
|
|
18621
|
+
return result.rows;
|
|
18622
|
+
});
|
|
18623
|
+
}
|
|
18624
|
+
/**
|
|
18625
|
+
* Get Immediate Children of a Node
|
|
18626
|
+
* If the node has no child (i.e. a leaf node), an empty array will be returned
|
|
18627
|
+
*
|
|
18628
|
+
* @param {string} parentNodeId
|
|
18629
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18630
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18631
|
+
* @returns {Promise<NodeRecord[]>}
|
|
18632
|
+
* @memberof NestedSetModelQueryer
|
|
18633
|
+
*/
|
|
18634
|
+
getImmediateChildren(parentNodeId, fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18635
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18636
|
+
const authConditions = authDecision.toSql({
|
|
18637
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18638
|
+
tableRef: "children" });
|
|
18639
|
+
|
|
18640
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18641
|
+
const result = yield (client ? client : this.pool).query(...sql_syntax_1.sqls`SELECT ${this.selectFields("Children", fields)}
|
|
18642
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18643
|
+
WHERE Children."left" BETWEEN Parents."left" AND Parents."right"
|
|
18644
|
+
AND Parents."left" = (
|
|
18645
|
+
SELECT MAX(S."left") FROM ${tbl} AS S
|
|
18646
|
+
WHERE S."left" < Children."left" AND S."right" > Children."right"
|
|
18647
|
+
)
|
|
18648
|
+
AND Parents."id" = ${parentNodeId}
|
|
18649
|
+
${authConditions.isEmpty ?
|
|
18650
|
+
sql_syntax_1.default.empty :
|
|
18651
|
+
sql_syntax_1.sqls` AND ${authConditions}`}
|
|
18652
|
+
ORDER BY Children."left" ASC`.toQuery());
|
|
18653
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18654
|
+
return [];
|
|
18655
|
+
return result.rows;
|
|
18656
|
+
});
|
|
18657
|
+
}
|
|
18658
|
+
/**
|
|
18659
|
+
* Get Immediate Parent of a Node
|
|
18660
|
+
* If the node has no parent (i.e. a root node), null will be returned
|
|
18661
|
+
*
|
|
18662
|
+
* @param {string} childNodeId
|
|
18663
|
+
* @param {string[]} [fields=null] Selected Fields; If null, use this.defaultSelectFieldList
|
|
18664
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18665
|
+
* @returns {Promise<NodeRecord>}
|
|
18666
|
+
* @memberof NestedSetModelQueryer
|
|
18667
|
+
*/
|
|
18668
|
+
getImmediateParent(childNodeId, fields = null, client = null, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18669
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18670
|
+
const authConditions = authDecision.toSql({
|
|
18671
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18672
|
+
tableRef: "parents" });
|
|
18673
|
+
|
|
18674
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18675
|
+
const result = yield (client ? client : this.pool).query(...sql_syntax_1.sqls`SELECT ${this.selectFields("Parents", fields)}
|
|
18676
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18677
|
+
WHERE Children.left BETWEEN Parents.left AND Parents.right
|
|
18678
|
+
AND Parents.left = (
|
|
18679
|
+
SELECT MAX(S.left) FROM ${tbl} AS S
|
|
18680
|
+
WHERE S.left < Children.left AND S.right > Children.right
|
|
18681
|
+
)
|
|
18682
|
+
AND Children.id = ${childNodeId}
|
|
18683
|
+
${authConditions.isEmpty ?
|
|
18684
|
+
sql_syntax_1.default.empty :
|
|
18685
|
+
sql_syntax_1.sqls` AND ${authConditions}`}`.toQuery());
|
|
18686
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18687
|
+
return tsmonad_1.Maybe.nothing();
|
|
18688
|
+
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
18689
|
+
});
|
|
18690
|
+
}
|
|
18691
|
+
/**
|
|
18692
|
+
* Get all nodes at level n from top
|
|
18693
|
+
* e.g. get all nodes at level 3:
|
|
18694
|
+
* this.getAllNodesAtLevel(3)
|
|
18695
|
+
* Root node is at level 1
|
|
18696
|
+
*
|
|
18697
|
+
* @param {number} level
|
|
18698
|
+
* @returns {Promise<NodeRecord[]>}
|
|
18699
|
+
* @memberof NestedSetModelQueryer
|
|
18700
|
+
*/
|
|
18701
|
+
getAllNodesAtLevel(level, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18702
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18703
|
+
const authConditions = authDecision.toSql({
|
|
18704
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18705
|
+
tableRef: "t2" });
|
|
18706
|
+
|
|
18707
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18708
|
+
const result = yield this.pool.query(...sql_syntax_1.sqls`SELECT ${this.selectFields("t2")}
|
|
18709
|
+
FROM ${tbl} AS t1, ${tbl} AS t2
|
|
18710
|
+
WHERE t2.left BETWEEN t1.left AND t1.right
|
|
18711
|
+
${authConditions.isEmpty ?
|
|
18712
|
+
sql_syntax_1.default.empty :
|
|
18713
|
+
sql_syntax_1.sqls` AND ${authConditions}`}
|
|
18714
|
+
GROUP BY t2.id
|
|
18715
|
+
HAVING COUNT(t1.id) = ${level}`.toQuery());
|
|
18716
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18717
|
+
return [];
|
|
18718
|
+
return result.rows;
|
|
18719
|
+
});
|
|
18720
|
+
}
|
|
18721
|
+
/**
|
|
18722
|
+
* Get level no. of a given node
|
|
18723
|
+
* Starts from 1. i.e. The root node is 1
|
|
18724
|
+
*
|
|
18725
|
+
* @param {string} nodeId
|
|
18726
|
+
* @returns {Promise<number>}
|
|
18727
|
+
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
18728
|
+
* @memberof NestedSetModelQueryer
|
|
18729
|
+
*/
|
|
18730
|
+
getLevelOfNode(nodeId, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18731
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18732
|
+
const authConditions = authDecision.toSql({
|
|
18733
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18734
|
+
tableRef: "parents" });
|
|
18735
|
+
|
|
18736
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18737
|
+
const result = yield this.pool.query(...sql_syntax_1.sqls`SELECT COUNT(Parents.id) AS level
|
|
18738
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18739
|
+
WHERE Children.left BETWEEN Parents.left AND Parents.right AND Children.id = ${nodeId}
|
|
18740
|
+
${authConditions.isEmpty ?
|
|
18741
|
+
sql_syntax_1.default.empty :
|
|
18742
|
+
sql_syntax_1.sqls` AND ${authConditions}`}`.toQuery());
|
|
18743
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18744
|
+
throw new NodeNotFoundError();
|
|
18745
|
+
const level = parseInt(result.rows[0]["level"]);
|
|
18746
|
+
if (!lodash_1.default.isNumber(level) || lodash_1.default.isNaN(level) || level < 1)
|
|
18747
|
+
throw new Error(`Could find a valid level for node ${nodeId}: ${level}`);
|
|
18748
|
+
return level;
|
|
18749
|
+
});
|
|
18750
|
+
}
|
|
18751
|
+
/**
|
|
18752
|
+
* Get total height (no. of the levels) of the tree
|
|
18753
|
+
* Starts with 1 level.
|
|
18754
|
+
*
|
|
18755
|
+
* @returns {Promise<number>}
|
|
18756
|
+
* @throws NodeNotFoundError If the root node can't be found in the tree
|
|
18757
|
+
* @memberof NestedSetModelQueryer
|
|
18758
|
+
*/
|
|
18759
|
+
getTreeHeight(authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18760
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18761
|
+
const authConditions = authDecision.toSql({
|
|
18762
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18763
|
+
tableRef: "t1" });
|
|
18764
|
+
|
|
18765
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18766
|
+
const result = yield this.pool.query(...sql_syntax_1.sqls`SELECT MAX(level) AS height
|
|
18767
|
+
FROM(
|
|
18768
|
+
SELECT COUNT(t1.id)
|
|
18769
|
+
FROM ${tbl} AS t1, ${tbl} AS t2
|
|
18770
|
+
WHERE t2.left BETWEEN t1.left AND t1.right
|
|
18771
|
+
${authConditions.isEmpty ?
|
|
18772
|
+
sql_syntax_1.default.empty :
|
|
18773
|
+
sql_syntax_1.sqls` AND ${authConditions}`}
|
|
18774
|
+
GROUP BY t2.id
|
|
18775
|
+
) AS L(level)`.toQuery());
|
|
18776
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18777
|
+
throw new NodeNotFoundError();
|
|
18778
|
+
const height = parseInt(result.rows[0]["height"]);
|
|
18779
|
+
if (!lodash_1.default.isNumber(height) || lodash_1.default.isNaN(height) || height < 0)
|
|
18780
|
+
throw new Error(`Invalid height for tree: ${height}`);
|
|
18781
|
+
return height;
|
|
18782
|
+
});
|
|
18783
|
+
}
|
|
18784
|
+
/**
|
|
18785
|
+
* Get left most immediate child of a node
|
|
18786
|
+
*
|
|
18787
|
+
* @param {string} parentNodeId
|
|
18788
|
+
* @returns {Promise<Maybe<NodeRecord>>}
|
|
18789
|
+
* @memberof NestedSetModelQueryer
|
|
18790
|
+
*/
|
|
18791
|
+
getLeftMostImmediateChild(parentNodeId, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18792
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18793
|
+
const authConditions = authDecision.toSql({
|
|
18794
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18795
|
+
tableRef: "children" });
|
|
18796
|
+
|
|
18797
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18798
|
+
const result = yield this.pool.query(...sql_syntax_1.sqls`SELECT ${this.selectFields("Children")}
|
|
18799
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18800
|
+
WHERE Children.left = Parents.left + 1 AND Parents.id = ${parentNodeId}
|
|
18801
|
+
${authConditions.isEmpty ?
|
|
18802
|
+
sql_syntax_1.default.empty :
|
|
18803
|
+
sql_syntax_1.sqls` AND ${authConditions}`}`.toQuery());
|
|
18804
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18805
|
+
return tsmonad_1.Maybe.nothing();
|
|
18806
|
+
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
18807
|
+
});
|
|
18808
|
+
}
|
|
18809
|
+
/**
|
|
18810
|
+
* Get right most immediate child of a node
|
|
18811
|
+
*
|
|
18812
|
+
* @param {string} parentNodeId
|
|
18813
|
+
* @returns {Promise<Maybe<NodeRecord>>}
|
|
18814
|
+
* @memberof NestedSetModelQueryer
|
|
18815
|
+
*/
|
|
18816
|
+
getRightMostImmediateChild(parentNodeId, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18817
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18818
|
+
const authConditions = authDecision.toSql({
|
|
18819
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18820
|
+
tableRef: "children" });
|
|
18821
|
+
|
|
18822
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18823
|
+
const result = yield this.pool.query(...sql_syntax_1.sqls`SELECT ${this.selectFields("Children")}
|
|
18824
|
+
FROM ${tbl} AS Parents, ${tbl} AS Children
|
|
18825
|
+
WHERE Children.right = Parents.right - 1 AND Parents.id = ${parentNodeId}
|
|
18826
|
+
${authConditions.isEmpty ?
|
|
18827
|
+
sql_syntax_1.default.empty :
|
|
18828
|
+
sql_syntax_1.sqls` AND ${authConditions}`}`.toQuery());
|
|
18829
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18830
|
+
return tsmonad_1.Maybe.nothing();
|
|
18831
|
+
return tsmonad_1.Maybe.just(result.rows[0]);
|
|
18832
|
+
});
|
|
18833
|
+
}
|
|
18834
|
+
/**
|
|
18835
|
+
* Get all nodes on the top to down path between the `higherNode` to the `lowerNode`
|
|
18836
|
+
* Sort from higher level nodes to lower level node.
|
|
18837
|
+
* Result will include `higherNode` and the `lowerNode`.
|
|
18838
|
+
* If `higherNode` and the `lowerNode` is the same node, an array contains the single node will be return.
|
|
18839
|
+
* If a path doesn't exist, empty array (`[]`) will be returned
|
|
18840
|
+
* If you pass a lower node to the `higherNodeId` and a higher node to `lowerNodeId`, empty array will be returned
|
|
18841
|
+
*
|
|
18842
|
+
* @param {string} higherNodeId
|
|
18843
|
+
* @param {string} lowerNodeId
|
|
18844
|
+
* @returns {Promise<Maybe<NodeRecord[]>}
|
|
18845
|
+
* @memberof NestedSetModelQueryer
|
|
18846
|
+
*/
|
|
18847
|
+
getTopDownPathBetween(higherNodeId, lowerNodeId, authDecision = AuthDecision_1.UnconditionalTrueDecision) {
|
|
18848
|
+
var _a;
|
|
18849
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18850
|
+
const authConditions = authDecision.toSql({
|
|
18851
|
+
prefixes: ["input.authObject.orgUnit"],
|
|
18852
|
+
tableRef: "t2" });
|
|
18853
|
+
|
|
18854
|
+
const tbl = SQLUtils_1.escapeIdentifier(this.tableName);
|
|
18855
|
+
const result = yield this.pool.query(...sql_syntax_1.sqls`SELECT ${this.selectFields("t2")}
|
|
18856
|
+
FROM ${tbl} AS t1, ${tbl} AS t2, ${tbl} AS t3
|
|
18857
|
+
WHERE t1.id = ${higherNodeId} AND t3.id = ${lowerNodeId}
|
|
18858
|
+
AND t2.left BETWEEN t1.left AND t1.right
|
|
18859
|
+
AND t3.left BETWEEN t2.left AND t2.right
|
|
18860
|
+
${authConditions.isEmpty ?
|
|
18861
|
+
sql_syntax_1.default.empty :
|
|
18862
|
+
sql_syntax_1.sqls` AND ${authConditions}`}
|
|
18863
|
+
ORDER BY (t2.right-t2.left) DESC`.toQuery());
|
|
18864
|
+
if (!((_a = result === null || result === void 0 ? void 0 : result.rows) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
18865
|
+
return [];
|
|
18866
|
+
} else
|
|
18867
|
+
{
|
|
18868
|
+
return result.rows;
|
|
18869
|
+
}
|
|
18870
|
+
});
|
|
18871
|
+
}
|
|
18872
|
+
/**
|
|
18873
|
+
* Compare the relative position of the two nodes
|
|
18874
|
+
* If node1 is superior to node2, return "ancestor"
|
|
18875
|
+
* if node1 is the subordinate of node2, return "descendant"
|
|
18876
|
+
* If node1 = node2 return "equal"
|
|
18877
|
+
* If there is no path can be found between Node1 and Node2 return "unrelated"
|
|
18878
|
+
*
|
|
18879
|
+
* @param {string} node1Id
|
|
18880
|
+
* @param {string} node2Id
|
|
18881
|
+
* @param {pg.Client} [client=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18882
|
+
* @returns {Promise<CompareNodeResult>}
|
|
18883
|
+
* @memberof NestedSetModelQueryer
|
|
18884
|
+
*/
|
|
18885
|
+
compareNodes(node1Id, node2Id, client = null) {
|
|
18886
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18887
|
+
const tbl = this.tableName;
|
|
18888
|
+
const result = yield (client ? client : this.pool).query(`SELECT (
|
|
18889
|
+
CASE
|
|
18890
|
+
WHEN CAST($1 AS varchar) = CAST($2 AS varchar)
|
|
18891
|
+
THEN 0
|
|
18892
|
+
WHEN t1.left BETWEEN t2.left AND t2.right
|
|
18893
|
+
THEN -1
|
|
18894
|
+
WHEN t2.left BETWEEN t1.left AND t1.right
|
|
18895
|
+
THEN 1
|
|
18896
|
+
ELSE null
|
|
18897
|
+
END
|
|
18898
|
+
) AS "result"
|
|
18899
|
+
FROM "${tbl}" AS t1, "${tbl}" AS t2
|
|
18900
|
+
WHERE t1.id = CAST($1 AS uuid) AND t2.id = CAST($2 AS uuid)`, [node1Id, node2Id]);
|
|
18901
|
+
if (!result || !result.rows || !result.rows.length)
|
|
18902
|
+
return "unrelated";
|
|
18903
|
+
const comparisonResult = result.rows[0]["result"];
|
|
18904
|
+
if (typeof comparisonResult === "number") {
|
|
18905
|
+
switch (comparisonResult) {
|
|
18906
|
+
case 1:
|
|
18907
|
+
return "ancestor";
|
|
18908
|
+
case -1:
|
|
18909
|
+
return "descendant";
|
|
18910
|
+
case 0:
|
|
18911
|
+
return "equal";}
|
|
18912
|
+
|
|
18913
|
+
}
|
|
18914
|
+
return "unrelated";
|
|
18915
|
+
});
|
|
18916
|
+
}
|
|
18917
|
+
getInsertFields(insertFieldList = null) {
|
|
18918
|
+
const fieldList = isNonEmptyArray(insertFieldList) ?
|
|
18919
|
+
insertFieldList :
|
|
18920
|
+
this.defaultInsertFieldList;
|
|
18921
|
+
if (!isNonEmptyArray(fieldList)) {
|
|
18922
|
+
throw new Error("Insert fields must be an non-empty array!");
|
|
18923
|
+
}
|
|
18924
|
+
return fieldList;
|
|
18925
|
+
}
|
|
18926
|
+
getNodesInsertSql(nodes, sqlValues, insertFieldList = null, tableAliasOrName = "") {
|
|
18927
|
+
if (!isNonEmptyArray(nodes)) {
|
|
18928
|
+
throw new Error("`sqlValues` parameter should be an non-empty array!");
|
|
18929
|
+
}
|
|
18930
|
+
if (!lodash_1.default.isArray(sqlValues)) {
|
|
18931
|
+
throw new Error("`sqlValues` parameter should be an array!");
|
|
18932
|
+
}
|
|
18933
|
+
if (!isValidSqlIdentifier(tableAliasOrName)) {
|
|
18934
|
+
throw new Error(`tableAliasOrName: ${tableAliasOrName} contains invalid characters!`);
|
|
18935
|
+
}
|
|
18936
|
+
const tbl = this.tableName;
|
|
18937
|
+
const fieldList = this.getInsertFields(insertFieldList);
|
|
18938
|
+
const columnsList = fieldList.
|
|
18939
|
+
map(f => {
|
|
18940
|
+
if (!isValidSqlIdentifier(f)) {
|
|
18941
|
+
throw new Error(`column name: ${f} contains invalid characters!`);
|
|
18942
|
+
}
|
|
18943
|
+
return tableAliasOrName == "" ?
|
|
18944
|
+
`"${f}"` :
|
|
18945
|
+
`${tableAliasOrName}."${f}"`;
|
|
18946
|
+
}).
|
|
18947
|
+
join(", ");
|
|
18948
|
+
const valuesList = nodes.
|
|
18949
|
+
map(node => "(" +
|
|
18950
|
+
fieldList.
|
|
18951
|
+
map(f => {
|
|
18952
|
+
sqlValues.push(node[f]);
|
|
18953
|
+
return `$${sqlValues.length}`;
|
|
18954
|
+
}).
|
|
18955
|
+
join(", ") +
|
|
18956
|
+
")").
|
|
18957
|
+
join(", ");
|
|
18958
|
+
return `INSERT INTO "${tbl}" (${columnsList}) VALUES ${valuesList}`;
|
|
18959
|
+
}
|
|
18960
|
+
/**
|
|
18961
|
+
* Create the root node of the tree.
|
|
18962
|
+
* If a root node already exists, an error will be thrown.
|
|
18030
18963
|
*
|
|
18031
|
-
* @
|
|
18032
|
-
* @
|
|
18033
|
-
* @
|
|
18964
|
+
* @param {NodeRecord} node
|
|
18965
|
+
* @param {pg.Client} [existingClient=null] Optional pg client; Use supplied client connection for query rather than a random connection from Pool
|
|
18966
|
+
* @returns {Promise<string>} newly created node ID
|
|
18967
|
+
* @memberof NestedSetModelQueryer
|
|
18034
18968
|
*/
|
|
18035
|
-
|
|
18036
|
-
|
|
18037
|
-
|
|
18038
|
-
|
|
18039
|
-
|
|
18040
|
-
|
|
18041
|
-
|
|
18042
|
-
|
|
18043
|
-
|
|
18044
|
-
|
|
18045
|
-
|
|
18046
|
-
|
|
18047
|
-
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
|
|
18051
|
-
if (result.__filtered__) {
|
|
18052
|
-
result.__takeCount__ = nativeMin(n, result.__takeCount__);
|
|
18053
|
-
} else {
|
|
18054
|
-
result.__views__.push({
|
|
18055
|
-
'size': nativeMin(n, MAX_ARRAY_LENGTH),
|
|
18056
|
-
'type': methodName + (result.__dir__ < 0 ? 'Right' : '')
|
|
18057
|
-
});
|
|
18969
|
+
createRootNode(node, existingClient = null) {
|
|
18970
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18971
|
+
const tbl = this.tableName;
|
|
18972
|
+
const client = existingClient ?
|
|
18973
|
+
existingClient :
|
|
18974
|
+
yield this.pool.connect();
|
|
18975
|
+
const fields = Object.keys(node);
|
|
18976
|
+
if (!fields.length) {
|
|
18977
|
+
throw new Error("`node` parameter cannot be an empty object with no key.");
|
|
18978
|
+
}
|
|
18979
|
+
let nodeId;
|
|
18980
|
+
try {
|
|
18981
|
+
yield client.query("BEGIN");
|
|
18982
|
+
let result = yield client.query(`SELECT "id" FROM "${tbl}" WHERE "left" = 1 LIMIT 1`);
|
|
18983
|
+
if (result && isNonEmptyArray(result.rows)) {
|
|
18984
|
+
throw new Error(`A root node with id: ${result.rows[0]["id"]} already exists`);
|
|
18058
18985
|
}
|
|
18059
|
-
|
|
18060
|
-
|
|
18061
|
-
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
|
|
18071
|
-
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
|
|
18080
|
-
|
|
18986
|
+
const countResult = yield client.query(`SELECT COUNT("id") AS "num" FROM "${tbl}" WHERE "left" != 1`);
|
|
18987
|
+
let countNum = countResult && countResult.rows && countResult.rows.length ?
|
|
18988
|
+
parseInt(countResult.rows[0].num) :
|
|
18989
|
+
0;
|
|
18990
|
+
countNum = isNaN(countNum) ? 0 : countNum;
|
|
18991
|
+
const right = countNum ? (countNum + 1) * 2 : 2;
|
|
18992
|
+
const sqlValues = [];
|
|
18993
|
+
result = yield client.query(this.getNodesInsertSql([
|
|
18994
|
+
Object.assign(Object.assign({}, node), { left: 1, right })],
|
|
18995
|
+
sqlValues, fields.concat(["left", "right"])) + " RETURNING id", sqlValues);
|
|
18996
|
+
if (!result || !isNonEmptyArray(result.rows)) {
|
|
18997
|
+
throw new Error("Cannot locate create root node ID!");
|
|
18998
|
+
}
|
|
18999
|
+
yield client.query("COMMIT");
|
|
19000
|
+
nodeId = result.rows[0]["id"];
|
|
19001
|
+
}
|
|
19002
|
+
catch (e) {
|
|
19003
|
+
yield client.query("ROLLBACK");
|
|
19004
|
+
throw e;
|
|
19005
|
+
} finally
|
|
19006
|
+
{
|
|
19007
|
+
if (!existingClient) {
|
|
19008
|
+
client.release();
|
|
19009
|
+
}
|
|
19010
|
+
}
|
|
19011
|
+
return nodeId;
|
|
18081
19012
|
});
|
|
19013
|
+
}
|
|
19014
|
+
getNodeDataWithinTx(client, nodeId, fields) {
|
|
19015
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19016
|
+
const node = yield this.getNodeById(nodeId, fields, client);
|
|
19017
|
+
return node.caseOf({
|
|
19018
|
+
just: node => node,
|
|
19019
|
+
nothing: () => {
|
|
19020
|
+
throw new NodeNotFoundError(`Cannot locate tree node record with id: ${nodeId}`);
|
|
19021
|
+
} });
|
|
18082
19022
|
|
|
18083
|
-
// Add `LazyWrapper` methods for `_.head` and `_.last`.
|
|
18084
|
-
arrayEach(['head', 'last'], function(methodName, index) {
|
|
18085
|
-
var takeName = 'take' + (index ? 'Right' : '');
|
|
18086
|
-
|
|
18087
|
-
LazyWrapper.prototype[methodName] = function() {
|
|
18088
|
-
return this[takeName](1).value()[0];
|
|
18089
|
-
};
|
|
18090
19023
|
});
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
|
|
18095
|
-
|
|
18096
|
-
|
|
18097
|
-
|
|
18098
|
-
|
|
19024
|
+
}
|
|
19025
|
+
/**
|
|
19026
|
+
* Insert a node to the tree under a parent node
|
|
19027
|
+
*
|
|
19028
|
+
* @param {string} parentNodeId
|
|
19029
|
+
* @param {NodeRecord} node
|
|
19030
|
+
* @returns {Promise<string>}
|
|
19031
|
+
* @throws NodeNotFoundError if parent node not found
|
|
19032
|
+
* @memberof NestedSetModelQueryer
|
|
19033
|
+
*/
|
|
19034
|
+
insertNode(parentNodeId, node) {
|
|
19035
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19036
|
+
if (!parentNodeId) {
|
|
19037
|
+
throw new Error("`parentNodeId` cannot be empty!");
|
|
19038
|
+
}
|
|
19039
|
+
const fields = Object.keys(node);
|
|
19040
|
+
if (!fields.length) {
|
|
19041
|
+
throw new Error("`node` parameter cannot be an empty object with no key.");
|
|
19042
|
+
}
|
|
19043
|
+
const tbl = this.tableName;
|
|
19044
|
+
const client = yield this.pool.connect();
|
|
19045
|
+
let nodeId;
|
|
19046
|
+
try {
|
|
19047
|
+
yield client.query("BEGIN");
|
|
19048
|
+
const { right: parentRight } = yield this.getNodeDataWithinTx(client, parentNodeId, ["right"]);
|
|
19049
|
+
yield client.query(`UPDATE "${tbl}"
|
|
19050
|
+
SET
|
|
19051
|
+
"left" = CASE WHEN "left" > $1 THEN "left" + 2 ELSE "left" END,
|
|
19052
|
+
"right" = CASE WHEN "right" >= $1 THEN "right" + 2 ELSE "right" END
|
|
19053
|
+
WHERE "right" >= $1`, [parentRight]);
|
|
19054
|
+
const sqlValues = [];
|
|
19055
|
+
const result = yield client.query(this.getNodesInsertSql([
|
|
19056
|
+
Object.assign(Object.assign({}, node), { left: parentRight, right: parentRight + 1 })],
|
|
19057
|
+
sqlValues, fields.concat(["left", "right"])) + " RETURNING id", sqlValues);
|
|
19058
|
+
if (!result || !isNonEmptyArray(result.rows)) {
|
|
19059
|
+
throw new Error("Cannot locate created node ID!");
|
|
19060
|
+
}
|
|
19061
|
+
yield client.query("COMMIT");
|
|
19062
|
+
nodeId = result.rows[0]["id"];
|
|
19063
|
+
}
|
|
19064
|
+
catch (e) {
|
|
19065
|
+
yield client.query("ROLLBACK");
|
|
19066
|
+
throw e;
|
|
19067
|
+
} finally
|
|
19068
|
+
{
|
|
19069
|
+
client.release();
|
|
19070
|
+
}
|
|
19071
|
+
return nodeId;
|
|
18099
19072
|
});
|
|
18100
|
-
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
|
|
18109
|
-
|
|
18110
|
-
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18114
|
-
|
|
18115
|
-
return new LazyWrapper(this);
|
|
19073
|
+
}
|
|
19074
|
+
/**
|
|
19075
|
+
* Insert a node to the right of its sibling
|
|
19076
|
+
* If `siblingNodeId` belongs to a root node, an error will be thrown
|
|
19077
|
+
*
|
|
19078
|
+
* @param {string} siblingNodeId
|
|
19079
|
+
* @param {NodeRecord} node
|
|
19080
|
+
* @returns {Promise<string>}
|
|
19081
|
+
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
19082
|
+
* @memberof NestedSetModelQueryer
|
|
19083
|
+
*/
|
|
19084
|
+
insertNodeToRightOfSibling(siblingNodeId, node) {
|
|
19085
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19086
|
+
if (!siblingNodeId) {
|
|
19087
|
+
throw new Error("`siblingNodeId` cannot be empty!");
|
|
18116
19088
|
}
|
|
18117
|
-
|
|
18118
|
-
|
|
18119
|
-
|
|
18120
|
-
|
|
18121
|
-
|
|
18122
|
-
|
|
18123
|
-
|
|
18124
|
-
|
|
18125
|
-
|
|
18126
|
-
|
|
18127
|
-
|
|
19089
|
+
const fields = Object.keys(node);
|
|
19090
|
+
if (!fields.length) {
|
|
19091
|
+
throw new Error("`node` parameter cannot be an empty object with no key.");
|
|
19092
|
+
}
|
|
19093
|
+
const tbl = this.tableName;
|
|
19094
|
+
const client = yield this.pool.connect();
|
|
19095
|
+
let nodeId;
|
|
19096
|
+
try {
|
|
19097
|
+
yield client.query("BEGIN");
|
|
19098
|
+
const { left: siblingLeft, right: siblingRight } = yield this.getNodeDataWithinTx(client, siblingNodeId, [
|
|
19099
|
+
"left",
|
|
19100
|
+
"right"]);
|
|
18128
19101
|
|
|
18129
|
-
|
|
18130
|
-
|
|
18131
|
-
|
|
19102
|
+
if (siblingLeft === 1) {
|
|
19103
|
+
throw new Error("Cannot add sibling to the Root node!");
|
|
19104
|
+
}
|
|
19105
|
+
yield client.query(`UPDATE "${tbl}"
|
|
19106
|
+
SET
|
|
19107
|
+
"left" = CASE WHEN "left" < $1 THEN "left" ELSE "left" + 2 END,
|
|
19108
|
+
"right" = CASE WHEN "right" < $1 THEN "right" ELSE "right" + 2 END
|
|
19109
|
+
WHERE "right" > $1`, [siblingRight]);
|
|
19110
|
+
const sqlValues = [];
|
|
19111
|
+
const result = yield client.query(this.getNodesInsertSql([
|
|
19112
|
+
Object.assign(Object.assign({}, node), { left: siblingRight + 1, right: siblingRight + 2 })],
|
|
19113
|
+
sqlValues, fields.concat(["left", "right"])) + " RETURNING id", sqlValues);
|
|
19114
|
+
if (!result || !isNonEmptyArray(result.rows)) {
|
|
19115
|
+
throw new Error("Cannot locate created node ID!");
|
|
19116
|
+
}
|
|
19117
|
+
yield client.query("COMMIT");
|
|
19118
|
+
nodeId = result.rows[0]["id"];
|
|
18132
19119
|
}
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
|
|
18136
|
-
|
|
19120
|
+
catch (e) {
|
|
19121
|
+
yield client.query("ROLLBACK");
|
|
19122
|
+
throw e;
|
|
19123
|
+
} finally
|
|
19124
|
+
{
|
|
19125
|
+
client.release();
|
|
18137
19126
|
}
|
|
18138
|
-
|
|
18139
|
-
|
|
18140
|
-
|
|
19127
|
+
return nodeId;
|
|
19128
|
+
});
|
|
19129
|
+
}
|
|
19130
|
+
/**
|
|
19131
|
+
* Move a subtree (the specified root node and all its subordinates)
|
|
19132
|
+
* to under a new parent.
|
|
19133
|
+
*
|
|
19134
|
+
* If the specifed sub tree root node is a child of the new parent node,
|
|
19135
|
+
* an error will be be thrown
|
|
19136
|
+
*
|
|
19137
|
+
* @param {string} subTreeRootNodeId
|
|
19138
|
+
* @param {string} newParentId
|
|
19139
|
+
* @returns {Promise<void>}
|
|
19140
|
+
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
19141
|
+
* @memberof NestedSetModelQueryer
|
|
19142
|
+
*/
|
|
19143
|
+
moveSubTreeTo(subTreeRootNodeId, newParentId) {
|
|
19144
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19145
|
+
if (!subTreeRootNodeId) {
|
|
19146
|
+
throw new Error("`subTreeRootNodeId` cannot be empty!");
|
|
18141
19147
|
}
|
|
18142
|
-
|
|
18143
|
-
|
|
18144
|
-
|
|
18145
|
-
LazyWrapper.prototype.takeRightWhile = function(predicate) {
|
|
18146
|
-
return this.reverse().takeWhile(predicate).reverse();
|
|
18147
|
-
};
|
|
18148
|
-
|
|
18149
|
-
LazyWrapper.prototype.toArray = function() {
|
|
18150
|
-
return this.take(MAX_ARRAY_LENGTH);
|
|
18151
|
-
};
|
|
18152
|
-
|
|
18153
|
-
// Add `LazyWrapper` methods to `lodash.prototype`.
|
|
18154
|
-
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|
18155
|
-
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
|
|
18156
|
-
isTaker = /^(?:head|last)$/.test(methodName),
|
|
18157
|
-
lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
|
|
18158
|
-
retUnwrapped = isTaker || /^find/.test(methodName);
|
|
18159
|
-
|
|
18160
|
-
if (!lodashFunc) {
|
|
18161
|
-
return;
|
|
19148
|
+
if (!newParentId) {
|
|
19149
|
+
throw new Error("`newParentId` cannot be empty!");
|
|
18162
19150
|
}
|
|
18163
|
-
|
|
18164
|
-
|
|
18165
|
-
|
|
18166
|
-
|
|
18167
|
-
|
|
18168
|
-
|
|
19151
|
+
const tbl = this.tableName;
|
|
19152
|
+
const client = yield this.pool.connect();
|
|
19153
|
+
try {
|
|
19154
|
+
yield client.query("BEGIN");
|
|
19155
|
+
const comparisonResult = yield this.compareNodes(subTreeRootNodeId, newParentId, client);
|
|
19156
|
+
if (comparisonResult === "ancestor") {
|
|
19157
|
+
throw new Error(`Cannot move a higher level node (id: ${subTreeRootNodeId})to its subordinate (id: ${newParentId})`);
|
|
19158
|
+
}
|
|
19159
|
+
const { left: originRootLeft, right: originRootRight } = yield this.getNodeDataWithinTx(client, subTreeRootNodeId, [
|
|
19160
|
+
"left",
|
|
19161
|
+
"right"]);
|
|
18169
19162
|
|
|
18170
|
-
|
|
18171
|
-
|
|
18172
|
-
|
|
18173
|
-
};
|
|
19163
|
+
if (originRootLeft === "1") {
|
|
19164
|
+
throw new Error("Cannot move Tree root node as substree.");
|
|
19165
|
+
}
|
|
19166
|
+
const { right: newParentRight } = yield this.getNodeDataWithinTx(client, newParentId, ["right"]);
|
|
19167
|
+
yield client.query(`
|
|
19168
|
+
UPDATE "${tbl}"
|
|
19169
|
+
SET
|
|
19170
|
+
"left" = "left" + CASE
|
|
19171
|
+
WHEN $3::int4 < $1::int4
|
|
19172
|
+
THEN CASE
|
|
19173
|
+
WHEN "left" BETWEEN $1 AND $2
|
|
19174
|
+
THEN $3 - $1
|
|
19175
|
+
WHEN "left" BETWEEN $3 AND ($1 - 1)
|
|
19176
|
+
THEN $2 - $1 + 1
|
|
19177
|
+
ELSE 0 END
|
|
19178
|
+
WHEN $3::int4 > $2::int4
|
|
19179
|
+
THEN CASE
|
|
19180
|
+
WHEN "left" BETWEEN $1 AND $2
|
|
19181
|
+
THEN $3 - $2 - 1
|
|
19182
|
+
WHEN "left" BETWEEN ($2 + 1) AND ($3 - 1)
|
|
19183
|
+
THEN $1 - $2 - 1
|
|
19184
|
+
ELSE 0 END
|
|
19185
|
+
ELSE 0 END,
|
|
19186
|
+
"right" = "right" + CASE
|
|
19187
|
+
WHEN $3::int4 < $1::int4
|
|
19188
|
+
THEN CASE
|
|
19189
|
+
WHEN "right" BETWEEN $1 AND $2
|
|
19190
|
+
THEN $3 - $1
|
|
19191
|
+
WHEN "right" BETWEEN $3 AND ($1 - 1)
|
|
19192
|
+
THEN $2 - $1 + 1
|
|
19193
|
+
ELSE 0 END
|
|
19194
|
+
WHEN $3::int4 > $2::int4
|
|
19195
|
+
THEN CASE
|
|
19196
|
+
WHEN "right" BETWEEN $1 AND $2
|
|
19197
|
+
THEN $3 - $2 - 1
|
|
19198
|
+
WHEN "right" BETWEEN ($2 + 1) AND ($3 - 1)
|
|
19199
|
+
THEN $1 - $2 - 1
|
|
19200
|
+
ELSE 0 END
|
|
19201
|
+
ELSE 0 END
|
|
19202
|
+
`, [originRootLeft, originRootRight, newParentRight]);
|
|
19203
|
+
yield client.query("COMMIT");
|
|
19204
|
+
}
|
|
19205
|
+
catch (e) {
|
|
19206
|
+
yield client.query("ROLLBACK");
|
|
19207
|
+
throw e;
|
|
19208
|
+
} finally
|
|
19209
|
+
{
|
|
19210
|
+
client.release();
|
|
19211
|
+
}
|
|
19212
|
+
});
|
|
19213
|
+
}
|
|
19214
|
+
/**
|
|
19215
|
+
* Delete a subtree (and all its dependents)
|
|
19216
|
+
* If you sent in a root node id (and `allowRootNodeId` is true), the whole tree will be removed
|
|
19217
|
+
* When `allowRootNodeId` is false and you passed a root node id, an error will be thrown
|
|
19218
|
+
*
|
|
19219
|
+
* @param {string} subTreeRootNodeId
|
|
19220
|
+
* @param {boolean} [allowRootNodeId=false]
|
|
19221
|
+
* @returns {Promise<void>}
|
|
19222
|
+
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
19223
|
+
* @memberof NestedSetModelQueryer
|
|
19224
|
+
*/
|
|
19225
|
+
deleteSubTree(subTreeRootNodeId, allowRootNodeId = false) {
|
|
19226
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19227
|
+
if (!subTreeRootNodeId) {
|
|
19228
|
+
throw new Error("`subTreeRootNodeId` cannot be empty!");
|
|
19229
|
+
}
|
|
19230
|
+
const tbl = this.tableName;
|
|
19231
|
+
const client = yield this.pool.connect();
|
|
19232
|
+
try {
|
|
19233
|
+
yield client.query("BEGIN");
|
|
19234
|
+
const { left: subTreeRootLeft, right: subTreeRootRight } = yield this.getNodeDataWithinTx(client, subTreeRootNodeId, [
|
|
19235
|
+
"left",
|
|
19236
|
+
"right"]);
|
|
18174
19237
|
|
|
18175
|
-
if (
|
|
18176
|
-
|
|
18177
|
-
isLazy = useLazy = false;
|
|
19238
|
+
if (subTreeRootLeft === 1 && !allowRootNodeId) {
|
|
19239
|
+
throw new Error("Root node id is not allowed!");
|
|
18178
19240
|
}
|
|
18179
|
-
|
|
18180
|
-
|
|
18181
|
-
|
|
18182
|
-
|
|
19241
|
+
// --- delete the sub tree nodes
|
|
19242
|
+
yield client.query(`DELETE FROM "${tbl}" WHERE "left" BETWEEN $1 AND $2`, [subTreeRootLeft, subTreeRootRight]);
|
|
19243
|
+
// --- closing the gap after deletion
|
|
19244
|
+
yield client.query(`
|
|
19245
|
+
UPDATE "${tbl}"
|
|
19246
|
+
SET "left" = CASE
|
|
19247
|
+
WHEN "left" > $1
|
|
19248
|
+
THEN "left" - ($2 - $1 + 1)
|
|
19249
|
+
ELSE "left" END,
|
|
19250
|
+
"right" = CASE
|
|
19251
|
+
WHEN "right" > $1
|
|
19252
|
+
THEN "right" - ($2 - $1 + 1)
|
|
19253
|
+
ELSE "right" END
|
|
19254
|
+
WHERE "left" > $1 OR "right" > $1
|
|
19255
|
+
`, [subTreeRootLeft, subTreeRootRight]);
|
|
19256
|
+
yield client.query("COMMIT");
|
|
19257
|
+
}
|
|
19258
|
+
catch (e) {
|
|
19259
|
+
yield client.query("ROLLBACK");
|
|
19260
|
+
throw e;
|
|
19261
|
+
} finally
|
|
19262
|
+
{
|
|
19263
|
+
client.release();
|
|
19264
|
+
}
|
|
19265
|
+
});
|
|
19266
|
+
}
|
|
19267
|
+
/**
|
|
19268
|
+
* Delete a single node from the tree
|
|
19269
|
+
* Its childrens will become its parent's children
|
|
19270
|
+
* Deleting a root node is not allowed.
|
|
19271
|
+
* You can, however, delete the whole sub tree from root node or update the root node, instead.
|
|
19272
|
+
*
|
|
19273
|
+
* @param {string} nodeId
|
|
19274
|
+
* @returns {Promise<void>}
|
|
19275
|
+
* @throws NodeNotFoundError If the node can't be found in the tree
|
|
19276
|
+
* @memberof NestedSetModelQueryer
|
|
19277
|
+
*/
|
|
19278
|
+
deleteNode(nodeId) {
|
|
19279
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19280
|
+
if (!nodeId) {
|
|
19281
|
+
throw new Error("`nodeId` cannot be empty!");
|
|
19282
|
+
}
|
|
19283
|
+
const tbl = this.tableName;
|
|
19284
|
+
const client = yield this.pool.connect();
|
|
19285
|
+
try {
|
|
19286
|
+
yield client.query("BEGIN");
|
|
19287
|
+
const { left: subTreeRootLeft, right: subTreeRootRight } = yield this.getNodeDataWithinTx(client, nodeId, [
|
|
19288
|
+
"left",
|
|
19289
|
+
"right"]);
|
|
18183
19290
|
|
|
18184
|
-
if (
|
|
18185
|
-
|
|
18186
|
-
var result = func.apply(value, args);
|
|
18187
|
-
result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
|
|
18188
|
-
return new LodashWrapper(result, chainAll);
|
|
18189
|
-
}
|
|
18190
|
-
if (isUnwrapped && onlyLazy) {
|
|
18191
|
-
return func.apply(this, args);
|
|
19291
|
+
if (subTreeRootLeft === 1) {
|
|
19292
|
+
throw new Error("Delete a root node is not allowed!");
|
|
18192
19293
|
}
|
|
18193
|
-
|
|
18194
|
-
|
|
18195
|
-
|
|
18196
|
-
|
|
18197
|
-
|
|
18198
|
-
// Add `Array` methods to `lodash.prototype`.
|
|
18199
|
-
arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {
|
|
18200
|
-
var func = arrayProto[methodName],
|
|
18201
|
-
chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',
|
|
18202
|
-
retUnwrapped = /^(?:pop|shift)$/.test(methodName);
|
|
19294
|
+
// --- delete the node
|
|
19295
|
+
// --- In nested set model, children are still bind to the deleted node's parent after deletion
|
|
19296
|
+
yield client.query(`DELETE FROM "${tbl}" WHERE "id" = $1`, [
|
|
19297
|
+
nodeId]);
|
|
18203
19298
|
|
|
18204
|
-
|
|
18205
|
-
|
|
18206
|
-
|
|
18207
|
-
|
|
18208
|
-
|
|
18209
|
-
|
|
18210
|
-
|
|
18211
|
-
|
|
18212
|
-
|
|
18213
|
-
|
|
19299
|
+
// --- closing the gap after deletion
|
|
19300
|
+
yield client.query(`
|
|
19301
|
+
UPDATE "${tbl}"
|
|
19302
|
+
SET "left" = CASE
|
|
19303
|
+
WHEN "left" > $1 AND "right" < $2
|
|
19304
|
+
THEN "left" - 1
|
|
19305
|
+
WHEN "left" > $1 AND "right" > $2
|
|
19306
|
+
THEN "left" - 2
|
|
19307
|
+
ELSE "left" END,
|
|
19308
|
+
"right" = CASE
|
|
19309
|
+
WHEN "left" > $1 AND "right" < $2
|
|
19310
|
+
THEN "right" - 1
|
|
19311
|
+
ELSE ("right" - 2) END
|
|
19312
|
+
WHERE "left" > $1 OR "right" > $1
|
|
19313
|
+
`, [subTreeRootLeft, subTreeRootRight]);
|
|
19314
|
+
yield client.query("COMMIT");
|
|
19315
|
+
}
|
|
19316
|
+
catch (e) {
|
|
19317
|
+
yield client.query("ROLLBACK");
|
|
19318
|
+
throw e;
|
|
19319
|
+
} finally
|
|
19320
|
+
{
|
|
19321
|
+
client.release();
|
|
19322
|
+
}
|
|
19323
|
+
});
|
|
19324
|
+
}
|
|
19325
|
+
/**
|
|
19326
|
+
* Update node data of the node specified by the nodeId
|
|
19327
|
+
* The followings fields will be ignored (as they should be generated by program):
|
|
19328
|
+
* - `left`
|
|
19329
|
+
* - `right`
|
|
19330
|
+
* - `id`
|
|
19331
|
+
*
|
|
19332
|
+
* @param {string} nodeId
|
|
19333
|
+
* @param {NodeRecord} nodeData
|
|
19334
|
+
* @param {pg.Client} [client=null]
|
|
19335
|
+
* @returns {Promise<void>}
|
|
19336
|
+
* @memberof NestedSetModelQueryer
|
|
19337
|
+
*/
|
|
19338
|
+
updateNode(nodeId, nodeData, client = null) {
|
|
19339
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19340
|
+
if (!isUuid_1.default(nodeId)) {
|
|
19341
|
+
throw new Error("nodeId is not valid UUID!");
|
|
19342
|
+
}
|
|
19343
|
+
const sqlUpdates = Object.keys(nodeData).
|
|
19344
|
+
filter(k => k !== "left" && k !== "right" && k !== "id").
|
|
19345
|
+
map(fieldName => sql_syntax_1.sqls`${SQLUtils_1.escapeIdentifier(fieldName)} = ${nodeData[fieldName]}`);
|
|
19346
|
+
if (!sqlUpdates.length) {
|
|
19347
|
+
console.log("update node parameter: ", nodeId, nodeData);
|
|
19348
|
+
throw new Error("No valid node data passed for updating.");
|
|
19349
|
+
}
|
|
19350
|
+
yield (client ? client : this.pool).query(...sql_syntax_1.sqls`UPDATE ${SQLUtils_1.escapeIdentifier(this.tableName)} SET ${sql_syntax_1.default.join(sqlUpdates, sql_syntax_1.sqls` , `)} WHERE "id" = ${nodeId}`.toQuery());
|
|
18214
19351
|
});
|
|
19352
|
+
}
|
|
19353
|
+
getChildTextTreeNodes(parentId) {
|
|
19354
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19355
|
+
const nodes = yield this.getImmediateChildren(parentId);
|
|
19356
|
+
if (!nodes || !nodes.length)
|
|
19357
|
+
return [];
|
|
19358
|
+
const textNodeList = [];
|
|
19359
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
19360
|
+
const nodeChildren = yield this.getChildTextTreeNodes(nodes[i].id);
|
|
19361
|
+
if (nodeChildren.length) {
|
|
19362
|
+
textNodeList.push({
|
|
19363
|
+
text: nodes[i].name,
|
|
19364
|
+
children: nodeChildren });
|
|
18215
19365
|
|
|
18216
|
-
|
|
18217
|
-
|
|
18218
|
-
|
|
18219
|
-
if (lodashFunc) {
|
|
18220
|
-
var key = lodashFunc.name + '';
|
|
18221
|
-
if (!hasOwnProperty.call(realNames, key)) {
|
|
18222
|
-
realNames[key] = [];
|
|
19366
|
+
} else
|
|
19367
|
+
{
|
|
19368
|
+
textNodeList.push(nodes[i].name);
|
|
18223
19369
|
}
|
|
18224
|
-
realNames[key].push({ 'name': methodName, 'func': lodashFunc });
|
|
18225
19370
|
}
|
|
19371
|
+
return textNodeList;
|
|
18226
19372
|
});
|
|
19373
|
+
}
|
|
19374
|
+
/**
|
|
19375
|
+
* Generate the Text View of the tree
|
|
19376
|
+
* Provided as Dev tool only
|
|
19377
|
+
*
|
|
19378
|
+
* E.g. output could be:
|
|
19379
|
+
* └─ Albert
|
|
19380
|
+
* ├─ Chuck
|
|
19381
|
+
* │ ├─ Fred
|
|
19382
|
+
* │ ├─ Eddie
|
|
19383
|
+
* │ └─ Donna
|
|
19384
|
+
* └─ Bert
|
|
19385
|
+
*
|
|
19386
|
+
* @returns {Promise<string>}
|
|
19387
|
+
* @memberof NestedSetModelQueryer
|
|
19388
|
+
*/
|
|
19389
|
+
getTreeTextView() {
|
|
19390
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19391
|
+
const rootNodeMaybe = yield this.getRootNode();
|
|
19392
|
+
return rootNodeMaybe.caseOf({
|
|
19393
|
+
just: rootNode => __awaiter(this, void 0, void 0, function* () {
|
|
19394
|
+
const tree = [];
|
|
19395
|
+
const children = yield this.getChildTextTreeNodes(rootNode.id);
|
|
19396
|
+
if (children.length) {
|
|
19397
|
+
tree.push({
|
|
19398
|
+
text: rootNode.name,
|
|
19399
|
+
children });
|
|
18227
19400
|
|
|
18228
|
-
|
|
18229
|
-
|
|
18230
|
-
|
|
18231
|
-
|
|
18232
|
-
|
|
18233
|
-
|
|
18234
|
-
|
|
18235
|
-
LazyWrapper.prototype.reverse = lazyReverse;
|
|
18236
|
-
LazyWrapper.prototype.value = lazyValue;
|
|
18237
|
-
|
|
18238
|
-
// Add chain sequence methods to the `lodash` wrapper.
|
|
18239
|
-
lodash.prototype.at = wrapperAt;
|
|
18240
|
-
lodash.prototype.chain = wrapperChain;
|
|
18241
|
-
lodash.prototype.commit = wrapperCommit;
|
|
18242
|
-
lodash.prototype.next = wrapperNext;
|
|
18243
|
-
lodash.prototype.plant = wrapperPlant;
|
|
18244
|
-
lodash.prototype.reverse = wrapperReverse;
|
|
18245
|
-
lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;
|
|
18246
|
-
|
|
18247
|
-
// Add lazy aliases.
|
|
18248
|
-
lodash.prototype.first = lodash.prototype.head;
|
|
18249
|
-
|
|
18250
|
-
if (symIterator) {
|
|
18251
|
-
lodash.prototype[symIterator] = wrapperToIterator;
|
|
18252
|
-
}
|
|
18253
|
-
return lodash;
|
|
18254
|
-
});
|
|
18255
|
-
|
|
18256
|
-
/*--------------------------------------------------------------------------*/
|
|
18257
|
-
|
|
18258
|
-
// Export lodash.
|
|
18259
|
-
var _ = runInContext();
|
|
18260
|
-
|
|
18261
|
-
// Some AMD build optimizers, like r.js, check for condition patterns like:
|
|
18262
|
-
if (true) {
|
|
18263
|
-
// Expose Lodash on the global object to prevent errors when Lodash is
|
|
18264
|
-
// loaded by a script tag in the presence of an AMD loader.
|
|
18265
|
-
// See http://requirejs.org/docs/errors.html#mismatch for more details.
|
|
18266
|
-
// Use `_.noConflict` to remove Lodash from the global object.
|
|
18267
|
-
root._ = _;
|
|
19401
|
+
} else
|
|
19402
|
+
{
|
|
19403
|
+
tree.push(rootNode.name);
|
|
19404
|
+
}
|
|
19405
|
+
return textTree(tree);
|
|
19406
|
+
}),
|
|
19407
|
+
nothing: () => __awaiter(this, void 0, void 0, function* () {return "Empty Tree";}) });
|
|
18268
19408
|
|
|
18269
|
-
|
|
18270
|
-
|
|
18271
|
-
!(__WEBPACK_AMD_DEFINE_RESULT__ = (function() {
|
|
18272
|
-
return _;
|
|
18273
|
-
}).call(exports, __webpack_require__, exports, module),
|
|
18274
|
-
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
|
|
18275
|
-
}
|
|
18276
|
-
// Check for `exports` after `define` in case a build optimizer adds it.
|
|
18277
|
-
else {}
|
|
18278
|
-
}.call(this));
|
|
19409
|
+
});
|
|
19410
|
+
}}
|
|
18279
19411
|
|
|
18280
|
-
|
|
19412
|
+
exports.default = NestedSetModelQueryer;
|
|
18281
19413
|
|
|
18282
19414
|
/***/ }),
|
|
18283
|
-
/*
|
|
19415
|
+
/* 14 */
|
|
18284
19416
|
/***/ (function(module, exports) {
|
|
18285
19417
|
|
|
18286
19418
|
module.exports = function(module) {
|
|
@@ -18308,14 +19440,262 @@ module.exports = function(module) {
|
|
|
18308
19440
|
|
|
18309
19441
|
|
|
18310
19442
|
/***/ }),
|
|
18311
|
-
/*
|
|
19443
|
+
/* 15 */
|
|
18312
19444
|
/***/ (function(module, exports, __webpack_require__) {
|
|
18313
19445
|
|
|
18314
19446
|
!function(t,n){if(true)module.exports=n();else { var i, e; }}(this,function(){return function(t){function n(i){if(e[i])return e[i].exports;var r=e[i]={exports:{},id:i,loaded:!1};return t[i].call(r.exports,r,r.exports,n),r.loaded=!0,r.exports}var e={};return n.m=t,n.c=e,n.p="",n(0)}([function(t,n,e){"use strict";function i(t){for(var e in t)n.hasOwnProperty(e)||(n[e]=t[e])}Object.defineProperty(n,"__esModule",{value:!0}),i(e(2)),i(e(3)),i(e(1)),i(e(4))},function(t,n){"use strict";function e(t,n){var i=0;if(t===n)return!0;if("function"==typeof t.equals)return t.equals(n);if(t.length>0&&t.length===n.length){for(;i<t.length;i+=1)if(!e(t[i],n[i]))return!1;return!0}return!1}Object.defineProperty(n,"__esModule",{value:!0}),n.eq=e},function(t,n,e){"use strict";function i(t){return null!==t&&void 0!==t}function r(t,n){if(i(t)&&i(n))throw new TypeError("Cannot construct an Either with both a left and a right");if(!i(t)&&!i(n))throw new TypeError("Cannot construct an Either with neither a left nor a right");return i(t)&&!i(n)?s.left(t):!i(t)&&i(n)?s.right(n):void 0}Object.defineProperty(n,"__esModule",{value:!0});var o,u=e(1);!function(t){t[t.Left=0]="Left",t[t.Right=1]="Right"}(o=n.EitherType||(n.EitherType={})),n.either=r;var s=function(){function t(t,n,e){this.type=t,this.l=n,this.r=e,this.of=this.unit,this.chain=this.bind,this.lift=this.fmap,this.map=this.fmap}return t.left=function(n){return new t(o.Left,n)},t.right=function(n){return new t(o.Right,null,n)},t.prototype.unit=function(n){return t.right(n)},t.prototype.bind=function(n){return this.type===o.Right?n(this.r):t.left(this.l)},t.prototype.fmap=function(t){var n=this;return this.bind(function(e){return n.unit(t(e))})},t.prototype.caseOf=function(t){return this.type===o.Right?t.right(this.r):t.left(this.l)},t.prototype.equals=function(t){return t.type===this.type&&(this.type===o.Left&&u.eq(t.l,this.l)||this.type===o.Right&&u.eq(t.r,this.r))},t.prototype["do"]=function(t){void 0===t&&(t={});var n={left:function(t){},right:function(t){}},e=Object.assign(n,t);return this.caseOf(e),this},t}();n.Either=s},function(t,n,e){"use strict";function i(t){return u.maybe(t)}Object.defineProperty(n,"__esModule",{value:!0});var r,o=e(1);!function(t){t[t.Nothing=0]="Nothing",t[t.Just=1]="Just"}(r=n.MaybeType||(n.MaybeType={})),n.maybe=i;var u=function(){function t(t,n){this.type=t,this.value=n,this.of=this.unit,this.chain=this.bind,this.lift=this.fmap,this.map=this.fmap}return t.sequence=function(n){if(Object.keys(n).filter(function(t){return n[t].type===r.Nothing}).length)return t.nothing();var e={};for(var i in n)n.hasOwnProperty(i)&&(e[i]=n[i].value);return t.just(e)},t.maybe=function(n){return null===n||void 0===n?new t(r.Nothing):new t(r.Just,n)},t.just=function(n){if(null===n||void 0===n)throw new TypeError("Cannot Maybe.just(null)");return new t(r.Just,n)},t.nothing=function(){return new t(r.Nothing)},t.prototype.unit=function(n){return t.maybe(n)},t.prototype.bind=function(n){return this.type===r.Just?n(this.value):t.nothing()},t.prototype.fmap=function(t){var n=this;return this.bind(function(e){return n.unit(t(e))})},t.prototype.caseOf=function(t){return this.type===r.Just?t.just(this.value):t.nothing()},t.prototype.defaulting=function(n){return t.just(this.valueOr(n))},t.prototype.equals=function(t){return t.type===this.type&&(this.type===r.Nothing||o.eq(t.value,this.value))},t.prototype.valueOr=function(t){return this.valueOrCompute(function(){return t})},t.prototype.valueOrCompute=function(t){return this.type===r.Just?this.value:t()},t.prototype.valueOrThrow=function(t){if(this.type===r.Just)return this.value;throw t||new Error("No value is available.")},t.prototype["do"]=function(t){void 0===t&&(t={});var n={just:function(t){},nothing:function(){}},e=Object.assign(n,t);return this.caseOf(e),this},t}();u.all=function(t){return u.sequence(t)},n.Maybe=u},function(t,n){"use strict";function e(t,n){return i.writer(t,n)}Object.defineProperty(n,"__esModule",{value:!0}),n.writer=e;var i=function(){function t(t,n){this.story=t,this.value=n,this.of=this.unit,this.chain=this.bind,this.lift=this.fmap,this.map=this.fmap}return t.writer=function(n,e){return new t(n,e)},t.tell=function(n){return new t([n],0)},t.prototype.unit=function(n){return new t([],n)},t.prototype.bind=function(n){var e=n(this.value),i=this.story.concat(e.story);return new t(i,e.value)},t.prototype.fmap=function(t){var n=this;return this.bind(function(e){return n.unit(t(e))})},t.prototype.caseOf=function(t){return t.writer(this.story,this.value)},t.prototype.equals=function(t){var n,e=!0;for(n=0;n<this.story.length;n+=1)e=e&&this.story[n]===t.story[n];return e&&this.value===t.value},t}();n.Writer=i}])});
|
|
18315
19447
|
//# sourceMappingURL=tsmonad.js.map
|
|
18316
19448
|
|
|
18317
19449
|
/***/ }),
|
|
18318
|
-
/*
|
|
19450
|
+
/* 16 */
|
|
19451
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
19452
|
+
|
|
19453
|
+
"use strict";
|
|
19454
|
+
|
|
19455
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19456
|
+
if (k2 === undefined) k2 = k;
|
|
19457
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
19458
|
+
}) : (function(o, m, k, k2) {
|
|
19459
|
+
if (k2 === undefined) k2 = k;
|
|
19460
|
+
o[k2] = m[k];
|
|
19461
|
+
}));
|
|
19462
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19463
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19464
|
+
}) : function(o, v) {
|
|
19465
|
+
o["default"] = v;
|
|
19466
|
+
});
|
|
19467
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19468
|
+
if (mod && mod.__esModule) return mod;
|
|
19469
|
+
var result = {};
|
|
19470
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
19471
|
+
__setModuleDefault(result, mod);
|
|
19472
|
+
return result;
|
|
19473
|
+
};
|
|
19474
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19475
|
+
exports.AspectQueryGroup = exports.AspectQueryValueInArray = exports.AspectQueryArrayNotEmpty = exports.AspectQueryWithValue = exports.AspectQueryExists = exports.AspectQueryFalse = exports.AspectQueryTrue = exports.AspectQuery = exports.AspectQueryValue = void 0;
|
|
19476
|
+
const sql_syntax_1 = __importStar(__webpack_require__(7));
|
|
19477
|
+
const SQLUtils_1 = __webpack_require__(10);
|
|
19478
|
+
class AspectQueryValue {
|
|
19479
|
+
constructor(value) {
|
|
19480
|
+
this.value = value;
|
|
19481
|
+
switch (typeof value) {
|
|
19482
|
+
case "number":
|
|
19483
|
+
this.postgresType = sql_syntax_1.sqls `NUMERIC`;
|
|
19484
|
+
break;
|
|
19485
|
+
case "boolean":
|
|
19486
|
+
this.postgresType = sql_syntax_1.sqls `BOOL`;
|
|
19487
|
+
break;
|
|
19488
|
+
case "string":
|
|
19489
|
+
this.postgresType = sql_syntax_1.sqls `TEXT`;
|
|
19490
|
+
break;
|
|
19491
|
+
default:
|
|
19492
|
+
throw new Error("getPostgresValueTypeCastStr: unsupported data type: `${" +
|
|
19493
|
+
typeof value +
|
|
19494
|
+
"}`");
|
|
19495
|
+
}
|
|
19496
|
+
}
|
|
19497
|
+
}
|
|
19498
|
+
exports.AspectQueryValue = AspectQueryValue;
|
|
19499
|
+
class AspectQuery {
|
|
19500
|
+
constructor(aspectId, path = [], negated = false) {
|
|
19501
|
+
this.aspectId = aspectId;
|
|
19502
|
+
this.path = path;
|
|
19503
|
+
this.negated = negated;
|
|
19504
|
+
}
|
|
19505
|
+
/**
|
|
19506
|
+
* Public interface of all types of AspectQuery. Call this method to covert AspectQuery to SQL statement.
|
|
19507
|
+
* Sub-class might choose to override this method to alter generic logic.
|
|
19508
|
+
*
|
|
19509
|
+
* @param {AspectQueryToSqlConfig} config
|
|
19510
|
+
* @return {SQLSyntax}
|
|
19511
|
+
* @memberof AspectQuery
|
|
19512
|
+
*/
|
|
19513
|
+
toSql(config) {
|
|
19514
|
+
if (!this.aspectId) {
|
|
19515
|
+
throw new Error("Invalid AspectQuery: aspectId cannot be empty.");
|
|
19516
|
+
}
|
|
19517
|
+
const sqlQuery = this.sqlQueries(config);
|
|
19518
|
+
if (this.negated) {
|
|
19519
|
+
return sql_syntax_1.sqls `NOT ${sqlQuery}`;
|
|
19520
|
+
}
|
|
19521
|
+
else {
|
|
19522
|
+
return sqlQuery;
|
|
19523
|
+
}
|
|
19524
|
+
}
|
|
19525
|
+
}
|
|
19526
|
+
exports.AspectQuery = AspectQuery;
|
|
19527
|
+
class AspectQueryTrue extends AspectQuery {
|
|
19528
|
+
constructor() {
|
|
19529
|
+
super(undefined);
|
|
19530
|
+
}
|
|
19531
|
+
sqlQueries(config) {
|
|
19532
|
+
return sql_syntax_1.sqls `TRUE`;
|
|
19533
|
+
}
|
|
19534
|
+
// override default toSql as we don't need any validation logic
|
|
19535
|
+
toSql(config) {
|
|
19536
|
+
return this.sqlQueries(config);
|
|
19537
|
+
}
|
|
19538
|
+
}
|
|
19539
|
+
exports.AspectQueryTrue = AspectQueryTrue;
|
|
19540
|
+
class AspectQueryFalse extends AspectQuery {
|
|
19541
|
+
constructor() {
|
|
19542
|
+
super(undefined);
|
|
19543
|
+
}
|
|
19544
|
+
sqlQueries(config) {
|
|
19545
|
+
return sql_syntax_1.sqls `FALSE`;
|
|
19546
|
+
}
|
|
19547
|
+
// override default toSql as we don't need any validation logic
|
|
19548
|
+
toSql(config) {
|
|
19549
|
+
return this.sqlQueries(config);
|
|
19550
|
+
}
|
|
19551
|
+
}
|
|
19552
|
+
exports.AspectQueryFalse = AspectQueryFalse;
|
|
19553
|
+
class AspectQueryExists extends AspectQuery {
|
|
19554
|
+
sqlQueries(config) {
|
|
19555
|
+
var _a;
|
|
19556
|
+
const fieldRef = SQLUtils_1.getTableColumnName(this.aspectId, config.tableRef, config.useLowerCaseColumnName);
|
|
19557
|
+
if ((_a = this.path) === null || _a === void 0 ? void 0 : _a.length) {
|
|
19558
|
+
return sql_syntax_1.sqls `(${fieldRef} #> string_to_array(${this.path.join(",")}, ',')) IS NOT NULL`;
|
|
19559
|
+
}
|
|
19560
|
+
else {
|
|
19561
|
+
return fieldRef.isNotNull();
|
|
19562
|
+
}
|
|
19563
|
+
}
|
|
19564
|
+
}
|
|
19565
|
+
exports.AspectQueryExists = AspectQueryExists;
|
|
19566
|
+
class AspectQueryWithValue extends AspectQuery {
|
|
19567
|
+
constructor(value, operator, placeReferenceFirst, aspectId, path = [], negated = false) {
|
|
19568
|
+
super(aspectId, path, negated);
|
|
19569
|
+
this.value = value;
|
|
19570
|
+
this.operator = operator;
|
|
19571
|
+
this.placeReferenceFirst = placeReferenceFirst;
|
|
19572
|
+
}
|
|
19573
|
+
sqlQueries(config) {
|
|
19574
|
+
var _a;
|
|
19575
|
+
const fieldRef = SQLUtils_1.getTableColumnName(this.aspectId, config.tableRef, config.useLowerCaseColumnName);
|
|
19576
|
+
const tableDataRef = !((_a = this.path) === null || _a === void 0 ? void 0 : _a.length)
|
|
19577
|
+
? sql_syntax_1.sqls `${fieldRef}::${this.value.postgresType}`
|
|
19578
|
+
: sql_syntax_1.sqls `(${fieldRef} #>> string_to_array(${this.path.join(",")}, ','))::${this.value.postgresType}`;
|
|
19579
|
+
return this.placeReferenceFirst
|
|
19580
|
+
? sql_syntax_1.sqls `COALESCE(${tableDataRef} ${this.operator} ${this.value.value}::${this.value.postgresType}, false)`
|
|
19581
|
+
: sql_syntax_1.sqls `COALESCE(${this.value.value}::${this.value.postgresType} ${this.operator} ${tableDataRef}, false)`;
|
|
19582
|
+
}
|
|
19583
|
+
}
|
|
19584
|
+
exports.AspectQueryWithValue = AspectQueryWithValue;
|
|
19585
|
+
class AspectQueryArrayNotEmpty extends AspectQuery {
|
|
19586
|
+
sqlQueries(config) {
|
|
19587
|
+
var _a;
|
|
19588
|
+
const fieldRef = SQLUtils_1.getTableColumnName(this.aspectId, config.tableRef, config.useLowerCaseColumnName);
|
|
19589
|
+
const tableDataRef = !((_a = this.path) === null || _a === void 0 ? void 0 : _a.length)
|
|
19590
|
+
? sql_syntax_1.sqls `(${fieldRef} #> string_to_array('0',','))`
|
|
19591
|
+
: sql_syntax_1.sqls `(${fieldRef} #>> string_to_array(${[...this.path, "0"].join(",")}, ','))`;
|
|
19592
|
+
return tableDataRef.isNotNull();
|
|
19593
|
+
}
|
|
19594
|
+
}
|
|
19595
|
+
exports.AspectQueryArrayNotEmpty = AspectQueryArrayNotEmpty;
|
|
19596
|
+
class AspectQueryValueInArray extends AspectQuery {
|
|
19597
|
+
constructor(value, aspectId, path = [], negated = false) {
|
|
19598
|
+
super(aspectId, path, negated);
|
|
19599
|
+
this.value = value;
|
|
19600
|
+
}
|
|
19601
|
+
sqlQueries(config) {
|
|
19602
|
+
var _a;
|
|
19603
|
+
const fieldRef = SQLUtils_1.getTableColumnName(this.aspectId, config.tableRef, config.useLowerCaseColumnName);
|
|
19604
|
+
const tableDataRef = !((_a = this.path) === null || _a === void 0 ? void 0 : _a.length)
|
|
19605
|
+
? sql_syntax_1.sqls `COALESCE(
|
|
19606
|
+
(
|
|
19607
|
+
(${fieldRef}::JSONB #> string_to_array('0',','))::JSONB
|
|
19608
|
+
) @> to_json(${this.value.value})::JSONB,
|
|
19609
|
+
FALSE
|
|
19610
|
+
)`
|
|
19611
|
+
: sql_syntax_1.sqls `COALESCE(
|
|
19612
|
+
(
|
|
19613
|
+
(${fieldRef}::JSONB #> string_to_array(${this.path.join(",")}, ','))::JSONB
|
|
19614
|
+
) @> to_json(${this.value.value})::JSONB,
|
|
19615
|
+
FALSE
|
|
19616
|
+
)`;
|
|
19617
|
+
return tableDataRef;
|
|
19618
|
+
}
|
|
19619
|
+
}
|
|
19620
|
+
exports.AspectQueryValueInArray = AspectQueryValueInArray;
|
|
19621
|
+
class AspectQueryGroup {
|
|
19622
|
+
constructor(queries, joinWithAnd = true, negated = false) {
|
|
19623
|
+
this.queries = queries;
|
|
19624
|
+
this.joinWithAnd = joinWithAnd;
|
|
19625
|
+
this.negated = negated;
|
|
19626
|
+
}
|
|
19627
|
+
toSql(config) {
|
|
19628
|
+
var _a;
|
|
19629
|
+
if (!((_a = this.queries) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
19630
|
+
return sql_syntax_1.default.empty;
|
|
19631
|
+
}
|
|
19632
|
+
let result;
|
|
19633
|
+
if (this.joinWithAnd) {
|
|
19634
|
+
if (this.queries.findIndex((item) => item instanceof AspectQueryFalse) !== -1) {
|
|
19635
|
+
result = sql_syntax_1.sqls `FALSE`;
|
|
19636
|
+
}
|
|
19637
|
+
else {
|
|
19638
|
+
result = sql_syntax_1.default.joinWithAnd(this.queries.map((q) => {
|
|
19639
|
+
if (q instanceof AspectQueryTrue) {
|
|
19640
|
+
return sql_syntax_1.default.empty;
|
|
19641
|
+
}
|
|
19642
|
+
else {
|
|
19643
|
+
return q.toSql(config);
|
|
19644
|
+
}
|
|
19645
|
+
}));
|
|
19646
|
+
}
|
|
19647
|
+
}
|
|
19648
|
+
else {
|
|
19649
|
+
if (this.queries.findIndex((item) => item instanceof AspectQueryTrue) !== -1) {
|
|
19650
|
+
result = sql_syntax_1.sqls `TRUE`;
|
|
19651
|
+
}
|
|
19652
|
+
else {
|
|
19653
|
+
result = sql_syntax_1.default.joinWithOr(this.queries.map((q) => {
|
|
19654
|
+
if (q instanceof AspectQueryFalse) {
|
|
19655
|
+
return sql_syntax_1.default.empty;
|
|
19656
|
+
}
|
|
19657
|
+
else {
|
|
19658
|
+
return q.toSql(config);
|
|
19659
|
+
}
|
|
19660
|
+
}));
|
|
19661
|
+
}
|
|
19662
|
+
}
|
|
19663
|
+
if (this.negated) {
|
|
19664
|
+
return sql_syntax_1.sqls `NOT ${result}`;
|
|
19665
|
+
}
|
|
19666
|
+
else {
|
|
19667
|
+
return result;
|
|
19668
|
+
}
|
|
19669
|
+
}
|
|
19670
|
+
}
|
|
19671
|
+
exports.AspectQueryGroup = AspectQueryGroup;
|
|
19672
|
+
//# sourceMappingURL=AspectQuery.js.map
|
|
19673
|
+
|
|
19674
|
+
/***/ }),
|
|
19675
|
+
/* 17 */
|
|
19676
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
19677
|
+
|
|
19678
|
+
"use strict";
|
|
19679
|
+
|
|
19680
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19681
|
+
class ServerError extends Error {
|
|
19682
|
+
constructor(message = "Unknown Error", statusCode = 500) {
|
|
19683
|
+
super(message);
|
|
19684
|
+
this.statusCode = statusCode;
|
|
19685
|
+
}
|
|
19686
|
+
toData() {
|
|
19687
|
+
return {
|
|
19688
|
+
isError: true,
|
|
19689
|
+
errorCode: this.statusCode,
|
|
19690
|
+
errorMessage: this.message
|
|
19691
|
+
};
|
|
19692
|
+
}
|
|
19693
|
+
}
|
|
19694
|
+
exports.default = ServerError;
|
|
19695
|
+
//# sourceMappingURL=ServerError.js.map
|
|
19696
|
+
|
|
19697
|
+
/***/ }),
|
|
19698
|
+
/* 18 */
|
|
18319
19699
|
/***/ (function(module, exports) {
|
|
18320
19700
|
|
|
18321
19701
|
module.exports = require("text-treeview");
|