@carbonorm/carbonnode 6.0.10 → 6.0.13
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/dist/executors/SqlExecutor.d.ts +6 -0
- package/dist/handlers/ExpressHandler.d.ts +8 -9
- package/dist/index.cjs.js +324 -108
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +324 -109
- package/dist/index.esm.js.map +1 -1
- package/dist/types/ormInterfaces.d.ts +25 -5
- package/dist/utils/cacheManager.d.ts +2 -3
- package/package.json +1 -1
- package/src/__tests__/convertForRequestBody.test.ts +58 -0
- package/src/__tests__/expressServer.e2e.test.ts +62 -38
- package/src/__tests__/fixtures/createTestServer.ts +7 -3
- package/src/__tests__/httpExecutorSingular.e2e.test.ts +97 -60
- package/src/__tests__/logSql.test.ts +13 -0
- package/src/__tests__/sakila-db/C6.js +1 -1
- package/src/__tests__/sakila-db/C6.mysqldump.json +1 -1
- package/src/__tests__/sakila-db/C6.mysqldump.sql +1 -1
- package/src/__tests__/sakila-db/C6.sqlAllowList.json +11 -11
- package/src/__tests__/sakila-db/C6.ts +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +4 -4
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +6 -6
- package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +6 -6
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +5 -5
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +2 -2
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +4 -4
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +3 -3
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +3 -3
- package/src/__tests__/sakila.generated.test.ts +11 -3
- package/src/__tests__/sqlBuilders.test.ts +46 -0
- package/src/__tests__/sqlExecutorLifecycleHooks.test.ts +122 -0
- package/src/api/convertForRequestBody.ts +9 -2
- package/src/api/restRequest.ts +1 -0
- package/src/executors/HttpExecutor.ts +1 -1
- package/src/executors/SqlExecutor.ts +252 -49
- package/src/handlers/ExpressHandler.ts +50 -24
- package/src/orm/builders/ConditionBuilder.ts +43 -1
- package/src/orm/queries/PostQueryBuilder.ts +24 -12
- package/src/types/ormInterfaces.ts +31 -5
- package/src/utils/cacheManager.ts +3 -4
- package/src/utils/colorSql.ts +18 -0
|
@@ -18,5 +18,11 @@ export declare class SqlExecutor<G extends OrmGenerics> extends Executor<G> {
|
|
|
18
18
|
private extractPrimaryKeyValuesFromData;
|
|
19
19
|
private broadcastWebsocketIfConfigured;
|
|
20
20
|
runQuery(): Promise<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
|
|
21
|
+
private getQueryBuilder;
|
|
22
|
+
private buildSqlExecutionContext;
|
|
23
|
+
private createResponseFromQueryResult;
|
|
24
|
+
private createLifecycleHookResponse;
|
|
25
|
+
private createCacheResponseEnvelope;
|
|
26
|
+
private executeQueryWithLifecycle;
|
|
21
27
|
private validateSqlAllowList;
|
|
22
28
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { Request, Response,
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
export declare function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
1
|
+
import type { Request, Response, Router } from "express";
|
|
2
|
+
import { iRest } from "../types/ormInterfaces";
|
|
3
|
+
import { OrmGenerics } from "../types/ormGenerics";
|
|
4
|
+
export declare function restExpressRequest<G extends OrmGenerics>(routerConfig: {
|
|
5
|
+
router: Pick<Router, "all">;
|
|
6
|
+
routePath?: string;
|
|
7
|
+
} & Omit<iRest<G['RestShortTableName'], G['RestTableInterface']>, "requestMethod" | "restModel">): void;
|
|
8
|
+
export declare function ExpressHandler<G extends OrmGenerics>(configX: (() => Omit<iRest<G['RestShortTableName'], G['RestTableInterface']>, "requestMethod" | "restModel">) | Omit<iRest<G['RestShortTableName'], G['RestTableInterface']>, "requestMethod" | "restModel">): (req: Request, res: Response) => Promise<void>;
|
package/dist/index.cjs.js
CHANGED
|
@@ -536,7 +536,11 @@ var logWithLevel = function (requiredLevel, context, logger) {
|
|
|
536
536
|
};
|
|
537
537
|
|
|
538
538
|
function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler) {
|
|
539
|
-
if (regexErrorHandler === void 0) { regexErrorHandler =
|
|
539
|
+
if (regexErrorHandler === void 0) { regexErrorHandler = function (message) {
|
|
540
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.alert === "function") {
|
|
541
|
+
globalThis.alert(message);
|
|
542
|
+
}
|
|
543
|
+
}; }
|
|
540
544
|
var payload = {};
|
|
541
545
|
var tableNames = Array.isArray(tableName) ? tableName : [tableName];
|
|
542
546
|
var tableDefinitions = tableNames.map(function (name) {
|
|
@@ -559,7 +563,8 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
|
|
|
559
563
|
C6Constants.DELETE,
|
|
560
564
|
C6Constants.WHERE,
|
|
561
565
|
C6Constants.JOIN,
|
|
562
|
-
C6Constants.PAGINATION
|
|
566
|
+
C6Constants.PAGINATION,
|
|
567
|
+
"cacheResults",
|
|
563
568
|
].includes(value)) {
|
|
564
569
|
var val_1 = restfulObject[value];
|
|
565
570
|
if (Array.isArray(val_1)) {
|
|
@@ -573,6 +578,9 @@ function convertForRequestBody (restfulObject, tableName, C6, regexErrorHandler)
|
|
|
573
578
|
return (tslib.__assign(tslib.__assign({}, acc), (_a = {}, _a[key] = val_1[key], _a)));
|
|
574
579
|
}, {});
|
|
575
580
|
}
|
|
581
|
+
else {
|
|
582
|
+
payload[value] = val_1;
|
|
583
|
+
}
|
|
576
584
|
return "continue";
|
|
577
585
|
}
|
|
578
586
|
if (shortReference in tableDefinition) {
|
|
@@ -904,7 +912,7 @@ function checkCache(method, tableName, requestData) {
|
|
|
904
912
|
var cached = apiRequestCache.get(key);
|
|
905
913
|
if (!cached)
|
|
906
914
|
return false;
|
|
907
|
-
if (shouldLog(exports.LogLevel.
|
|
915
|
+
if (shouldLog(exports.LogLevel.INFO, undefined)) {
|
|
908
916
|
console.groupCollapsed("%c API cache hit for ".concat(method, " ").concat(tableName), "color:#0c0");
|
|
909
917
|
console.log("Request Data:", requestData);
|
|
910
918
|
console.groupEnd();
|
|
@@ -2131,14 +2139,51 @@ var ConditionBuilder = /** @class */ (function (_super) {
|
|
|
2131
2139
|
return typeof mysqlType === 'string' && mysqlType.toLowerCase().includes('json');
|
|
2132
2140
|
};
|
|
2133
2141
|
ConditionBuilder.prototype.serializeUpdateValue = function (value, params, contextColumn) {
|
|
2142
|
+
var _a;
|
|
2143
|
+
var _this = this;
|
|
2134
2144
|
var normalized = value instanceof Map ? Object.fromEntries(value) : value;
|
|
2135
2145
|
var allowColumnRefs = this.isJsonColumn(contextColumn);
|
|
2136
2146
|
if (this.isPlainArrayLiteral(normalized, allowColumnRefs)
|
|
2137
2147
|
|| this.isPlainObjectLiteral(normalized, allowColumnRefs)) {
|
|
2138
2148
|
return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
|
|
2139
2149
|
}
|
|
2140
|
-
var
|
|
2150
|
+
var sql;
|
|
2151
|
+
var isReference;
|
|
2152
|
+
var isExpression;
|
|
2153
|
+
var isSubSelect;
|
|
2154
|
+
var shouldStringifyObjectFallback = function (candidate) {
|
|
2155
|
+
if (typeof candidate !== 'object'
|
|
2156
|
+
|| candidate === null
|
|
2157
|
+
|| candidate instanceof Date
|
|
2158
|
+
|| (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(candidate))) {
|
|
2159
|
+
return false;
|
|
2160
|
+
}
|
|
2161
|
+
var normalizedCandidate = candidate instanceof Map
|
|
2162
|
+
? Object.fromEntries(candidate)
|
|
2163
|
+
: candidate;
|
|
2164
|
+
var entries = Object.entries(normalizedCandidate);
|
|
2165
|
+
if (entries.length !== 1) {
|
|
2166
|
+
return true;
|
|
2167
|
+
}
|
|
2168
|
+
var key = entries[0][0];
|
|
2169
|
+
if (_this.isOperator(key) || _this.BOOLEAN_OPERATORS.has(key)) {
|
|
2170
|
+
return false;
|
|
2171
|
+
}
|
|
2172
|
+
return true;
|
|
2173
|
+
};
|
|
2174
|
+
try {
|
|
2175
|
+
(_a = this.serializeOperand(normalized, params, contextColumn), sql = _a.sql, isReference = _a.isReference, isExpression = _a.isExpression, isSubSelect = _a.isSubSelect);
|
|
2176
|
+
}
|
|
2177
|
+
catch (err) {
|
|
2178
|
+
if (shouldStringifyObjectFallback(normalized)) {
|
|
2179
|
+
return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
|
|
2180
|
+
}
|
|
2181
|
+
throw err;
|
|
2182
|
+
}
|
|
2141
2183
|
if (!isReference && !isExpression && !isSubSelect && typeof normalized === 'object' && normalized !== null) {
|
|
2184
|
+
if (shouldStringifyObjectFallback(normalized)) {
|
|
2185
|
+
return this.addParam(params, contextColumn !== null && contextColumn !== void 0 ? contextColumn : '', JSON.stringify(normalized));
|
|
2186
|
+
}
|
|
2142
2187
|
throw new Error('Unsupported operand type in SQL expression.');
|
|
2143
2188
|
}
|
|
2144
2189
|
return sql;
|
|
@@ -2952,11 +2997,27 @@ function collapseBinds(sql) {
|
|
|
2952
2997
|
return "".concat(C$1.DIM, "? \u00D7").concat(count).concat(RESET);
|
|
2953
2998
|
});
|
|
2954
2999
|
}
|
|
3000
|
+
/**
|
|
3001
|
+
* ( ? ×9 ), ( ? ×9 ), ( ? ×9 ) -> ( ? ×9 ) ×3
|
|
3002
|
+
*/
|
|
3003
|
+
function collapseRepeatedValueRows(sql) {
|
|
3004
|
+
var repeatedRowPattern = /(\((?:\x1b\[[0-9;]*m)?\?\s*×\d+(?:\x1b\[[0-9;]*m)?\)|\(\s*(?:\?\s*,\s*)+\?\s*\))(?:\s*,\s*\1){2,}/g;
|
|
3005
|
+
return sql.replace(repeatedRowPattern, function (match, row) {
|
|
3006
|
+
var _a, _b;
|
|
3007
|
+
var rowMatches = match.match(new RegExp(row.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"));
|
|
3008
|
+
var count = (_a = rowMatches === null || rowMatches === void 0 ? void 0 : rowMatches.length) !== null && _a !== void 0 ? _a : 1;
|
|
3009
|
+
var normalizedRow = row.includes("×")
|
|
3010
|
+
? row
|
|
3011
|
+
: "(".concat(C$1.DIM, "? \u00D7").concat(((_b = row.match(/\?/g)) !== null && _b !== void 0 ? _b : []).length).concat(RESET, ")");
|
|
3012
|
+
return "".concat(normalizedRow, " ").concat(C$1.DIM, "\u00D7").concat(count).concat(RESET);
|
|
3013
|
+
});
|
|
3014
|
+
}
|
|
2955
3015
|
/* ---------- main formatter ---------- */
|
|
2956
3016
|
function colorSql(sql) {
|
|
2957
3017
|
var s = sql.trim();
|
|
2958
3018
|
/* 1️⃣ collapse bind noise */
|
|
2959
3019
|
s = collapseBinds(s);
|
|
3020
|
+
s = collapseRepeatedValueRows(s);
|
|
2960
3021
|
/* 2️⃣ table.column coloring (core visual grouping) */
|
|
2961
3022
|
s = s.replace(/\b(`?\w+`?)\.(\w+)\b/g, function (_, table, column) {
|
|
2962
3023
|
return "".concat(tableColor(table)).concat(table).concat(RESET, ".") +
|
|
@@ -2973,7 +3034,7 @@ function colorSql(sql) {
|
|
|
2973
3034
|
return s;
|
|
2974
3035
|
}
|
|
2975
3036
|
|
|
2976
|
-
var version = "6.0.
|
|
3037
|
+
var version = "6.0.13";
|
|
2977
3038
|
|
|
2978
3039
|
var DEFAULT_STEP = 8;
|
|
2979
3040
|
function parseSemver(version) {
|
|
@@ -3160,21 +3221,35 @@ var PostQueryBuilder = /** @class */ (function (_super) {
|
|
|
3160
3221
|
};
|
|
3161
3222
|
PostQueryBuilder.prototype.build = function (table) {
|
|
3162
3223
|
var _this = this;
|
|
3224
|
+
var _a, _b;
|
|
3163
3225
|
this.aliasMap = {};
|
|
3164
3226
|
var verb = C6C.REPLACE in this.request ? C6C.REPLACE : C6C.INSERT;
|
|
3165
|
-
var
|
|
3166
|
-
|
|
3227
|
+
var directRows = Array.isArray(this.request)
|
|
3228
|
+
? this.request
|
|
3229
|
+
: [];
|
|
3230
|
+
var rows = directRows.length > 0
|
|
3231
|
+
? directRows
|
|
3232
|
+
: Array.isArray(this.request.dataInsertMultipleRows) &&
|
|
3233
|
+
this.request.dataInsertMultipleRows.length > 0
|
|
3234
|
+
? this.request.dataInsertMultipleRows
|
|
3235
|
+
: [verb in this.request ? this.request[verb] : this.request];
|
|
3236
|
+
var keys = Object.keys((_a = rows[0]) !== null && _a !== void 0 ? _a : {});
|
|
3167
3237
|
var params = this.useNamedParams ? {} : [];
|
|
3168
|
-
var
|
|
3169
|
-
for (var _i = 0,
|
|
3170
|
-
var
|
|
3171
|
-
var
|
|
3172
|
-
var
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3238
|
+
var rowPlaceholders = [];
|
|
3239
|
+
for (var _i = 0, rows_1 = rows; _i < rows_1.length; _i++) {
|
|
3240
|
+
var row = rows_1[_i];
|
|
3241
|
+
var placeholders = [];
|
|
3242
|
+
for (var _c = 0, keys_1 = keys; _c < keys_1.length; _c++) {
|
|
3243
|
+
var key = keys_1[_c];
|
|
3244
|
+
var value = (_b = row[key]) !== null && _b !== void 0 ? _b : null;
|
|
3245
|
+
var trimmed = this.trimTablePrefix(table, key);
|
|
3246
|
+
var qualified = "".concat(table, ".").concat(trimmed);
|
|
3247
|
+
var placeholder = this.serializeUpdateValue(value, params, qualified);
|
|
3248
|
+
placeholders.push(placeholder);
|
|
3249
|
+
}
|
|
3250
|
+
rowPlaceholders.push("(".concat(placeholders.join(', '), ")"));
|
|
3176
3251
|
}
|
|
3177
|
-
var sql = "".concat(verb, " INTO `").concat(table, "` (\n ").concat(keys.map(function (k) { return "`".concat(_this.trimTablePrefix(table, k), "`"); }).join(', '), "\n ) VALUES
|
|
3252
|
+
var sql = "".concat(verb, " INTO `").concat(table, "` (\n ").concat(keys.map(function (k) { return "`".concat(_this.trimTablePrefix(table, k), "`"); }).join(', '), "\n ) VALUES\n ").concat(rowPlaceholders.join(',\n '));
|
|
3178
3253
|
if (C6C.UPDATE in this.request) {
|
|
3179
3254
|
var updateData = this.request[C6C.UPDATE];
|
|
3180
3255
|
if (!Array.isArray(updateData)) {
|
|
@@ -3494,12 +3569,18 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
3494
3569
|
}
|
|
3495
3570
|
SqlExecutor.prototype.execute = function () {
|
|
3496
3571
|
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
3497
|
-
var TABLE_NAME, method, logContext, _a, rest, result, result, result;
|
|
3572
|
+
var TABLE_NAME, method, logContext, response, _a, rest, getResponse, restRows, result, result, result;
|
|
3498
3573
|
return tslib.__generator(this, function (_b) {
|
|
3499
3574
|
switch (_b.label) {
|
|
3500
3575
|
case 0:
|
|
3501
3576
|
TABLE_NAME = this.config.restModel.TABLE_NAME;
|
|
3502
3577
|
method = this.config.requestMethod;
|
|
3578
|
+
return [4 /*yield*/, this.runLifecycleHooks("beforeProcessing", {
|
|
3579
|
+
config: this.config,
|
|
3580
|
+
request: this.request,
|
|
3581
|
+
})];
|
|
3582
|
+
case 1:
|
|
3583
|
+
_b.sent();
|
|
3503
3584
|
// Normalize singular T-shaped requests into complex ORM shape (GET/PUT/DELETE)
|
|
3504
3585
|
try {
|
|
3505
3586
|
this.request = normalizeSingularRequest(method, this.request, this.config.restModel, undefined);
|
|
@@ -3513,45 +3594,54 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
3513
3594
|
logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE9 Request:", this.request);
|
|
3514
3595
|
_a = method;
|
|
3515
3596
|
switch (_a) {
|
|
3516
|
-
case 'GET': return [3 /*break*/,
|
|
3517
|
-
case 'POST': return [3 /*break*/,
|
|
3518
|
-
case 'PUT': return [3 /*break*/,
|
|
3519
|
-
case 'DELETE': return [3 /*break*/,
|
|
3597
|
+
case 'GET': return [3 /*break*/, 2];
|
|
3598
|
+
case 'POST': return [3 /*break*/, 4];
|
|
3599
|
+
case 'PUT': return [3 /*break*/, 7];
|
|
3600
|
+
case 'DELETE': return [3 /*break*/, 10];
|
|
3520
3601
|
}
|
|
3521
|
-
return [3 /*break*/,
|
|
3522
|
-
case
|
|
3523
|
-
case
|
|
3602
|
+
return [3 /*break*/, 13];
|
|
3603
|
+
case 2: return [4 /*yield*/, this.runQuery()];
|
|
3604
|
+
case 3:
|
|
3524
3605
|
rest = _b.sent();
|
|
3525
3606
|
if (this.config.reactBootstrap) {
|
|
3607
|
+
getResponse = rest;
|
|
3608
|
+
restRows = Array.isArray(getResponse.rest)
|
|
3609
|
+
? getResponse.rest
|
|
3610
|
+
: [getResponse.rest];
|
|
3526
3611
|
this.config.reactBootstrap.updateRestfulObjectArrays({
|
|
3527
|
-
dataOrCallback:
|
|
3612
|
+
dataOrCallback: restRows,
|
|
3528
3613
|
stateKey: this.config.restModel.TABLE_NAME,
|
|
3529
3614
|
uniqueObjectId: this.config.restModel.PRIMARY_SHORT,
|
|
3530
3615
|
});
|
|
3531
3616
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
case 4:
|
|
3617
|
+
response = rest;
|
|
3618
|
+
return [3 /*break*/, 14];
|
|
3619
|
+
case 4: return [4 /*yield*/, this.runQuery()];
|
|
3620
|
+
case 5:
|
|
3535
3621
|
result = _b.sent();
|
|
3536
3622
|
return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
|
|
3537
|
-
case
|
|
3623
|
+
case 6:
|
|
3538
3624
|
_b.sent();
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
case 7:
|
|
3625
|
+
response = result;
|
|
3626
|
+
return [3 /*break*/, 14];
|
|
3627
|
+
case 7: return [4 /*yield*/, this.runQuery()];
|
|
3628
|
+
case 8:
|
|
3542
3629
|
result = _b.sent();
|
|
3543
3630
|
return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
|
|
3544
|
-
case
|
|
3631
|
+
case 9:
|
|
3545
3632
|
_b.sent();
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
case 10:
|
|
3633
|
+
response = result;
|
|
3634
|
+
return [3 /*break*/, 14];
|
|
3635
|
+
case 10: return [4 /*yield*/, this.runQuery()];
|
|
3636
|
+
case 11:
|
|
3549
3637
|
result = _b.sent();
|
|
3550
3638
|
return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
|
|
3551
|
-
case
|
|
3639
|
+
case 12:
|
|
3552
3640
|
_b.sent();
|
|
3553
|
-
|
|
3554
|
-
|
|
3641
|
+
response = result;
|
|
3642
|
+
return [3 /*break*/, 14];
|
|
3643
|
+
case 13: throw new Error("Unsupported request method: ".concat(method));
|
|
3644
|
+
case 14: return [2 /*return*/, response];
|
|
3555
3645
|
}
|
|
3556
3646
|
});
|
|
3557
3647
|
});
|
|
@@ -3848,65 +3938,182 @@ var SqlExecutor = /** @class */ (function (_super) {
|
|
|
3848
3938
|
};
|
|
3849
3939
|
SqlExecutor.prototype.runQuery = function () {
|
|
3850
3940
|
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
3851
|
-
var
|
|
3941
|
+
var method, tableName, logContext, cacheResults, cacheRequestData, requestArgumentsSerialized, cachedRequest, sqlExecution, queryPromise, cacheRequest, cacheResponse;
|
|
3852
3942
|
var _this = this;
|
|
3853
|
-
|
|
3854
|
-
|
|
3943
|
+
var _a, _b;
|
|
3944
|
+
return tslib.__generator(this, function (_c) {
|
|
3945
|
+
switch (_c.label) {
|
|
3855
3946
|
case 0:
|
|
3856
|
-
TABLE_NAME = this.config.restModel.TABLE_NAME;
|
|
3857
3947
|
method = this.config.requestMethod;
|
|
3858
|
-
|
|
3859
|
-
case 'GET':
|
|
3860
|
-
builder = new SelectQueryBuilder(this.config, this.request);
|
|
3861
|
-
break;
|
|
3862
|
-
case 'PUT':
|
|
3863
|
-
builder = new UpdateQueryBuilder(this.config, this.request);
|
|
3864
|
-
break;
|
|
3865
|
-
case 'DELETE':
|
|
3866
|
-
builder = new DeleteQueryBuilder(this.config, this.request);
|
|
3867
|
-
break;
|
|
3868
|
-
case 'POST':
|
|
3869
|
-
builder = new PostQueryBuilder(this.config, this.request);
|
|
3870
|
-
break;
|
|
3871
|
-
default:
|
|
3872
|
-
throw new Error("Unsupported query method: ".concat(method));
|
|
3873
|
-
}
|
|
3874
|
-
QueryResult = builder.build(TABLE_NAME);
|
|
3948
|
+
tableName = this.config.restModel.TABLE_NAME;
|
|
3875
3949
|
logContext = getLogContext(this.config, this.request);
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3950
|
+
cacheResults = method === C6Constants.GET
|
|
3951
|
+
&& !this.config.sqlAllowListPath
|
|
3952
|
+
&& ((_a = this.request) === null || _a === void 0 ? void 0 : _a.cacheResults) !== false;
|
|
3953
|
+
cacheRequestData = cacheResults
|
|
3954
|
+
? JSON.parse(JSON.stringify((_b = this.request) !== null && _b !== void 0 ? _b : {}))
|
|
3955
|
+
: undefined;
|
|
3956
|
+
requestArgumentsSerialized = cacheResults
|
|
3957
|
+
? sortAndSerializeQueryObject(tableName, cacheRequestData !== null && cacheRequestData !== void 0 ? cacheRequestData : {})
|
|
3958
|
+
: undefined;
|
|
3959
|
+
if (!cacheResults) return [3 /*break*/, 2];
|
|
3960
|
+
cachedRequest = checkCache(method, tableName, cacheRequestData);
|
|
3961
|
+
if (!cachedRequest) return [3 /*break*/, 2];
|
|
3962
|
+
return [4 /*yield*/, cachedRequest];
|
|
3963
|
+
case 1: return [2 /*return*/, (_c.sent()).data];
|
|
3964
|
+
case 2:
|
|
3965
|
+
sqlExecution = this.buildSqlExecutionContext(method, tableName, logContext);
|
|
3966
|
+
queryPromise = this.withConnection(function (conn) { return tslib.__awaiter(_this, void 0, void 0, function () { return tslib.__generator(this, function (_a) {
|
|
3967
|
+
return [2 /*return*/, this.executeQueryWithLifecycle(conn, method, sqlExecution, logContext)];
|
|
3968
|
+
}); }); });
|
|
3969
|
+
if (!(!cacheResults || !cacheRequestData || !requestArgumentsSerialized)) return [3 /*break*/, 4];
|
|
3970
|
+
return [4 /*yield*/, queryPromise];
|
|
3971
|
+
case 3: return [2 /*return*/, _c.sent()];
|
|
3972
|
+
case 4:
|
|
3973
|
+
cacheRequest = queryPromise.then(function (data) {
|
|
3974
|
+
return _this.createCacheResponseEnvelope(method, tableName, data);
|
|
3975
|
+
});
|
|
3976
|
+
setCache(method, tableName, cacheRequestData, {
|
|
3977
|
+
requestArgumentsSerialized: requestArgumentsSerialized,
|
|
3978
|
+
request: cacheRequest,
|
|
3979
|
+
});
|
|
3980
|
+
return [4 /*yield*/, cacheRequest];
|
|
3981
|
+
case 5:
|
|
3982
|
+
cacheResponse = _c.sent();
|
|
3983
|
+
setCache(method, tableName, cacheRequestData, {
|
|
3984
|
+
requestArgumentsSerialized: requestArgumentsSerialized,
|
|
3985
|
+
request: cacheRequest,
|
|
3986
|
+
response: cacheResponse,
|
|
3987
|
+
final: true,
|
|
3988
|
+
});
|
|
3989
|
+
return [2 /*return*/, cacheResponse.data];
|
|
3990
|
+
}
|
|
3991
|
+
});
|
|
3992
|
+
});
|
|
3993
|
+
};
|
|
3994
|
+
SqlExecutor.prototype.getQueryBuilder = function (method) {
|
|
3995
|
+
switch (method) {
|
|
3996
|
+
case C6Constants.GET:
|
|
3997
|
+
return new SelectQueryBuilder(this.config, this.request);
|
|
3998
|
+
case C6Constants.PUT:
|
|
3999
|
+
return new UpdateQueryBuilder(this.config, this.request);
|
|
4000
|
+
case C6Constants.DELETE:
|
|
4001
|
+
return new DeleteQueryBuilder(this.config, this.request);
|
|
4002
|
+
case C6Constants.POST:
|
|
4003
|
+
return new PostQueryBuilder(this.config, this.request);
|
|
4004
|
+
default:
|
|
4005
|
+
throw new Error("Unsupported query method: ".concat(method));
|
|
4006
|
+
}
|
|
4007
|
+
};
|
|
4008
|
+
SqlExecutor.prototype.buildSqlExecutionContext = function (method, tableName, logContext) {
|
|
4009
|
+
var builder = this.getQueryBuilder(method);
|
|
4010
|
+
var queryResult = builder.build(tableName);
|
|
4011
|
+
logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Generated ".concat(method.toUpperCase(), " SQL:"), queryResult);
|
|
4012
|
+
var formatted = this.formatSQLWithParams(queryResult.sql, queryResult.params);
|
|
4013
|
+
logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Formatted ".concat(method.toUpperCase(), " SQL:"), formatted);
|
|
4014
|
+
var toUnnamed = namedPlaceholders();
|
|
4015
|
+
var _a = toUnnamed(queryResult.sql, queryResult.params), sql = _a[0], values = _a[1];
|
|
4016
|
+
return { sql: sql, values: values };
|
|
4017
|
+
};
|
|
4018
|
+
SqlExecutor.prototype.createResponseFromQueryResult = function (method, result, sqlExecution, logContext) {
|
|
4019
|
+
if (method === C6Constants.GET) {
|
|
4020
|
+
return {
|
|
4021
|
+
rest: result.map(this.serialize),
|
|
4022
|
+
sql: { sql: sqlExecution.sql, values: sqlExecution.values },
|
|
4023
|
+
};
|
|
4024
|
+
}
|
|
4025
|
+
logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \u270F\uFE0F Rows affected:", result.affectedRows);
|
|
4026
|
+
return {
|
|
4027
|
+
affected: result.affectedRows,
|
|
4028
|
+
insertId: result.insertId,
|
|
4029
|
+
rest: [],
|
|
4030
|
+
sql: { sql: sqlExecution.sql, values: sqlExecution.values },
|
|
4031
|
+
};
|
|
4032
|
+
};
|
|
4033
|
+
SqlExecutor.prototype.createLifecycleHookResponse = function (response) {
|
|
4034
|
+
var data = Object.assign({ success: true }, response);
|
|
4035
|
+
return { data: data };
|
|
4036
|
+
};
|
|
4037
|
+
SqlExecutor.prototype.createCacheResponseEnvelope = function (method, tableName, data) {
|
|
4038
|
+
return {
|
|
4039
|
+
data: data,
|
|
4040
|
+
config: {
|
|
4041
|
+
method: method.toLowerCase(),
|
|
4042
|
+
url: "/rest/".concat(tableName),
|
|
4043
|
+
},
|
|
4044
|
+
};
|
|
4045
|
+
};
|
|
4046
|
+
SqlExecutor.prototype.executeQueryWithLifecycle = function (conn, method, sqlExecution, logContext) {
|
|
4047
|
+
return tslib.__awaiter(this, void 0, void 0, function () {
|
|
4048
|
+
var useTransaction, committed, result, response, hookResponse, err_1, rollbackErr_1;
|
|
4049
|
+
return tslib.__generator(this, function (_a) {
|
|
4050
|
+
switch (_a.label) {
|
|
4051
|
+
case 0:
|
|
4052
|
+
useTransaction = method !== C6Constants.GET;
|
|
4053
|
+
committed = false;
|
|
4054
|
+
_a.label = 1;
|
|
3882
4055
|
case 1:
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
4056
|
+
_a.trys.push([1, 11, , 16]);
|
|
4057
|
+
if (!useTransaction) return [3 /*break*/, 3];
|
|
4058
|
+
logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDFE Beginning transaction");
|
|
4059
|
+
return [4 /*yield*/, conn.beginTransaction()];
|
|
4060
|
+
case 2:
|
|
4061
|
+
_a.sent();
|
|
4062
|
+
_a.label = 3;
|
|
4063
|
+
case 3: return [4 /*yield*/, this.validateSqlAllowList(sqlExecution.sql)];
|
|
4064
|
+
case 4:
|
|
4065
|
+
_a.sent();
|
|
4066
|
+
return [4 /*yield*/, this.runLifecycleHooks("beforeExecution", {
|
|
4067
|
+
config: this.config,
|
|
4068
|
+
request: this.request,
|
|
4069
|
+
sqlExecution: sqlExecution,
|
|
4070
|
+
})];
|
|
4071
|
+
case 5:
|
|
4072
|
+
_a.sent();
|
|
4073
|
+
return [4 /*yield*/, conn.query(sqlExecution.sql, sqlExecution.values)];
|
|
4074
|
+
case 6:
|
|
4075
|
+
result = (_a.sent())[0];
|
|
4076
|
+
response = this.createResponseFromQueryResult(method, result, sqlExecution, logContext);
|
|
4077
|
+
hookResponse = this.createLifecycleHookResponse(response);
|
|
4078
|
+
return [4 /*yield*/, this.runLifecycleHooks("afterExecution", {
|
|
4079
|
+
config: this.config,
|
|
4080
|
+
request: this.request,
|
|
4081
|
+
response: hookResponse,
|
|
4082
|
+
})];
|
|
4083
|
+
case 7:
|
|
4084
|
+
_a.sent();
|
|
4085
|
+
if (!useTransaction) return [3 /*break*/, 9];
|
|
4086
|
+
return [4 /*yield*/, conn.commit()];
|
|
4087
|
+
case 8:
|
|
4088
|
+
_a.sent();
|
|
4089
|
+
committed = true;
|
|
4090
|
+
logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDFE Transaction committed");
|
|
4091
|
+
_a.label = 9;
|
|
4092
|
+
case 9: return [4 /*yield*/, this.runLifecycleHooks("afterCommit", {
|
|
4093
|
+
config: this.config,
|
|
4094
|
+
request: this.request,
|
|
4095
|
+
response: hookResponse,
|
|
4096
|
+
})];
|
|
4097
|
+
case 10:
|
|
4098
|
+
_a.sent();
|
|
4099
|
+
return [2 /*return*/, response];
|
|
4100
|
+
case 11:
|
|
4101
|
+
err_1 = _a.sent();
|
|
4102
|
+
if (!(useTransaction && !committed)) return [3 /*break*/, 15];
|
|
4103
|
+
_a.label = 12;
|
|
4104
|
+
case 12:
|
|
4105
|
+
_a.trys.push([12, 14, , 15]);
|
|
4106
|
+
return [4 /*yield*/, conn.rollback()];
|
|
4107
|
+
case 13:
|
|
4108
|
+
_a.sent();
|
|
4109
|
+
logWithLevel(exports.LogLevel.WARN, logContext, console.warn, "[SQL EXECUTOR] \uD83E\uDDFE Transaction rolled back");
|
|
4110
|
+
return [3 /*break*/, 15];
|
|
4111
|
+
case 14:
|
|
4112
|
+
rollbackErr_1 = _a.sent();
|
|
4113
|
+
logWithLevel(exports.LogLevel.ERROR, logContext, console.error, "[SQL EXECUTOR] Rollback failed", rollbackErr_1);
|
|
4114
|
+
return [3 /*break*/, 15];
|
|
4115
|
+
case 15: throw err_1;
|
|
4116
|
+
case 16: return [2 /*return*/];
|
|
3910
4117
|
}
|
|
3911
4118
|
});
|
|
3912
4119
|
});
|
|
@@ -3941,18 +4148,23 @@ var SqlExecutor$1 = /*#__PURE__*/Object.freeze({
|
|
|
3941
4148
|
SqlExecutor: SqlExecutor
|
|
3942
4149
|
});
|
|
3943
4150
|
|
|
4151
|
+
function restExpressRequest(routerConfig) {
|
|
4152
|
+
var router = routerConfig.router, _a = routerConfig.routePath, routePath = _a === void 0 ? "/rest/:table{/:primary}" : _a, handlerConfig = tslib.__rest(routerConfig, ["router", "routePath"]);
|
|
4153
|
+
router.all(routePath, ExpressHandler(handlerConfig));
|
|
4154
|
+
}
|
|
3944
4155
|
// TODO - WE MUST make this a generic - optional, but helpful
|
|
3945
4156
|
// note sure how it would help anyone actually...
|
|
3946
|
-
function ExpressHandler(
|
|
4157
|
+
function ExpressHandler(configX) {
|
|
3947
4158
|
var _this = this;
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
var incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1;
|
|
4159
|
+
return function (req, res) { return tslib.__awaiter(_this, void 0, void 0, function () {
|
|
4160
|
+
var config, C6, incomingMethod, table, primary, methodOverrideRaw, methodOverride, treatAsGet, method, payload, normalized, restModel, primaryKeys_1, primaryShortKeys_1, columnMap_1, resolveShortKey_1, hasPrimaryKeyValues, primaryKeyName, response, err_1, message;
|
|
3951
4161
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
3952
4162
|
return tslib.__generator(this, function (_h) {
|
|
3953
4163
|
switch (_h.label) {
|
|
3954
4164
|
case 0:
|
|
3955
4165
|
_h.trys.push([0, 2, , 3]);
|
|
4166
|
+
config = typeof configX === "function" ? configX() : configX;
|
|
4167
|
+
C6 = config.C6;
|
|
3956
4168
|
incomingMethod = req.method.toUpperCase();
|
|
3957
4169
|
table = req.params.table;
|
|
3958
4170
|
primary = req.params.primary;
|
|
@@ -3961,12 +4173,21 @@ function ExpressHandler(_a) {
|
|
|
3961
4173
|
treatAsGet = incomingMethod === 'POST' && methodOverride === 'GET';
|
|
3962
4174
|
method = treatAsGet ? 'GET' : incomingMethod;
|
|
3963
4175
|
payload = treatAsGet ? tslib.__assign({}, req.body) : (method === 'GET' ? req.query : req.body);
|
|
4176
|
+
// Query strings are text; coerce known boolean controls.
|
|
4177
|
+
if (typeof (payload === null || payload === void 0 ? void 0 : payload.cacheResults) === "string") {
|
|
4178
|
+
normalized = payload.cacheResults.toLowerCase();
|
|
4179
|
+
if (normalized === "false")
|
|
4180
|
+
payload.cacheResults = false;
|
|
4181
|
+
if (normalized === "true")
|
|
4182
|
+
payload.cacheResults = true;
|
|
4183
|
+
}
|
|
3964
4184
|
// Remove transport-only METHOD flag so it never leaks into ORM parsing
|
|
3965
4185
|
if (treatAsGet && 'METHOD' in payload) {
|
|
3966
4186
|
try {
|
|
3967
4187
|
delete payload.METHOD;
|
|
3968
4188
|
}
|
|
3969
|
-
catch ( /* noop */_j) { /* noop */
|
|
4189
|
+
catch ( /* noop */_j) { /* noop */
|
|
4190
|
+
}
|
|
3970
4191
|
}
|
|
3971
4192
|
// Warn for unsupported overrides but continue normally
|
|
3972
4193
|
if (incomingMethod !== 'GET' && methodOverride && methodOverride !== 'GET') {
|
|
@@ -4028,22 +4249,16 @@ function ExpressHandler(_a) {
|
|
|
4028
4249
|
(_g = payload[primaryKeyName]) !== null && _g !== void 0 ? _g : primary;
|
|
4029
4250
|
}
|
|
4030
4251
|
}
|
|
4031
|
-
return [4 /*yield*/, restRequest({
|
|
4032
|
-
C6: C6,
|
|
4033
|
-
mysqlPool: mysqlPool,
|
|
4034
|
-
sqlAllowListPath: sqlAllowListPath,
|
|
4035
|
-
websocketBroadcast: websocketBroadcast,
|
|
4036
|
-
requestMethod: method,
|
|
4037
|
-
restModel: C6.TABLES[table]
|
|
4038
|
-
})(payload)];
|
|
4252
|
+
return [4 /*yield*/, restRequest(tslib.__assign(tslib.__assign({}, config), { requestMethod: method, restModel: C6.TABLES[table] }))(payload)];
|
|
4039
4253
|
case 1:
|
|
4040
4254
|
response = _h.sent();
|
|
4041
4255
|
res.status(200).json(tslib.__assign({ success: true }, response));
|
|
4042
4256
|
return [3 /*break*/, 3];
|
|
4043
4257
|
case 2:
|
|
4044
4258
|
err_1 = _h.sent();
|
|
4045
|
-
|
|
4046
|
-
|
|
4259
|
+
message = err_1 instanceof Error ? err_1.message : String(err_1);
|
|
4260
|
+
logWithLevel(exports.LogLevel.ERROR, undefined, console.error, message);
|
|
4261
|
+
res.status(500).json({ success: false, error: message });
|
|
4047
4262
|
return [3 /*break*/, 3];
|
|
4048
4263
|
case 3: return [2 /*return*/];
|
|
4049
4264
|
}
|
|
@@ -4221,6 +4436,7 @@ exports.removeInvalidKeys = removeInvalidKeys;
|
|
|
4221
4436
|
exports.removePrefixIfExists = removePrefixIfExists;
|
|
4222
4437
|
exports.resolveDerivedTable = resolveDerivedTable;
|
|
4223
4438
|
exports.resolveLogLevel = resolveLogLevel;
|
|
4439
|
+
exports.restExpressRequest = restExpressRequest;
|
|
4224
4440
|
exports.restOrm = restOrm;
|
|
4225
4441
|
exports.restRequest = restRequest;
|
|
4226
4442
|
exports.setCache = setCache;
|