@carbonorm/carbonnode 6.0.10 → 6.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/executors/SqlExecutor.d.ts +5 -0
  2. package/dist/handlers/ExpressHandler.d.ts +10 -3
  3. package/dist/index.cjs.js +170 -78
  4. package/dist/index.cjs.js.map +1 -1
  5. package/dist/index.esm.js +170 -79
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/types/ormInterfaces.d.ts +12 -2
  8. package/package.json +1 -1
  9. package/src/__tests__/sakila-db/C6.js +1 -1
  10. package/src/__tests__/sakila-db/C6.mysqldump.json +1 -1
  11. package/src/__tests__/sakila-db/C6.mysqldump.sql +1 -1
  12. package/src/__tests__/sakila-db/C6.ts +1 -1
  13. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +3 -3
  14. package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.latest.json +3 -3
  15. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.json +1 -1
  16. package/src/__tests__/sakila-db/sqlResponses/C6.actor.put.lookup.json +3 -3
  17. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.json +5 -5
  18. package/src/__tests__/sakila-db/sqlResponses/C6.address.post.latest.json +5 -5
  19. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.json +1 -1
  20. package/src/__tests__/sakila-db/sqlResponses/C6.address.put.lookup.json +5 -5
  21. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.json +2 -2
  22. package/src/__tests__/sakila-db/sqlResponses/C6.category.post.latest.json +2 -2
  23. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.json +1 -1
  24. package/src/__tests__/sakila-db/sqlResponses/C6.category.put.lookup.json +2 -2
  25. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.json +2 -2
  26. package/src/__tests__/sakila-db/sqlResponses/C6.city.post.latest.json +2 -2
  27. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.json +1 -1
  28. package/src/__tests__/sakila-db/sqlResponses/C6.city.put.lookup.json +2 -2
  29. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.json +2 -2
  30. package/src/__tests__/sakila-db/sqlResponses/C6.country.post.latest.json +2 -2
  31. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.json +1 -1
  32. package/src/__tests__/sakila-db/sqlResponses/C6.country.put.lookup.json +2 -2
  33. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.json +5 -5
  34. package/src/__tests__/sakila-db/sqlResponses/C6.customer.post.latest.json +5 -5
  35. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.json +1 -1
  36. package/src/__tests__/sakila-db/sqlResponses/C6.customer.put.lookup.json +5 -5
  37. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.json +2 -2
  38. package/src/__tests__/sakila-db/sqlResponses/C6.film.post.latest.json +2 -2
  39. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.json +1 -1
  40. package/src/__tests__/sakila-db/sqlResponses/C6.film.put.lookup.json +2 -2
  41. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.json +1 -1
  42. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.post.latest.json +1 -1
  43. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.json +1 -1
  44. package/src/__tests__/sakila-db/sqlResponses/C6.inventory.put.lookup.json +1 -1
  45. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.json +2 -2
  46. package/src/__tests__/sakila-db/sqlResponses/C6.language.post.latest.json +2 -2
  47. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.json +1 -1
  48. package/src/__tests__/sakila-db/sqlResponses/C6.language.put.lookup.json +2 -2
  49. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.json +2 -2
  50. package/src/__tests__/sakila-db/sqlResponses/C6.payment.post.latest.json +2 -2
  51. package/src/__tests__/sakila-db/sqlResponses/C6.payment.put.lookup.json +2 -2
  52. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +3 -3
  53. package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.latest.json +3 -3
  54. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.json +1 -1
  55. package/src/__tests__/sakila-db/sqlResponses/C6.rental.put.lookup.json +3 -3
  56. package/src/__tests__/sakila.generated.test.ts +11 -3
  57. package/src/__tests__/sqlExecutorLifecycleHooks.test.ts +122 -0
  58. package/src/executors/SqlExecutor.ts +190 -49
  59. package/src/handlers/ExpressHandler.ts +22 -7
  60. package/src/types/ormInterfaces.ts +16 -2
@@ -18,5 +18,10 @@ 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 executeQueryWithLifecycle;
21
26
  private validateSqlAllowList;
22
27
  }
@@ -1,9 +1,16 @@
1
- import type { Request, Response, NextFunction } from "express";
1
+ import type { Request, Response, NextFunction, Router } from "express";
2
2
  import type { Pool } from "mysql2/promise";
3
3
  import type { iC6Object, tWebsocketBroadcast } from "../types/ormInterfaces";
4
- export declare function ExpressHandler({ C6, mysqlPool, sqlAllowListPath, websocketBroadcast, }: {
4
+ type iExpressHandlerConfig = {
5
5
  C6: iC6Object;
6
6
  mysqlPool: Pool;
7
7
  sqlAllowListPath?: string;
8
8
  websocketBroadcast?: tWebsocketBroadcast;
9
- }): (req: Request, res: Response, next: NextFunction) => Promise<void>;
9
+ };
10
+ type iRestExpressRequestConfig = iExpressHandlerConfig & {
11
+ router: Pick<Router, "all">;
12
+ routePath?: string;
13
+ };
14
+ export declare function restExpressRequest({ router, routePath, ...handlerConfig }: iRestExpressRequestConfig): void;
15
+ export declare function ExpressHandler({ C6, mysqlPool, sqlAllowListPath, websocketBroadcast, }: iExpressHandlerConfig): (req: Request, res: Response, next: NextFunction) => Promise<void>;
16
+ export {};
package/dist/index.cjs.js CHANGED
@@ -2973,7 +2973,7 @@ function colorSql(sql) {
2973
2973
  return s;
2974
2974
  }
2975
2975
 
2976
- var version = "6.0.10";
2976
+ var version = "6.0.12";
2977
2977
 
2978
2978
  var DEFAULT_STEP = 8;
2979
2979
  function parseSemver(version) {
@@ -3494,12 +3494,18 @@ var SqlExecutor = /** @class */ (function (_super) {
3494
3494
  }
3495
3495
  SqlExecutor.prototype.execute = function () {
3496
3496
  return tslib.__awaiter(this, void 0, void 0, function () {
3497
- var TABLE_NAME, method, logContext, _a, rest, result, result, result;
3497
+ var TABLE_NAME, method, logContext, response, _a, rest, getResponse, restRows, result, result, result;
3498
3498
  return tslib.__generator(this, function (_b) {
3499
3499
  switch (_b.label) {
3500
3500
  case 0:
3501
3501
  TABLE_NAME = this.config.restModel.TABLE_NAME;
3502
3502
  method = this.config.requestMethod;
3503
+ return [4 /*yield*/, this.runLifecycleHooks("beforeProcessing", {
3504
+ config: this.config,
3505
+ request: this.request,
3506
+ })];
3507
+ case 1:
3508
+ _b.sent();
3503
3509
  // Normalize singular T-shaped requests into complex ORM shape (GET/PUT/DELETE)
3504
3510
  try {
3505
3511
  this.request = normalizeSingularRequest(method, this.request, this.config.restModel, undefined);
@@ -3513,45 +3519,54 @@ var SqlExecutor = /** @class */ (function (_super) {
3513
3519
  logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE9 Request:", this.request);
3514
3520
  _a = method;
3515
3521
  switch (_a) {
3516
- case 'GET': return [3 /*break*/, 1];
3517
- case 'POST': return [3 /*break*/, 3];
3518
- case 'PUT': return [3 /*break*/, 6];
3519
- case 'DELETE': return [3 /*break*/, 9];
3522
+ case 'GET': return [3 /*break*/, 2];
3523
+ case 'POST': return [3 /*break*/, 4];
3524
+ case 'PUT': return [3 /*break*/, 7];
3525
+ case 'DELETE': return [3 /*break*/, 10];
3520
3526
  }
3521
- return [3 /*break*/, 12];
3522
- case 1: return [4 /*yield*/, this.runQuery()];
3523
- case 2:
3527
+ return [3 /*break*/, 13];
3528
+ case 2: return [4 /*yield*/, this.runQuery()];
3529
+ case 3:
3524
3530
  rest = _b.sent();
3525
3531
  if (this.config.reactBootstrap) {
3532
+ getResponse = rest;
3533
+ restRows = Array.isArray(getResponse.rest)
3534
+ ? getResponse.rest
3535
+ : [getResponse.rest];
3526
3536
  this.config.reactBootstrap.updateRestfulObjectArrays({
3527
- dataOrCallback: rest.rest,
3537
+ dataOrCallback: restRows,
3528
3538
  stateKey: this.config.restModel.TABLE_NAME,
3529
3539
  uniqueObjectId: this.config.restModel.PRIMARY_SHORT,
3530
3540
  });
3531
3541
  }
3532
- return [2 /*return*/, rest];
3533
- case 3: return [4 /*yield*/, this.runQuery()];
3534
- case 4:
3542
+ response = rest;
3543
+ return [3 /*break*/, 14];
3544
+ case 4: return [4 /*yield*/, this.runQuery()];
3545
+ case 5:
3535
3546
  result = _b.sent();
3536
3547
  return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
3537
- case 5:
3548
+ case 6:
3538
3549
  _b.sent();
3539
- return [2 /*return*/, result];
3540
- case 6: return [4 /*yield*/, this.runQuery()];
3541
- case 7:
3550
+ response = result;
3551
+ return [3 /*break*/, 14];
3552
+ case 7: return [4 /*yield*/, this.runQuery()];
3553
+ case 8:
3542
3554
  result = _b.sent();
3543
3555
  return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
3544
- case 8:
3556
+ case 9:
3545
3557
  _b.sent();
3546
- return [2 /*return*/, result];
3547
- case 9: return [4 /*yield*/, this.runQuery()];
3548
- case 10:
3558
+ response = result;
3559
+ return [3 /*break*/, 14];
3560
+ case 10: return [4 /*yield*/, this.runQuery()];
3561
+ case 11:
3549
3562
  result = _b.sent();
3550
3563
  return [4 /*yield*/, this.broadcastWebsocketIfConfigured(result)];
3551
- case 11:
3564
+ case 12:
3552
3565
  _b.sent();
3553
- return [2 /*return*/, result];
3554
- case 12: throw new Error("Unsupported request method: ".concat(method));
3566
+ response = result;
3567
+ return [3 /*break*/, 14];
3568
+ case 13: throw new Error("Unsupported request method: ".concat(method));
3569
+ case 14: return [2 /*return*/, response];
3555
3570
  }
3556
3571
  });
3557
3572
  });
@@ -3848,65 +3863,137 @@ var SqlExecutor = /** @class */ (function (_super) {
3848
3863
  };
3849
3864
  SqlExecutor.prototype.runQuery = function () {
3850
3865
  return tslib.__awaiter(this, void 0, void 0, function () {
3851
- var TABLE_NAME, method, builder, QueryResult, logContext, formatted, toUnnamed, _a, sql, values;
3866
+ var method, tableName, logContext, sqlExecution;
3852
3867
  var _this = this;
3853
- return tslib.__generator(this, function (_b) {
3854
- switch (_b.label) {
3868
+ return tslib.__generator(this, function (_a) {
3869
+ switch (_a.label) {
3855
3870
  case 0:
3856
- TABLE_NAME = this.config.restModel.TABLE_NAME;
3857
3871
  method = this.config.requestMethod;
3858
- switch (method) {
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);
3872
+ tableName = this.config.restModel.TABLE_NAME;
3875
3873
  logContext = getLogContext(this.config, this.request);
3876
- logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Generated ".concat(method.toUpperCase(), " SQL:"), QueryResult);
3877
- formatted = this.formatSQLWithParams(QueryResult.sql, QueryResult.params);
3878
- logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Formatted ".concat(method.toUpperCase(), " SQL:"), formatted);
3879
- toUnnamed = namedPlaceholders();
3880
- _a = toUnnamed(QueryResult.sql, QueryResult.params), sql = _a[0], values = _a[1];
3881
- return [4 /*yield*/, this.validateSqlAllowList(sql)];
3874
+ sqlExecution = this.buildSqlExecutionContext(method, tableName, logContext);
3875
+ return [4 /*yield*/, this.withConnection(function (conn) { return tslib.__awaiter(_this, void 0, void 0, function () { return tslib.__generator(this, function (_a) {
3876
+ return [2 /*return*/, this.executeQueryWithLifecycle(conn, method, sqlExecution, logContext)];
3877
+ }); }); })];
3878
+ case 1: return [2 /*return*/, _a.sent()];
3879
+ }
3880
+ });
3881
+ });
3882
+ };
3883
+ SqlExecutor.prototype.getQueryBuilder = function (method) {
3884
+ switch (method) {
3885
+ case C6Constants.GET:
3886
+ return new SelectQueryBuilder(this.config, this.request);
3887
+ case C6Constants.PUT:
3888
+ return new UpdateQueryBuilder(this.config, this.request);
3889
+ case C6Constants.DELETE:
3890
+ return new DeleteQueryBuilder(this.config, this.request);
3891
+ case C6Constants.POST:
3892
+ return new PostQueryBuilder(this.config, this.request);
3893
+ default:
3894
+ throw new Error("Unsupported query method: ".concat(method));
3895
+ }
3896
+ };
3897
+ SqlExecutor.prototype.buildSqlExecutionContext = function (method, tableName, logContext) {
3898
+ var builder = this.getQueryBuilder(method);
3899
+ var queryResult = builder.build(tableName);
3900
+ logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Generated ".concat(method.toUpperCase(), " SQL:"), queryResult);
3901
+ var formatted = this.formatSQLWithParams(queryResult.sql, queryResult.params);
3902
+ logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDE0 Formatted ".concat(method.toUpperCase(), " SQL:"), formatted);
3903
+ var toUnnamed = namedPlaceholders();
3904
+ var _a = toUnnamed(queryResult.sql, queryResult.params), sql = _a[0], values = _a[1];
3905
+ return { sql: sql, values: values };
3906
+ };
3907
+ SqlExecutor.prototype.createResponseFromQueryResult = function (method, result, sqlExecution, logContext) {
3908
+ if (method === C6Constants.GET) {
3909
+ return {
3910
+ rest: result.map(this.serialize),
3911
+ sql: { sql: sqlExecution.sql, values: sqlExecution.values },
3912
+ };
3913
+ }
3914
+ logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \u270F\uFE0F Rows affected:", result.affectedRows);
3915
+ return {
3916
+ affected: result.affectedRows,
3917
+ insertId: result.insertId,
3918
+ rest: [],
3919
+ sql: { sql: sqlExecution.sql, values: sqlExecution.values },
3920
+ };
3921
+ };
3922
+ SqlExecutor.prototype.createLifecycleHookResponse = function (response) {
3923
+ var data = Object.assign({ success: true }, response);
3924
+ return { data: data };
3925
+ };
3926
+ SqlExecutor.prototype.executeQueryWithLifecycle = function (conn, method, sqlExecution, logContext) {
3927
+ return tslib.__awaiter(this, void 0, void 0, function () {
3928
+ var useTransaction, committed, result, response, hookResponse, err_1, rollbackErr_1;
3929
+ return tslib.__generator(this, function (_a) {
3930
+ switch (_a.label) {
3931
+ case 0:
3932
+ useTransaction = method !== C6Constants.GET;
3933
+ committed = false;
3934
+ _a.label = 1;
3882
3935
  case 1:
3883
- _b.sent();
3884
- return [4 /*yield*/, this.withConnection(function (conn) { return tslib.__awaiter(_this, void 0, void 0, function () {
3885
- var result;
3886
- return tslib.__generator(this, function (_a) {
3887
- switch (_a.label) {
3888
- case 0: return [4 /*yield*/, conn.query(sql, values)];
3889
- case 1:
3890
- result = (_a.sent())[0];
3891
- if (method === 'GET') {
3892
- return [2 /*return*/, {
3893
- rest: result.map(this.serialize),
3894
- sql: { sql: sql, values: values }
3895
- }];
3896
- }
3897
- else {
3898
- logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \u270F\uFE0F Rows affected:", result.affectedRows);
3899
- return [2 /*return*/, {
3900
- affected: result.affectedRows,
3901
- insertId: result.insertId,
3902
- rest: [], // TODO - remove rest empty array from non-GET responses?
3903
- sql: { sql: sql, values: values }
3904
- }];
3905
- }
3906
- }
3907
- });
3908
- }); })];
3909
- case 2: return [2 /*return*/, _b.sent()];
3936
+ _a.trys.push([1, 11, , 16]);
3937
+ if (!useTransaction) return [3 /*break*/, 3];
3938
+ logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDFE Beginning transaction");
3939
+ return [4 /*yield*/, conn.beginTransaction()];
3940
+ case 2:
3941
+ _a.sent();
3942
+ _a.label = 3;
3943
+ case 3: return [4 /*yield*/, this.validateSqlAllowList(sqlExecution.sql)];
3944
+ case 4:
3945
+ _a.sent();
3946
+ return [4 /*yield*/, this.runLifecycleHooks("beforeExecution", {
3947
+ config: this.config,
3948
+ request: this.request,
3949
+ sqlExecution: sqlExecution,
3950
+ })];
3951
+ case 5:
3952
+ _a.sent();
3953
+ return [4 /*yield*/, conn.query(sqlExecution.sql, sqlExecution.values)];
3954
+ case 6:
3955
+ result = (_a.sent())[0];
3956
+ response = this.createResponseFromQueryResult(method, result, sqlExecution, logContext);
3957
+ hookResponse = this.createLifecycleHookResponse(response);
3958
+ return [4 /*yield*/, this.runLifecycleHooks("afterExecution", {
3959
+ config: this.config,
3960
+ request: this.request,
3961
+ response: hookResponse,
3962
+ })];
3963
+ case 7:
3964
+ _a.sent();
3965
+ if (!useTransaction) return [3 /*break*/, 9];
3966
+ return [4 /*yield*/, conn.commit()];
3967
+ case 8:
3968
+ _a.sent();
3969
+ committed = true;
3970
+ logWithLevel(exports.LogLevel.DEBUG, logContext, console.log, "[SQL EXECUTOR] \uD83E\uDDFE Transaction committed");
3971
+ _a.label = 9;
3972
+ case 9: return [4 /*yield*/, this.runLifecycleHooks("afterCommit", {
3973
+ config: this.config,
3974
+ request: this.request,
3975
+ response: hookResponse,
3976
+ })];
3977
+ case 10:
3978
+ _a.sent();
3979
+ return [2 /*return*/, response];
3980
+ case 11:
3981
+ err_1 = _a.sent();
3982
+ if (!(useTransaction && !committed)) return [3 /*break*/, 15];
3983
+ _a.label = 12;
3984
+ case 12:
3985
+ _a.trys.push([12, 14, , 15]);
3986
+ return [4 /*yield*/, conn.rollback()];
3987
+ case 13:
3988
+ _a.sent();
3989
+ logWithLevel(exports.LogLevel.WARN, logContext, console.warn, "[SQL EXECUTOR] \uD83E\uDDFE Transaction rolled back");
3990
+ return [3 /*break*/, 15];
3991
+ case 14:
3992
+ rollbackErr_1 = _a.sent();
3993
+ logWithLevel(exports.LogLevel.ERROR, logContext, console.error, "[SQL EXECUTOR] Rollback failed", rollbackErr_1);
3994
+ return [3 /*break*/, 15];
3995
+ case 15: throw err_1;
3996
+ case 16: return [2 /*return*/];
3910
3997
  }
3911
3998
  });
3912
3999
  });
@@ -3941,6 +4028,10 @@ var SqlExecutor$1 = /*#__PURE__*/Object.freeze({
3941
4028
  SqlExecutor: SqlExecutor
3942
4029
  });
3943
4030
 
4031
+ function restExpressRequest(_a) {
4032
+ var router = _a.router, _b = _a.routePath, routePath = _b === void 0 ? "/rest/:table{/:primary}" : _b, handlerConfig = tslib.__rest(_a, ["router", "routePath"]);
4033
+ router.all(routePath, ExpressHandler(handlerConfig));
4034
+ }
3944
4035
  // TODO - WE MUST make this a generic - optional, but helpful
3945
4036
  // note sure how it would help anyone actually...
3946
4037
  function ExpressHandler(_a) {
@@ -4221,6 +4312,7 @@ exports.removeInvalidKeys = removeInvalidKeys;
4221
4312
  exports.removePrefixIfExists = removePrefixIfExists;
4222
4313
  exports.resolveDerivedTable = resolveDerivedTable;
4223
4314
  exports.resolveLogLevel = resolveLogLevel;
4315
+ exports.restExpressRequest = restExpressRequest;
4224
4316
  exports.restOrm = restOrm;
4225
4317
  exports.restRequest = restRequest;
4226
4318
  exports.setCache = setCache;