spice-js 2.6.53 → 2.6.55
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/build/models/SpiceModel.js +171 -74
- package/package.json +1 -1
- package/src/models/SpiceModel.js +198 -104
|
@@ -19,8 +19,6 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try
|
|
|
19
19
|
|
|
20
20
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
21
21
|
|
|
22
|
-
global.mapping_dept = "*";
|
|
23
|
-
|
|
24
22
|
var co = require("co");
|
|
25
23
|
|
|
26
24
|
var regeneratorRuntime = require("regenerator-runtime");
|
|
@@ -37,7 +35,8 @@ var SDate = require("sonover-date"),
|
|
|
37
35
|
_external_modifier_loaded = Symbol(),
|
|
38
36
|
_skip_cache = Symbol(),
|
|
39
37
|
_serializers = Symbol(),
|
|
40
|
-
_level = Symbol()
|
|
38
|
+
_level = Symbol(),
|
|
39
|
+
_mapping_dept = Symbol(); //const _type = Symbol("type");
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
var that;
|
|
@@ -59,20 +58,21 @@ class SpiceModel {
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
try {
|
|
62
|
-
var
|
|
61
|
+
var _args2, _args2$args, _args$args, _args3, _args3$args, _args4, _args4$args, _args5, _args5$args;
|
|
63
62
|
|
|
64
63
|
var dbtype = spice.config.database.connections[args.connection].type || "couchbase";
|
|
65
64
|
|
|
66
65
|
var Database = require("spice-" + dbtype);
|
|
67
66
|
|
|
67
|
+
this[_mapping_dept] = ((_args2 = args) == null ? void 0 : (_args2$args = _args2.args) == null ? void 0 : _args2$args.mapping_dept) || 100;
|
|
68
68
|
this.type = "";
|
|
69
69
|
this.collection = args.connection;
|
|
70
70
|
this[_args] = args.args;
|
|
71
71
|
this[_external_modifier_loaded] = false;
|
|
72
72
|
this[_disable_lifecycle_events] = ((_args$args = args.args) == null ? void 0 : _args$args.disable_lifecycle_events) || false;
|
|
73
|
-
this[_ctx] = (
|
|
74
|
-
this[_skip_cache] = ((
|
|
75
|
-
this[_level] = ((
|
|
73
|
+
this[_ctx] = (_args3 = args) == null ? void 0 : (_args3$args = _args3.args) == null ? void 0 : _args3$args.ctx;
|
|
74
|
+
this[_skip_cache] = ((_args4 = args) == null ? void 0 : (_args4$args = _args4.args) == null ? void 0 : _args4$args.skip_cache) || false;
|
|
75
|
+
this[_level] = ((_args5 = args) == null ? void 0 : (_args5$args = _args5.args) == null ? void 0 : _args5$args._level) || 0;
|
|
76
76
|
this[_hooks] = {
|
|
77
77
|
create: {
|
|
78
78
|
before: [],
|
|
@@ -533,7 +533,7 @@ class SpiceModel {
|
|
|
533
533
|
var _this3 = this;
|
|
534
534
|
|
|
535
535
|
return _asyncToGenerator(function* () {
|
|
536
|
-
var obj = _this3.getCacheProviderObject();
|
|
536
|
+
var obj = _this3.getCacheProviderObject(_this3.type);
|
|
537
537
|
|
|
538
538
|
var monitor_record = yield obj.get("monitor::" + _this3.type);
|
|
539
539
|
|
|
@@ -551,7 +551,7 @@ class SpiceModel {
|
|
|
551
551
|
|
|
552
552
|
setMonitor() {
|
|
553
553
|
var current_time = new Date().getTime();
|
|
554
|
-
var obj = this.getCacheProviderObject();
|
|
554
|
+
var obj = this.getCacheProviderObject(this.type);
|
|
555
555
|
var key = "monitor::" + this.type;
|
|
556
556
|
var value = {
|
|
557
557
|
time: current_time
|
|
@@ -566,9 +566,7 @@ class SpiceModel {
|
|
|
566
566
|
|
|
567
567
|
return _asyncToGenerator(function* () {
|
|
568
568
|
try {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
global.mapping_dept = ((_args5 = args) == null ? void 0 : _args5.mapping_dept) ? Number(args.mapping_dept) : global.mapping_dept;
|
|
569
|
+
if (args.mapping_dept) _this4[_mapping_dept] = args.mapping_dept;
|
|
572
570
|
|
|
573
571
|
if (!args) {
|
|
574
572
|
args = {};
|
|
@@ -586,7 +584,7 @@ class SpiceModel {
|
|
|
586
584
|
var cached_results = yield _this4.getCacheProviderObject(_this4.type).get(key);
|
|
587
585
|
results = cached_results == null ? void 0 : cached_results.value;
|
|
588
586
|
|
|
589
|
-
if ((cached_results == null ? void 0 : cached_results.value)
|
|
587
|
+
if ((cached_results == null ? void 0 : cached_results.value) === undefined || (yield _this4.shouldForceRefresh(cached_results)) || _this4.type == "workflow") {
|
|
590
588
|
results = yield _this4.database.get(args.id);
|
|
591
589
|
yield _this4.getCacheProviderObject(_this4.type).set(key, {
|
|
592
590
|
value: results,
|
|
@@ -663,7 +661,7 @@ class SpiceModel {
|
|
|
663
661
|
var cached_results = yield _this6.getCacheProviderObject(_this6.type).get(key);
|
|
664
662
|
results = cached_results == null ? void 0 : cached_results.value;
|
|
665
663
|
|
|
666
|
-
if ((cached_results == null ? void 0 : cached_results.value)
|
|
664
|
+
if ((cached_results == null ? void 0 : cached_results.value) === undefined || (yield _this6.shouldForceRefresh(cached_results))) {
|
|
667
665
|
results = yield _this6.database.multi_get(args.ids, true);
|
|
668
666
|
|
|
669
667
|
_this6.getCacheProviderObject(_this6.type).set(key, {
|
|
@@ -916,11 +914,11 @@ class SpiceModel {
|
|
|
916
914
|
if (column === "meta().id") return undefined;
|
|
917
915
|
|
|
918
916
|
if (!column.startsWith("`") && !column.endsWith("`")) {
|
|
919
|
-
column = "`" + column + "`";
|
|
917
|
+
column = "`" + column.trim() + "`";
|
|
920
918
|
}
|
|
921
919
|
|
|
922
920
|
if (column && !column.includes(".") && !column.startsWith(this.type)) {
|
|
923
|
-
column = "`" + this.type + "`." + column;
|
|
921
|
+
column = "`" + this.type + "`." + column.trim();
|
|
924
922
|
}
|
|
925
923
|
|
|
926
924
|
return column;
|
|
@@ -931,11 +929,33 @@ class SpiceModel {
|
|
|
931
929
|
}
|
|
932
930
|
|
|
933
931
|
filterResultsByColumns(data, columns) {
|
|
932
|
+
if (this.type === "user") {
|
|
933
|
+
console.log("Filter Columns::", columns);
|
|
934
|
+
}
|
|
935
|
+
|
|
934
936
|
if (columns && columns !== "") {
|
|
935
|
-
|
|
936
|
-
var
|
|
937
|
-
|
|
938
|
-
|
|
937
|
+
// Remove backticks and replace meta().id with id
|
|
938
|
+
var cleanedColumns = columns.replace(/`/g, "").replace(/meta\(\)\.id/g, "id"); // Process each column by splitting on comma and trimming
|
|
939
|
+
|
|
940
|
+
var columnList = cleanedColumns.split(",").map(col => col.trim()).map(col => {
|
|
941
|
+
// Check for alias with AS (case-insensitive)
|
|
942
|
+
var aliasMatch = col.match(/\s+AS\s+([\w]+)/i);
|
|
943
|
+
|
|
944
|
+
if (aliasMatch) {
|
|
945
|
+
return aliasMatch[1].trim();
|
|
946
|
+
} // Otherwise, if a dot is present, take the part after the last dot
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
if (col.includes(".")) {
|
|
950
|
+
return col.split(".").pop().trim();
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
return col;
|
|
954
|
+
}); // Ensure that essential keys are always picked
|
|
955
|
+
|
|
956
|
+
var requiredKeys = ["_permissions", "_permissions_", "id"];
|
|
957
|
+
var finalKeys = [...new Set([...columnList, ...requiredKeys])];
|
|
958
|
+
return data.map(entry => _.pick(entry, finalKeys));
|
|
939
959
|
}
|
|
940
960
|
|
|
941
961
|
return data;
|
|
@@ -984,6 +1004,69 @@ class SpiceModel {
|
|
|
984
1004
|
return str.replace(/[^a-zA-Z0-9]/g, "");
|
|
985
1005
|
}
|
|
986
1006
|
|
|
1007
|
+
fixColumnName(columns) {
|
|
1008
|
+
// Guard clause: if columns is not provided or not a string, return it as-is.
|
|
1009
|
+
if (!columns || typeof columns !== "string") {
|
|
1010
|
+
return columns;
|
|
1011
|
+
} // Split the columns string on commas and trim each column expression.
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
var columnList = columns.split(",").map(col => col.trim()); // Object to keep track of alias usage to avoid duplicates.
|
|
1015
|
+
|
|
1016
|
+
var aliasTracker = {};
|
|
1017
|
+
var fixedColumns = columnList.map(col => {
|
|
1018
|
+
// Check if an explicit alias is already provided.
|
|
1019
|
+
var aliasRegex = /\s+AS\s+`?([\w]+)`?$/i;
|
|
1020
|
+
var aliasMatch = col.match(aliasRegex);
|
|
1021
|
+
var explicitAlias = aliasMatch ? aliasMatch[1] : null; // Remove the alias clause for easier processing.
|
|
1022
|
+
|
|
1023
|
+
var colWithoutAlias = explicitAlias ? col.replace(aliasRegex, "").trim() : col; // Use regex to extract the table and column names.
|
|
1024
|
+
// This regex matches optional backticks around each identifier.
|
|
1025
|
+
|
|
1026
|
+
var tableName = this.type;
|
|
1027
|
+
var columnName = "";
|
|
1028
|
+
var columnRegex = /^`?([\w]+)`?\.`?([\w]+)`?$/;
|
|
1029
|
+
var match = colWithoutAlias.match(columnRegex);
|
|
1030
|
+
|
|
1031
|
+
if (match) {
|
|
1032
|
+
tableName = match[1];
|
|
1033
|
+
columnName = match[2];
|
|
1034
|
+
} else {
|
|
1035
|
+
// If no table prefix is found, use the column name only.
|
|
1036
|
+
columnName = colWithoutAlias.replace(/`/g, "");
|
|
1037
|
+
} // Generate the alias.
|
|
1038
|
+
// If the column comes from a table different from this.type, then the alias
|
|
1039
|
+
// becomes tableName_columnName. Otherwise, just use the column name.
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
var newAlias = columnName;
|
|
1043
|
+
|
|
1044
|
+
if (tableName && tableName !== this.type) {
|
|
1045
|
+
newAlias = tableName + "_" + columnName; // If an explicit alias was provided, favor that.
|
|
1046
|
+
|
|
1047
|
+
if (explicitAlias) {
|
|
1048
|
+
newAlias = explicitAlias;
|
|
1049
|
+
} // Avoid duplicates by appending a count if needed.
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
if (aliasTracker.hasOwnProperty(newAlias)) {
|
|
1053
|
+
aliasTracker[newAlias]++;
|
|
1054
|
+
newAlias = newAlias + "_" + aliasTracker[newAlias];
|
|
1055
|
+
} else {
|
|
1056
|
+
aliasTracker[newAlias] = 0;
|
|
1057
|
+
} // If there's no explicit alias, add an alias clause.
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
if (!explicitAlias) {
|
|
1061
|
+
return colWithoutAlias + " AS `" + newAlias + "`";
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
return col;
|
|
1066
|
+
});
|
|
1067
|
+
return fixedColumns.join(", ");
|
|
1068
|
+
}
|
|
1069
|
+
|
|
987
1070
|
list(args) {
|
|
988
1071
|
var _this12 = this;
|
|
989
1072
|
|
|
@@ -993,10 +1076,12 @@ class SpiceModel {
|
|
|
993
1076
|
}
|
|
994
1077
|
|
|
995
1078
|
try {
|
|
996
|
-
var _args6, _args7, _args8
|
|
1079
|
+
var _args6, _args7, _args8;
|
|
997
1080
|
|
|
998
1081
|
args.columns = _this12.prepColumns(args.columns);
|
|
1082
|
+
if (args.mapping_dept) _this12[_mapping_dept] = args.mapping_dept;
|
|
999
1083
|
var nestings = [..._this12.extractNestings((_args6 = args) == null ? void 0 : _args6.query, _this12.type), ..._this12.extractNestings((_args7 = args) == null ? void 0 : _args7.columns, _this12.type), ..._this12.extractNestings((_args8 = args) == null ? void 0 : _args8.sort, _this12.type)];
|
|
1084
|
+
args.columns = _this12.fixColumnName(args.columns);
|
|
1000
1085
|
|
|
1001
1086
|
var mappedNestings = _.compact(_.uniq(nestings).map(nesting => {
|
|
1002
1087
|
var _prop$map;
|
|
@@ -1028,7 +1113,6 @@ class SpiceModel {
|
|
|
1028
1113
|
|
|
1029
1114
|
args.limit = Number(args.limit) || undefined;
|
|
1030
1115
|
args.offset = Number(args.offset) || 0;
|
|
1031
|
-
global.mapping_dept = ((_args9 = args) == null ? void 0 : _args9.mapping_dept) ? Number(args.mapping_dept) : global.mapping_dept;
|
|
1032
1116
|
args.sort = args.sort ? args.sort.split(",").map(item => item.includes(".") ? item : "`" + _this12.type + "`." + _this12.formatSortComponent(item)).join(",") : "`" + _this12.type + "`.created_at DESC";
|
|
1033
1117
|
|
|
1034
1118
|
if (args.skip_hooks !== true) {
|
|
@@ -1042,8 +1126,7 @@ class SpiceModel {
|
|
|
1042
1126
|
if (_this12.shouldUseCache(_this12.type)) {
|
|
1043
1127
|
var cachedResults = yield _this12.getCacheProviderObject(_this12.type).get(cacheKey);
|
|
1044
1128
|
results = cachedResults == null ? void 0 : cachedResults.value;
|
|
1045
|
-
|
|
1046
|
-
if (!results || (yield _this12.shouldForceRefresh(cachedResults))) {
|
|
1129
|
+
if ((cachedResults == null ? void 0 : cachedResults.value) === undefined) if (!results || (yield _this12.shouldForceRefresh(cachedResults))) {
|
|
1047
1130
|
results = yield _this12.fetchResults(args, query);
|
|
1048
1131
|
yield _this12.getCacheProviderObject(_this12.type).set(cacheKey, {
|
|
1049
1132
|
value: results,
|
|
@@ -1058,6 +1141,8 @@ class SpiceModel {
|
|
|
1058
1141
|
results.data = yield _this12.do_serialize(results.data, "read", {}, args, (yield _this12.propsToBeRemoved(results.data)));
|
|
1059
1142
|
}
|
|
1060
1143
|
|
|
1144
|
+
if (_this12.type == "user") console.log("results", results.data);
|
|
1145
|
+
|
|
1061
1146
|
if (args.skip_hooks !== true) {
|
|
1062
1147
|
yield _this12.run_hook(results.data, "list", "after");
|
|
1063
1148
|
}
|
|
@@ -1158,7 +1243,9 @@ class SpiceModel {
|
|
|
1158
1243
|
data = Array.of(data);
|
|
1159
1244
|
}
|
|
1160
1245
|
|
|
1161
|
-
|
|
1246
|
+
_this15[_mapping_dept];
|
|
1247
|
+
|
|
1248
|
+
if (_this15[_level] + 1 < _this15[_mapping_dept]) {
|
|
1162
1249
|
var classes = _.isArray(Class) ? Class : [Class];
|
|
1163
1250
|
var ids = [];
|
|
1164
1251
|
|
|
@@ -1171,7 +1258,8 @@ class SpiceModel {
|
|
|
1171
1258
|
var returned_all = yield Promise.allSettled(_.map(classes, obj => {
|
|
1172
1259
|
return new obj(_extends({}, _this15[_args], {
|
|
1173
1260
|
skip_cache: _this15[_skip_cache],
|
|
1174
|
-
_level: _this15[_level] + 1
|
|
1261
|
+
_level: _this15[_level] + 1,
|
|
1262
|
+
mapping_dept: _this15[_mapping_dept]
|
|
1175
1263
|
})).getMulti({
|
|
1176
1264
|
skip_hooks: true,
|
|
1177
1265
|
ids: ids
|
|
@@ -1205,7 +1293,7 @@ class SpiceModel {
|
|
|
1205
1293
|
data = Array.of(data);
|
|
1206
1294
|
}
|
|
1207
1295
|
|
|
1208
|
-
if (_this16[_level]
|
|
1296
|
+
if (_this16[_level] + 1 < _this16[_mapping_dept]) {
|
|
1209
1297
|
var ids = [];
|
|
1210
1298
|
|
|
1211
1299
|
_.each(data, result => {
|
|
@@ -1228,7 +1316,8 @@ class SpiceModel {
|
|
|
1228
1316
|
var returned_all = yield Promise.allSettled(_.map(classes, obj => {
|
|
1229
1317
|
return new obj(_extends({}, _this16[_args], {
|
|
1230
1318
|
skip_cache: _this16[_skip_cache],
|
|
1231
|
-
_level: _this16[_level] + 1
|
|
1319
|
+
_level: _this16[_level] + 1,
|
|
1320
|
+
mapping_dept: _this16[_mapping_dept]
|
|
1232
1321
|
})).getMulti({
|
|
1233
1322
|
skip_hooks: true,
|
|
1234
1323
|
ids: ids
|
|
@@ -1362,70 +1451,78 @@ class SpiceModel {
|
|
|
1362
1451
|
var _this18 = this;
|
|
1363
1452
|
|
|
1364
1453
|
return _asyncToGenerator(function* () {
|
|
1365
|
-
|
|
1454
|
+
if (path_to_be_removed === void 0) {
|
|
1455
|
+
path_to_be_removed = [];
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1366
1458
|
try {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1459
|
+
var _this18$_serializers, _this18$_serializers$;
|
|
1460
|
+
|
|
1461
|
+
// Early exit if serialization should not run.
|
|
1462
|
+
if (!_this18.shouldSerializerRun(args, type)) {
|
|
1463
|
+
return data;
|
|
1464
|
+
} // Add external modifiers only once.
|
|
1465
|
+
|
|
1466
|
+
|
|
1467
|
+
if (_this18.type && !_this18[_external_modifier_loaded]) {
|
|
1468
|
+
_this18.addExternalModifiers(_this18.type);
|
|
1371
1469
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
_this18.addExternalModifiers(_this18.type);
|
|
1470
|
+
_this18[_external_modifier_loaded] = true;
|
|
1471
|
+
} // Cache the modifiers lookup for the specified type.
|
|
1375
1472
|
|
|
1376
|
-
|
|
1473
|
+
|
|
1474
|
+
var modifiers = ((_this18$_serializers = _this18[_serializers]) == null ? void 0 : (_this18$_serializers$ = _this18$_serializers[type]) == null ? void 0 : _this18$_serializers$.modifiers) || [];
|
|
1475
|
+
|
|
1476
|
+
for (var modifier of modifiers) {
|
|
1477
|
+
try {
|
|
1478
|
+
data = yield modifier(data, old_data, _this18[_ctx], _this18.type);
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
console.error("Modifier error in do_serialize:", error.stack);
|
|
1377
1481
|
}
|
|
1482
|
+
} // Ensure data is always an array for consistent processing.
|
|
1378
1483
|
|
|
1379
|
-
for (var i of _this18[_serializers][type]["modifiers"]) {
|
|
1380
|
-
try {
|
|
1381
|
-
data = yield i(data, old_data, _this18[_ctx], _this18.type);
|
|
1382
|
-
} catch (e) {
|
|
1383
|
-
console.log(e.stack);
|
|
1384
|
-
}
|
|
1385
|
-
} // make data an array
|
|
1386
1484
|
|
|
1485
|
+
var originalIsArray = Array.isArray(data);
|
|
1387
1486
|
|
|
1388
|
-
|
|
1487
|
+
if (!originalIsArray) {
|
|
1488
|
+
data = [data];
|
|
1489
|
+
} // Compute the defaults from properties using reduce.
|
|
1389
1490
|
|
|
1390
|
-
if (!original_is_array) {
|
|
1391
|
-
data = Array.of(data);
|
|
1392
|
-
} // get defaults from peroperties
|
|
1393
1491
|
|
|
1492
|
+
var defaults = Object.keys(_this18.props).reduce((acc, key) => {
|
|
1493
|
+
var _this18$props$key, _this18$props$key$def;
|
|
1394
1494
|
|
|
1395
|
-
var
|
|
1495
|
+
var def = (_this18$props$key = _this18.props[key]) == null ? void 0 : (_this18$props$key$def = _this18$props$key.defaults) == null ? void 0 : _this18$props$key$def[type];
|
|
1396
1496
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
}
|
|
1404
|
-
} // apply defaults
|
|
1497
|
+
if (def !== undefined) {
|
|
1498
|
+
acc[key] = _.isFunction(def) ? def({
|
|
1499
|
+
old_data: data,
|
|
1500
|
+
new_data: old_data
|
|
1501
|
+
}) : def;
|
|
1502
|
+
}
|
|
1405
1503
|
|
|
1504
|
+
return acc;
|
|
1505
|
+
}, {}); // Merge defaults into each object.
|
|
1406
1506
|
|
|
1407
|
-
|
|
1408
|
-
_.defaults(_i2, defaults);
|
|
1409
|
-
} // apply cleaners
|
|
1507
|
+
data = data.map(item => _.defaults(item, defaults)); // If type is "read", clean the data by omitting certain props.
|
|
1410
1508
|
|
|
1509
|
+
if (type === "read") {
|
|
1510
|
+
// Collect hidden properties from schema.
|
|
1511
|
+
var hiddenProps = Object.keys(_this18.props).filter(key => {
|
|
1512
|
+
var _this18$props$key2;
|
|
1411
1513
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1514
|
+
return (_this18$props$key2 = _this18.props[key]) == null ? void 0 : _this18$props$key2.hide;
|
|
1515
|
+
}); // Combine default props to remove.
|
|
1414
1516
|
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1517
|
+
var propsToClean = ["deleted", "type", ...path_to_be_removed, ...hiddenProps];
|
|
1518
|
+
data = data.map(item => _.omit(item, propsToClean));
|
|
1519
|
+
} // Return in the original format (array or single object).
|
|
1420
1520
|
|
|
1421
|
-
data = _.map(data, obj => _.omit(obj, props_to_clean));
|
|
1422
|
-
}
|
|
1423
1521
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
throw e;
|
|
1522
|
+
return originalIsArray ? data : data[0];
|
|
1523
|
+
} catch (error) {
|
|
1524
|
+
console.error("Error in do_serialize:", error.stack);
|
|
1525
|
+
throw error;
|
|
1429
1526
|
}
|
|
1430
1527
|
})();
|
|
1431
1528
|
}
|
package/package.json
CHANGED
package/src/models/SpiceModel.js
CHANGED
|
@@ -165,15 +165,17 @@ export default class SpiceModel {
|
|
|
165
165
|
}
|
|
166
166
|
case Number:
|
|
167
167
|
case "number": {
|
|
168
|
-
this[i] =
|
|
169
|
-
|
|
168
|
+
this[i] =
|
|
169
|
+
_.isNumber(args.args[i]) ?
|
|
170
|
+
args.args[i]
|
|
170
171
|
: Number(args.args[i]);
|
|
171
172
|
break;
|
|
172
173
|
}
|
|
173
174
|
case Boolean:
|
|
174
175
|
case "boolean": {
|
|
175
|
-
this[i] =
|
|
176
|
-
|
|
176
|
+
this[i] =
|
|
177
|
+
_.isBoolean(args.args[i]) ?
|
|
178
|
+
args.args[i]
|
|
177
179
|
: args.args[i] == "true" ||
|
|
178
180
|
args.args[i] == 1 ||
|
|
179
181
|
args.args[i] == "True";
|
|
@@ -186,15 +188,17 @@ export default class SpiceModel {
|
|
|
186
188
|
}
|
|
187
189
|
case Array:
|
|
188
190
|
case "array": {
|
|
189
|
-
this[i] =
|
|
190
|
-
|
|
191
|
+
this[i] =
|
|
192
|
+
_.isArray(args.args[i]) ?
|
|
193
|
+
args.args[i]
|
|
191
194
|
: JSON.parse(args.args[i]);
|
|
192
195
|
break;
|
|
193
196
|
}
|
|
194
197
|
case Object:
|
|
195
198
|
case "object": {
|
|
196
|
-
this[i] =
|
|
197
|
-
|
|
199
|
+
this[i] =
|
|
200
|
+
_.isObject(args.args[i]) ?
|
|
201
|
+
args.args[i]
|
|
198
202
|
: JSON.parse(args.args[i]);
|
|
199
203
|
break;
|
|
200
204
|
}
|
|
@@ -458,7 +462,7 @@ export default class SpiceModel {
|
|
|
458
462
|
}
|
|
459
463
|
|
|
460
464
|
async shouldForceRefresh(response) {
|
|
461
|
-
let obj = this.getCacheProviderObject();
|
|
465
|
+
let obj = this.getCacheProviderObject(this.type);
|
|
462
466
|
let monitor_record = await obj.get(`monitor::${this.type}`);
|
|
463
467
|
if (monitor_record == undefined) {
|
|
464
468
|
return false;
|
|
@@ -471,7 +475,7 @@ export default class SpiceModel {
|
|
|
471
475
|
|
|
472
476
|
setMonitor() {
|
|
473
477
|
let current_time = new Date().getTime();
|
|
474
|
-
let obj = this.getCacheProviderObject();
|
|
478
|
+
let obj = this.getCacheProviderObject(this.type);
|
|
475
479
|
let key = `monitor::${this.type}`;
|
|
476
480
|
let value = { time: current_time };
|
|
477
481
|
obj.set(key, value, { ttl: 0 });
|
|
@@ -497,7 +501,7 @@ export default class SpiceModel {
|
|
|
497
501
|
|
|
498
502
|
results = cached_results?.value;
|
|
499
503
|
if (
|
|
500
|
-
cached_results?.value
|
|
504
|
+
cached_results?.value === undefined ||
|
|
501
505
|
(await this.shouldForceRefresh(cached_results)) ||
|
|
502
506
|
this.type == "workflow"
|
|
503
507
|
) {
|
|
@@ -573,7 +577,7 @@ export default class SpiceModel {
|
|
|
573
577
|
);
|
|
574
578
|
results = cached_results?.value;
|
|
575
579
|
if (
|
|
576
|
-
cached_results?.value
|
|
580
|
+
cached_results?.value === undefined ||
|
|
577
581
|
(await this.shouldForceRefresh(cached_results))
|
|
578
582
|
) {
|
|
579
583
|
results = await this.database.multi_get(args.ids, true);
|
|
@@ -815,14 +819,14 @@ export default class SpiceModel {
|
|
|
815
819
|
columnList.map((column) => {
|
|
816
820
|
if (column === "meta().id") return undefined;
|
|
817
821
|
if (!column.startsWith("`") && !column.endsWith("`")) {
|
|
818
|
-
column = `\`${column}\``;
|
|
822
|
+
column = `\`${column.trim()}\``;
|
|
819
823
|
}
|
|
820
824
|
if (
|
|
821
825
|
column &&
|
|
822
826
|
!column.includes(".") &&
|
|
823
827
|
!column.startsWith(this.type)
|
|
824
828
|
) {
|
|
825
|
-
column = `\`${this.type}\`.${column}`;
|
|
829
|
+
column = `\`${this.type}\`.${column.trim()}`;
|
|
826
830
|
}
|
|
827
831
|
return column;
|
|
828
832
|
})
|
|
@@ -834,15 +838,37 @@ export default class SpiceModel {
|
|
|
834
838
|
}
|
|
835
839
|
|
|
836
840
|
filterResultsByColumns(data, columns) {
|
|
841
|
+
if (this.type === "user") {
|
|
842
|
+
console.log("Filter Columns::", columns);
|
|
843
|
+
}
|
|
837
844
|
if (columns && columns !== "") {
|
|
838
|
-
|
|
839
|
-
|
|
845
|
+
// Remove backticks and replace meta().id with id
|
|
846
|
+
const cleanedColumns = columns
|
|
847
|
+
.replace(/`/g, "")
|
|
848
|
+
.replace(/meta\(\)\.id/g, "id");
|
|
849
|
+
|
|
850
|
+
// Process each column by splitting on comma and trimming
|
|
851
|
+
const columnList = cleanedColumns
|
|
840
852
|
.split(",")
|
|
841
|
-
.map((
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
853
|
+
.map((col) => col.trim())
|
|
854
|
+
.map((col) => {
|
|
855
|
+
// Check for alias with AS (case-insensitive)
|
|
856
|
+
const aliasMatch = col.match(/\s+AS\s+([\w]+)/i);
|
|
857
|
+
if (aliasMatch) {
|
|
858
|
+
return aliasMatch[1].trim();
|
|
859
|
+
}
|
|
860
|
+
// Otherwise, if a dot is present, take the part after the last dot
|
|
861
|
+
if (col.includes(".")) {
|
|
862
|
+
return col.split(".").pop().trim();
|
|
863
|
+
}
|
|
864
|
+
return col;
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// Ensure that essential keys are always picked
|
|
868
|
+
const requiredKeys = ["_permissions", "_permissions_", "id"];
|
|
869
|
+
const finalKeys = [...new Set([...columnList, ...requiredKeys])];
|
|
870
|
+
|
|
871
|
+
return data.map((entry) => _.pick(entry, finalKeys));
|
|
846
872
|
}
|
|
847
873
|
return data;
|
|
848
874
|
}
|
|
@@ -899,9 +925,76 @@ export default class SpiceModel {
|
|
|
899
925
|
return str.replace(/[^a-zA-Z0-9]/g, "");
|
|
900
926
|
}
|
|
901
927
|
|
|
928
|
+
fixColumnName(columns) {
|
|
929
|
+
// Guard clause: if columns is not provided or not a string, return it as-is.
|
|
930
|
+
if (!columns || typeof columns !== "string") {
|
|
931
|
+
return columns;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// Split the columns string on commas and trim each column expression.
|
|
935
|
+
const columnList = columns.split(",").map((col) => col.trim());
|
|
936
|
+
// Object to keep track of alias usage to avoid duplicates.
|
|
937
|
+
const aliasTracker = {};
|
|
938
|
+
|
|
939
|
+
const fixedColumns = columnList.map((col) => {
|
|
940
|
+
// Check if an explicit alias is already provided.
|
|
941
|
+
const aliasRegex = /\s+AS\s+`?([\w]+)`?$/i;
|
|
942
|
+
const aliasMatch = col.match(aliasRegex);
|
|
943
|
+
let explicitAlias = aliasMatch ? aliasMatch[1] : null;
|
|
944
|
+
|
|
945
|
+
// Remove the alias clause for easier processing.
|
|
946
|
+
let colWithoutAlias =
|
|
947
|
+
explicitAlias ? col.replace(aliasRegex, "").trim() : col;
|
|
948
|
+
|
|
949
|
+
// Use regex to extract the table and column names.
|
|
950
|
+
// This regex matches optional backticks around each identifier.
|
|
951
|
+
let tableName = this.type;
|
|
952
|
+
let columnName = "";
|
|
953
|
+
const columnRegex = /^`?([\w]+)`?\.`?([\w]+)`?$/;
|
|
954
|
+
const match = colWithoutAlias.match(columnRegex);
|
|
955
|
+
if (match) {
|
|
956
|
+
tableName = match[1];
|
|
957
|
+
columnName = match[2];
|
|
958
|
+
} else {
|
|
959
|
+
// If no table prefix is found, use the column name only.
|
|
960
|
+
columnName = colWithoutAlias.replace(/`/g, "");
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Generate the alias.
|
|
964
|
+
// If the column comes from a table different from this.type, then the alias
|
|
965
|
+
// becomes tableName_columnName. Otherwise, just use the column name.
|
|
966
|
+
let newAlias = columnName;
|
|
967
|
+
if (tableName && tableName !== this.type) {
|
|
968
|
+
newAlias = `${tableName}_${columnName}`;
|
|
969
|
+
|
|
970
|
+
// If an explicit alias was provided, favor that.
|
|
971
|
+
if (explicitAlias) {
|
|
972
|
+
newAlias = explicitAlias;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Avoid duplicates by appending a count if needed.
|
|
976
|
+
if (aliasTracker.hasOwnProperty(newAlias)) {
|
|
977
|
+
aliasTracker[newAlias]++;
|
|
978
|
+
newAlias = `${newAlias}_${aliasTracker[newAlias]}`;
|
|
979
|
+
} else {
|
|
980
|
+
aliasTracker[newAlias] = 0;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// If there's no explicit alias, add an alias clause.
|
|
984
|
+
if (!explicitAlias) {
|
|
985
|
+
return `${colWithoutAlias} AS \`${newAlias}\``;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return col;
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
return fixedColumns.join(", ");
|
|
992
|
+
}
|
|
993
|
+
|
|
902
994
|
async list(args = {}) {
|
|
903
995
|
try {
|
|
904
996
|
args.columns = this.prepColumns(args.columns);
|
|
997
|
+
|
|
905
998
|
if (args.mapping_dept) this[_mapping_dept] = args.mapping_dept;
|
|
906
999
|
|
|
907
1000
|
const nestings = [
|
|
@@ -910,6 +1003,8 @@ export default class SpiceModel {
|
|
|
910
1003
|
...this.extractNestings(args?.sort, this.type),
|
|
911
1004
|
];
|
|
912
1005
|
|
|
1006
|
+
args.columns = this.fixColumnName(args.columns);
|
|
1007
|
+
|
|
913
1008
|
const mappedNestings = _.compact(
|
|
914
1009
|
_.uniq(nestings).map((nesting) => {
|
|
915
1010
|
const prop = this.props[nesting];
|
|
@@ -930,11 +1025,11 @@ export default class SpiceModel {
|
|
|
930
1025
|
if (args.is_full_text === "true" || args.is_custom_query === "true") {
|
|
931
1026
|
query = args.query;
|
|
932
1027
|
} else {
|
|
933
|
-
query =
|
|
934
|
-
? this.makeQueryFromFilter(args.filters)
|
|
935
|
-
: args.query
|
|
936
|
-
|
|
937
|
-
|
|
1028
|
+
query =
|
|
1029
|
+
args.filters ? this.makeQueryFromFilter(args.filters)
|
|
1030
|
+
: args.query ?
|
|
1031
|
+
`${args.query} AND (\`${this.type}\`.deleted = false OR \`${this.type}\`.deleted IS MISSING)`
|
|
1032
|
+
: `(\`${this.type}\`.deleted = false OR \`${this.type}\`.deleted IS MISSING)`;
|
|
938
1033
|
}
|
|
939
1034
|
|
|
940
1035
|
if (hasSQLInjection(query)) {
|
|
@@ -943,13 +1038,14 @@ export default class SpiceModel {
|
|
|
943
1038
|
|
|
944
1039
|
args.limit = Number(args.limit) || undefined;
|
|
945
1040
|
args.offset = Number(args.offset) || 0;
|
|
946
|
-
args.sort =
|
|
947
|
-
|
|
1041
|
+
args.sort =
|
|
1042
|
+
args.sort ?
|
|
1043
|
+
args.sort
|
|
948
1044
|
.split(",")
|
|
949
1045
|
.map((item) =>
|
|
950
|
-
item.includes(".")
|
|
951
|
-
|
|
952
|
-
|
|
1046
|
+
item.includes(".") ? item : (
|
|
1047
|
+
`\`${this.type}\`.${this.formatSortComponent(item)}`
|
|
1048
|
+
)
|
|
953
1049
|
)
|
|
954
1050
|
.join(",")
|
|
955
1051
|
: `\`${this.type}\`.created_at DESC`;
|
|
@@ -969,14 +1065,15 @@ export default class SpiceModel {
|
|
|
969
1065
|
);
|
|
970
1066
|
results = cachedResults?.value;
|
|
971
1067
|
|
|
972
|
-
if (
|
|
973
|
-
results
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1068
|
+
if (cachedResults?.value === undefined)
|
|
1069
|
+
if (!results || (await this.shouldForceRefresh(cachedResults))) {
|
|
1070
|
+
results = await this.fetchResults(args, query);
|
|
1071
|
+
await this.getCacheProviderObject(this.type).set(
|
|
1072
|
+
cacheKey,
|
|
1073
|
+
{ value: results, time: new Date().getTime() },
|
|
1074
|
+
this.getCacheConfig(this.type)
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
980
1077
|
} else {
|
|
981
1078
|
results = await this.fetchResults(args, query);
|
|
982
1079
|
}
|
|
@@ -991,6 +1088,8 @@ export default class SpiceModel {
|
|
|
991
1088
|
);
|
|
992
1089
|
}
|
|
993
1090
|
|
|
1091
|
+
if (this.type == "user") console.log("results", results.data);
|
|
1092
|
+
|
|
994
1093
|
if (args.skip_hooks !== true) {
|
|
995
1094
|
await this.run_hook(results.data, "list", "after");
|
|
996
1095
|
}
|
|
@@ -1238,9 +1337,9 @@ export default class SpiceModel {
|
|
|
1238
1337
|
execute: async (data) => {
|
|
1239
1338
|
return await this.mapToObject(
|
|
1240
1339
|
data,
|
|
1241
|
-
_.isString(properties[i].map.reference)
|
|
1242
|
-
|
|
1243
|
-
|
|
1340
|
+
_.isString(properties[i].map.reference) ?
|
|
1341
|
+
spice.models[properties[i].map.reference]
|
|
1342
|
+
: properties[i].map.reference,
|
|
1244
1343
|
i,
|
|
1245
1344
|
properties[i].map.destination || i,
|
|
1246
1345
|
properties[i]
|
|
@@ -1256,9 +1355,9 @@ export default class SpiceModel {
|
|
|
1256
1355
|
execute: async (data) => {
|
|
1257
1356
|
return await this.mapToObjectArray(
|
|
1258
1357
|
data,
|
|
1259
|
-
_.isString(properties[i].map.reference)
|
|
1260
|
-
|
|
1261
|
-
|
|
1358
|
+
_.isString(properties[i].map.reference) ?
|
|
1359
|
+
spice.models[properties[i].map.reference]
|
|
1360
|
+
: properties[i].map.reference,
|
|
1262
1361
|
i,
|
|
1263
1362
|
properties[i].map.destination || i,
|
|
1264
1363
|
properties[i]
|
|
@@ -1278,76 +1377,71 @@ export default class SpiceModel {
|
|
|
1278
1377
|
}
|
|
1279
1378
|
}
|
|
1280
1379
|
|
|
1281
|
-
async do_serialize(data, type, old_data, args, path_to_be_removed) {
|
|
1282
|
-
//console.log("CTX INside Model DO", this[_ctx]);
|
|
1380
|
+
async do_serialize(data, type, old_data, args, path_to_be_removed = []) {
|
|
1283
1381
|
try {
|
|
1284
|
-
// run
|
|
1382
|
+
// Early exit if serialization should not run.
|
|
1383
|
+
if (!this.shouldSerializerRun(args, type)) {
|
|
1384
|
+
return data;
|
|
1385
|
+
}
|
|
1285
1386
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1387
|
+
// Add external modifiers only once.
|
|
1388
|
+
if (this.type && !this[_external_modifier_loaded]) {
|
|
1389
|
+
this.addExternalModifiers(this.type);
|
|
1390
|
+
this[_external_modifier_loaded] = true;
|
|
1288
1391
|
}
|
|
1289
1392
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
this[
|
|
1295
|
-
) {
|
|
1296
|
-
|
|
1297
|
-
this[_external_modifier_loaded] = true;
|
|
1298
|
-
}
|
|
1299
|
-
for (let i of this[_serializers][type]["modifiers"]) {
|
|
1300
|
-
try {
|
|
1301
|
-
data = await i(data, old_data, this[_ctx], this.type);
|
|
1302
|
-
} catch (e) {
|
|
1303
|
-
console.log(e.stack);
|
|
1304
|
-
}
|
|
1393
|
+
// Cache the modifiers lookup for the specified type.
|
|
1394
|
+
const modifiers = this[_serializers]?.[type]?.modifiers || [];
|
|
1395
|
+
for (const modifier of modifiers) {
|
|
1396
|
+
try {
|
|
1397
|
+
data = await modifier(data, old_data, this[_ctx], this.type);
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
console.error("Modifier error in do_serialize:", error.stack);
|
|
1305
1400
|
}
|
|
1401
|
+
}
|
|
1306
1402
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1403
|
+
// Ensure data is always an array for consistent processing.
|
|
1404
|
+
const originalIsArray = Array.isArray(data);
|
|
1405
|
+
if (!originalIsArray) {
|
|
1406
|
+
data = [data];
|
|
1407
|
+
}
|
|
1309
1408
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1409
|
+
// Compute the defaults from properties using reduce.
|
|
1410
|
+
const defaults = Object.keys(this.props).reduce((acc, key) => {
|
|
1411
|
+
const def = this.props[key]?.defaults?.[type];
|
|
1412
|
+
if (def !== undefined) {
|
|
1413
|
+
acc[key] =
|
|
1414
|
+
_.isFunction(def) ?
|
|
1415
|
+
def({ old_data: data, new_data: old_data })
|
|
1416
|
+
: def;
|
|
1312
1417
|
}
|
|
1418
|
+
return acc;
|
|
1419
|
+
}, {});
|
|
1313
1420
|
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
for (let i in this.props) {
|
|
1317
|
-
if (
|
|
1318
|
-
this.props[i].defaults != undefined &&
|
|
1319
|
-
this.props[i].defaults[type] != undefined &&
|
|
1320
|
-
this.props[i].defaults[type] != undefined
|
|
1321
|
-
) {
|
|
1322
|
-
defaults[i] = _.isFunction(this.props[i].defaults[type])
|
|
1323
|
-
? this.props[i].defaults[type]({
|
|
1324
|
-
old_data: data,
|
|
1325
|
-
new_data: old_data,
|
|
1326
|
-
})
|
|
1327
|
-
: this.props[i].defaults[type];
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
// apply defaults
|
|
1331
|
-
for (let i of data) {
|
|
1332
|
-
_.defaults(i, defaults);
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
// apply cleaners
|
|
1336
|
-
if (type == "read") {
|
|
1337
|
-
let props_to_clean = ["deleted", "type", ...path_to_be_removed];
|
|
1338
|
-
for (let i in this.props) {
|
|
1339
|
-
if (this.props[i].hide) {
|
|
1340
|
-
props_to_clean.push(i);
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
data = _.map(data, (obj) => _.omit(obj, props_to_clean));
|
|
1344
|
-
}
|
|
1421
|
+
// Merge defaults into each object.
|
|
1422
|
+
data = data.map((item) => _.defaults(item, defaults));
|
|
1345
1423
|
|
|
1346
|
-
|
|
1424
|
+
// If type is "read", clean the data by omitting certain props.
|
|
1425
|
+
if (type === "read") {
|
|
1426
|
+
// Collect hidden properties from schema.
|
|
1427
|
+
const hiddenProps = Object.keys(this.props).filter(
|
|
1428
|
+
(key) => this.props[key]?.hide
|
|
1429
|
+
);
|
|
1430
|
+
// Combine default props to remove.
|
|
1431
|
+
const propsToClean = [
|
|
1432
|
+
"deleted",
|
|
1433
|
+
"type",
|
|
1434
|
+
...path_to_be_removed,
|
|
1435
|
+
...hiddenProps,
|
|
1436
|
+
];
|
|
1437
|
+
data = data.map((item) => _.omit(item, propsToClean));
|
|
1347
1438
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1439
|
+
|
|
1440
|
+
// Return in the original format (array or single object).
|
|
1441
|
+
return originalIsArray ? data : data[0];
|
|
1442
|
+
} catch (error) {
|
|
1443
|
+
console.error("Error in do_serialize:", error.stack);
|
|
1444
|
+
throw error;
|
|
1351
1445
|
}
|
|
1352
1446
|
}
|
|
1353
1447
|
}
|