@carbonorm/carbonnode 3.0.2 → 3.0.4

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.
@@ -5,32 +5,131 @@ import isTest from "../../variables/isTest";
5
5
  import isVerbose from "../../variables/isVerbose";
6
6
  import convertForRequestBody from "../convertForRequestBody";
7
7
  import {eFetchDependencies} from "../types/dynamicFetching";
8
- import {Modify} from "../types/modifyTypes";
9
- import {apiReturn, DELETE, GET, iCacheAPI, iConstraint, iGetC6RestResponse, POST, PUT, RequestQueryBody} from "../types/ormInterfaces";
10
- import {removePrefixIfExists, TestRestfulResponse} from "../utils/apiHelpers";
8
+ import {
9
+ apiReturn,
10
+ DELETE, DetermineResponseDataType,
11
+ GET,
12
+ iCacheAPI,
13
+ iConstraint,
14
+ iGetC6RestResponse,
15
+ iRestMethods,
16
+ POST,
17
+ PUT, RequestQueryBody
18
+ } from "../types/ormInterfaces";
19
+ import {removeInvalidKeys, removePrefixIfExists, TestRestfulResponse} from "../utils/apiHelpers";
11
20
  import {apiRequestCache, checkCache, userCustomClearCache} from "../utils/cacheManager";
12
21
  import {sortAndSerializeQueryObject} from "../utils/sortAndSerializeQueryObject";
13
22
  import {Executor} from "./Executor";
14
- import {toastOptions, toastOptionsDevs } from "variables/toastOptions";
23
+ import {toastOptions, toastOptionsDevs} from "variables/toastOptions";
15
24
 
16
25
  export class HttpExecutor<
26
+ RequestMethod extends iRestMethods,
17
27
  RestShortTableName extends string = any,
18
28
  RestTableInterface extends { [key: string]: any } = any,
19
29
  PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
20
30
  CustomAndRequiredFields extends { [key: string]: any } = any,
21
- RequestTableOverrides extends { [key: string]: any; } = { [key in keyof RestTableInterface]: any },
22
- ResponseDataType = any
31
+ RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
23
32
  >
24
33
  extends Executor<
34
+ RequestMethod,
25
35
  RestShortTableName,
26
36
  RestTableInterface,
27
37
  PrimaryKey,
28
38
  CustomAndRequiredFields,
29
- RequestTableOverrides,
30
- ResponseDataType
39
+ RequestTableOverrides
31
40
  > {
32
41
 
33
- public async execute() : Promise<apiReturn<ResponseDataType>> {
42
+ public putState(
43
+ response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
44
+ request: RequestQueryBody<
45
+ RequestMethod,
46
+ RestTableInterface,
47
+ CustomAndRequiredFields,
48
+ RequestTableOverrides
49
+ >,
50
+ callback: () => void
51
+ ) {
52
+ this.config.reactBootstrap?.updateRestfulObjectArrays<RestTableInterface>({
53
+ callback,
54
+ dataOrCallback: [
55
+ removeInvalidKeys<RestTableInterface>({
56
+ ...request,
57
+ ...response?.data?.rest,
58
+ }, this.config.C6.TABLES)
59
+ ],
60
+ stateKey: this.config.restModel.TABLE_NAME,
61
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT
62
+ })
63
+ }
64
+
65
+ public postState(
66
+ response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
67
+ request: RequestQueryBody<
68
+ RequestMethod,
69
+ RestTableInterface,
70
+ CustomAndRequiredFields,
71
+ RequestTableOverrides
72
+ >,
73
+ callback: () => void
74
+ ) {
75
+
76
+ if (1 !== this.config.restModel.PRIMARY_SHORT.length) {
77
+
78
+ console.error("C6 received unexpected result's given the primary key length");
79
+
80
+ } else {
81
+
82
+ const pk = this.config.restModel.PRIMARY_SHORT[0];
83
+
84
+ // TODO - should overrides be handled differently? Why override: (react/php), driver missmatches, aux data..
85
+ // @ts-ignore - this is technically a correct error, but we allow it anyway...
86
+ request[pk] = response.data?.created as RestTableInterface[PrimaryKey]
87
+
88
+ }
89
+
90
+ this.config.reactBootstrap?.updateRestfulObjectArrays<RestTableInterface>({
91
+ callback,
92
+ dataOrCallback: undefined !== request.dataInsertMultipleRows
93
+ ? request.dataInsertMultipleRows.map((request, index) => {
94
+ return removeInvalidKeys<RestTableInterface>({
95
+ ...request,
96
+ ...(index === 0 ? response?.data?.rest : {}),
97
+ }, this.config.C6.TABLES)
98
+ })
99
+ : [
100
+ removeInvalidKeys<RestTableInterface>({
101
+ ...request,
102
+ ...response?.data?.rest,
103
+ }, this.config.C6.TABLES)
104
+ ],
105
+ stateKey: this.config.restModel.TABLE_NAME,
106
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[]
107
+ })
108
+ }
109
+
110
+ public deleteState(
111
+ _response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
112
+ request: RequestQueryBody<
113
+ RequestMethod,
114
+ RestTableInterface,
115
+ CustomAndRequiredFields,
116
+ RequestTableOverrides
117
+ >,
118
+ callback: () => void
119
+ ) {
120
+ this.config.reactBootstrap?.deleteRestfulObjectArrays<RestTableInterface>({
121
+ callback,
122
+ dataOrCallback: [
123
+ request as unknown as RestTableInterface,
124
+ ],
125
+ stateKey: this.config.restModel.TABLE_NAME,
126
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[]
127
+ })
128
+ }
129
+
130
+ public async execute(): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> {
131
+
132
+ type ResponseDataType = DetermineResponseDataType<RequestMethod, RestTableInterface>;
34
133
 
35
134
  const {
36
135
  C6,
@@ -38,13 +137,20 @@ export class HttpExecutor<
38
137
  restURL,
39
138
  withCredentials,
40
139
  restModel,
140
+ reactBootstrap,
41
141
  requestMethod,
42
- queryCallback,
43
- responseCallback,
44
142
  skipPrimaryCheck,
45
143
  clearCache,
46
144
  } = this.config
47
145
 
146
+
147
+ await this.runLifecycleHooks<"beforeProcessing">(
148
+ "beforeProcessing", {
149
+ config: this.config,
150
+ request: this.request,
151
+ });
152
+
153
+
48
154
  const tableName = restModel.TABLE_NAME;
49
155
 
50
156
  const fullTableList = Array.isArray(tableName) ? tableName : [tableName];
@@ -79,27 +185,9 @@ export class HttpExecutor<
79
185
 
80
186
  // an undefined query would indicate queryCallback returned undefined,
81
187
  // thus the request shouldn't fire as is in custom cache
82
- let query: RequestQueryBody<Modify<RestTableInterface, RequestTableOverrides>> | undefined | null;
83
-
84
- if ('function' === typeof queryCallback) {
85
-
86
- query = queryCallback(this.request); // obj or obj[]
87
-
88
- } else {
89
-
90
- query = queryCallback;
91
-
92
- }
93
-
94
- if (undefined === query || null === query) {
188
+ if (undefined === this.request || null === this.request) {
95
189
 
96
- if (this.request.debug && isLocal) {
97
-
98
- toast.warning("DEV: queryCallback returned undefined, signaling in Custom Cache. (returning null)", toastOptionsDevs)
99
-
100
- }
101
-
102
- console.groupCollapsed('%c API: (' + requestMethod + ') Request Query for (' + tableName + ') undefined, returning null (will not fire ajax)!', 'color: #c00')
190
+ console.groupCollapsed('%c API: (' + requestMethod + ') Request Query for (' + tableName + ') undefined, returning null (will not fire)!', 'color: #c00')
103
191
 
104
192
  console.log('%c Returning (undefined|null) for a query would indicate a custom cache hit (outside API.tsx), thus the request should not fire.', 'color: #c00')
105
193
 
@@ -111,6 +199,8 @@ export class HttpExecutor<
111
199
 
112
200
  }
113
201
 
202
+ let query = this.request;
203
+
114
204
  if (C6.GET === requestMethod) {
115
205
 
116
206
  if (undefined === query[C6.PAGINATION]) {
@@ -126,7 +216,7 @@ export class HttpExecutor<
126
216
  }
127
217
 
128
218
  // this could return itself with a new page number, or undefined if the end is reached
129
- const apiRequest = async (): Promise<apiReturn<ResponseDataType>> => {
219
+ const apiRequest = async (): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> => {
130
220
 
131
221
  const {
132
222
  debug,
@@ -134,7 +224,7 @@ export class HttpExecutor<
134
224
  dataInsertMultipleRows,
135
225
  success,
136
226
  fetchDependencies = eFetchDependencies.NONE,
137
- error = "An unexpected API error occurred!"
227
+ error = "An unexpected API error occurred!"
138
228
  } = this.request
139
229
 
140
230
  if (C6.GET === requestMethod
@@ -168,7 +258,12 @@ export class HttpExecutor<
168
258
 
169
259
  if (undefined === query || null === query) {
170
260
 
171
- query = {}
261
+ query = {} as RequestQueryBody<
262
+ RequestMethod,
263
+ RestTableInterface,
264
+ CustomAndRequiredFields,
265
+ RequestTableOverrides
266
+ >
172
267
 
173
268
  }
174
269
 
@@ -329,8 +424,13 @@ export class HttpExecutor<
329
424
  const removedPkValue = query[primaryKey];
330
425
 
331
426
  addBackPK = () => {
332
- query ??= {}
333
- query[primaryKey] = removedPkValue
427
+ query ??= {} as RequestQueryBody<
428
+ RequestMethod,
429
+ RestTableInterface,
430
+ CustomAndRequiredFields,
431
+ RequestTableOverrides
432
+ >;
433
+ query[primaryKey] = removedPkValue;
334
434
  }
335
435
 
336
436
  delete query[primaryKey]
@@ -357,64 +457,64 @@ export class HttpExecutor<
357
457
 
358
458
  console.groupEnd()
359
459
 
460
+ this.runLifecycleHooks<"beforeExecution">(
461
+ "beforeExecution", {
462
+ config: this.config,
463
+ request: this.request
464
+ })
465
+
360
466
  const axiosActiveRequest: AxiosPromise<ResponseDataType> = axios![requestMethod.toLowerCase()]<ResponseDataType>(
361
467
  restRequestUri,
362
- ...((() => {
363
-
364
- // @link - https://axios-http.com/docs/instance
365
- // How configuration vs data is passed is variable, use documentation above for reference
366
- if (requestMethod === GET) {
367
-
368
- return [{
369
- withCredentials: withCredentials,
370
- params: query
371
- }]
372
-
373
- } else if (requestMethod === POST) {
374
-
375
- if (undefined !== dataInsertMultipleRows) {
376
-
377
- return [
378
- dataInsertMultipleRows.map(data =>
379
- convertForRequestBody<typeof data>(data, fullTableList, C6, (message) => toast.error(message, toastOptions))),
380
- {
381
- withCredentials: withCredentials,
382
- }
383
- ]
384
-
385
- }
386
-
387
- return [
388
- convertForRequestBody<RestTableInterface>(query as RestTableInterface, fullTableList, C6, (message) => toast.error(message, toastOptions)),
389
- {
390
- withCredentials: withCredentials,
468
+ ...(() => {
469
+ const convert = (data: any) =>
470
+ convertForRequestBody<
471
+ RequestMethod,
472
+ RestTableInterface,
473
+ CustomAndRequiredFields,
474
+ RequestTableOverrides
475
+ >(
476
+ data,
477
+ fullTableList,
478
+ C6,
479
+ (message) => toast.error(message, toastOptions)
480
+ );
481
+
482
+ const baseConfig = {
483
+ withCredentials: withCredentials,
484
+ };
485
+
486
+ switch (requestMethod) {
487
+ case GET:
488
+ return [{
489
+ ...baseConfig,
490
+ params: query
491
+ }];
492
+
493
+ case POST:
494
+ if (dataInsertMultipleRows !== undefined) {
495
+ return [
496
+ dataInsertMultipleRows.map(convert),
497
+ baseConfig
498
+ ];
391
499
  }
392
- ]
500
+ return [convert(query), baseConfig];
393
501
 
394
- } else if (requestMethod === PUT) {
395
-
396
- return [
397
- convertForRequestBody<RestTableInterface>(query as RestTableInterface, fullTableList, C6, (message) => toast.error(message, toastOptions)),
398
- {
399
- withCredentials: withCredentials,
400
- }
401
- ]
402
- } else if (requestMethod === DELETE) {
403
-
404
- return [{
405
- withCredentials: withCredentials,
406
- data: convertForRequestBody<RestTableInterface>(query as RestTableInterface, fullTableList, C6, (message) => toast.error(message, toastOptions))
407
- }]
408
-
409
- } else {
502
+ case PUT:
503
+ return [convert(query), baseConfig];
410
504
 
411
- throw new Error('The request method (' + requestMethod + ') was not recognized.')
505
+ case DELETE:
506
+ return [{
507
+ ...baseConfig,
508
+ data: convert(query)
509
+ }];
412
510
 
511
+ default:
512
+ throw new Error(`The request method (${requestMethod}) was not recognized.`);
413
513
  }
414
-
415
- })())
514
+ })()
416
515
  );
417
516
 
517
+
418
518
  if (cachingConfirmed) {
419
519
 
420
520
  // push to cache so we do not repeat the request
@@ -431,8 +531,9 @@ export class HttpExecutor<
431
531
 
432
532
  // returning the promise with this then is important for tests. todo - we could make that optional.
433
533
  // https://rapidapi.com/guides/axios-async-await
434
- return axiosActiveRequest.then(async (response): Promise<AxiosResponse<ResponseDataType, any>> => {
534
+ return axiosActiveRequest.then(async (response: AxiosResponse<ResponseDataType, any>): Promise<AxiosResponse<ResponseDataType, any>> => {
435
535
 
536
+ // noinspection SuspiciousTypeOfGuard
436
537
  if (typeof response.data === 'string') {
437
538
 
438
539
  if (isTest) {
@@ -458,6 +559,14 @@ export class HttpExecutor<
458
559
 
459
560
  }
460
561
 
562
+ this.runLifecycleHooks<"afterExecution">(
563
+ "afterExecution", {
564
+ config: this.config,
565
+ request: this.request,
566
+ response
567
+ })
568
+
569
+ // todo - this feels dumb now, but i digress
461
570
  apiResponse = TestRestfulResponse(response, success, error)
462
571
 
463
572
  if (false === apiResponse) {
@@ -472,11 +581,36 @@ export class HttpExecutor<
472
581
 
473
582
  }
474
583
 
475
- // stateful operations are done in the response callback - its leverages rest generated functions
476
- if (responseCallback) {
477
-
478
- responseCallback(response, this.request, apiResponse)
479
584
 
585
+ const callback = () => this.runLifecycleHooks<"afterCommit">(
586
+ "afterCommit", {
587
+ config: this.config,
588
+ request: this.request,
589
+ response
590
+ });
591
+
592
+ if (undefined !== reactBootstrap && response) {
593
+ switch (requestMethod) {
594
+ case GET:
595
+ reactBootstrap.updateRestfulObjectArrays<RestTableInterface>({
596
+ dataOrCallback: Array.isArray(response.data.rest) ? response.data.rest : [response.data.rest],
597
+ stateKey: this.config.restModel.TABLE_NAME,
598
+ uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[],
599
+ callback
600
+ })
601
+ break;
602
+ case POST:
603
+ this.postState(response, this.request, callback);
604
+ break;
605
+ case PUT:
606
+ this.putState(response, this.request, callback);
607
+ break;
608
+ case DELETE:
609
+ this.deleteState(response, this.request, callback);
610
+ break;
611
+ }
612
+ } else {
613
+ callback();
480
614
  }
481
615
 
482
616
  if (C6.GET === requestMethod) {
@@ -1,27 +1,27 @@
1
- import {apiReturn} from "@carbonorm/carbonnode";
1
+ import {iRestMethods} from "@carbonorm/carbonnode";
2
2
  import { PoolConnection, RowDataPacket } from 'mysql2/promise';
3
3
  import {buildSelectQuery} from "../builders/sqlBuilder";
4
4
  import {Executor} from "./Executor";
5
5
 
6
6
 
7
7
  export class SqlExecutor<
8
+ RequestMethod extends iRestMethods,
8
9
  RestShortTableName extends string = any,
9
10
  RestTableInterface extends { [key: string]: any } = any,
10
11
  PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
11
12
  CustomAndRequiredFields extends { [key: string]: any } = any,
12
- RequestTableOverrides extends { [key: string]: any; } = { [key in keyof RestTableInterface]: any },
13
- ResponseDataType = any
13
+ RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
14
14
  >
15
15
  extends Executor<
16
+ RequestMethod,
16
17
  RestShortTableName,
17
18
  RestTableInterface,
18
19
  PrimaryKey,
19
20
  CustomAndRequiredFields,
20
- RequestTableOverrides,
21
- ResponseDataType
21
+ RequestTableOverrides
22
22
  > {
23
23
 
24
- public execute(): Promise<apiReturn<ResponseDataType>> {
24
+ public execute() {
25
25
  switch (this.config.requestMethod) {
26
26
  case 'GET':
27
27
  return (this.select(
@@ -0,0 +1,61 @@
1
+ import restRequest from "./restRequest";
2
+ import {iRest} from "./types/ormInterfaces";
3
+
4
+ export function restOrm<
5
+ RestShortTableName extends string = any,
6
+ RestTableInterface extends { [key: string]: any } = any,
7
+ PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
8
+ CustomAndRequiredFields extends { [key: string]: any } = any,
9
+ RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
10
+ >(config: Omit<iRest<
11
+ RestShortTableName,
12
+ RestTableInterface,
13
+ PrimaryKey
14
+ >, "requestMethod">) {
15
+ return {
16
+ Get: restRequest<
17
+ "GET",
18
+ RestShortTableName,
19
+ RestTableInterface,
20
+ PrimaryKey,
21
+ CustomAndRequiredFields,
22
+ RequestTableOverrides
23
+ >({
24
+ ...config,
25
+ requestMethod: "GET",
26
+ }),
27
+ Put: restRequest<
28
+ "PUT",
29
+ RestShortTableName,
30
+ RestTableInterface,
31
+ PrimaryKey,
32
+ CustomAndRequiredFields,
33
+ RequestTableOverrides
34
+ >({
35
+ ...config,
36
+ requestMethod: "PUT",
37
+ }),
38
+ Post: restRequest<
39
+ "POST",
40
+ RestShortTableName,
41
+ RestTableInterface,
42
+ PrimaryKey,
43
+ CustomAndRequiredFields,
44
+ RequestTableOverrides
45
+ >({
46
+ ...config,
47
+ requestMethod: "POST",
48
+ }),
49
+ Delete: restRequest<
50
+ "DELETE",
51
+ RestShortTableName,
52
+ RestTableInterface,
53
+ PrimaryKey,
54
+ CustomAndRequiredFields,
55
+ RequestTableOverrides
56
+ >({
57
+ ...config,
58
+ requestMethod: "DELETE",
59
+ }),
60
+ }
61
+ }
@@ -1,41 +1,45 @@
1
1
  import isNode from '../variables/isNode';
2
- import {Modify} from "./types/modifyTypes";
3
- import {apiReturn, iAPI, iRest} from "./types/ormInterfaces";
2
+ import {
3
+ apiReturn, DetermineResponseDataType,
4
+ iRest, iRestMethods, RequestQueryBody
5
+ } from "./types/ormInterfaces";
4
6
 
5
7
  /**
6
8
  * Facade: routes API calls to SQL or HTTP executors based on runtime context.
7
9
  */
8
10
  export default function restRequest<
11
+ RequestMethod extends iRestMethods,
9
12
  RestShortTableName extends string = any,
10
13
  RestTableInterface extends { [key: string]: any } = any,
11
14
  PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
12
15
  CustomAndRequiredFields extends { [key: string]: any } = any,
13
- RequestTableOverrides extends { [key: string]: any } = { [key in keyof RestTableInterface]: any },
14
- ResponseDataType = any
16
+ RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
15
17
  >(
16
18
  config: iRest<
17
19
  RestShortTableName,
18
20
  RestTableInterface,
19
- PrimaryKey,
20
- CustomAndRequiredFields,
21
- RequestTableOverrides,
22
- ResponseDataType
21
+ PrimaryKey
23
22
  >
24
23
  ) {
25
24
  return async (
26
- request: iAPI<Modify<RestTableInterface, RequestTableOverrides>> & CustomAndRequiredFields = {} as iAPI<Modify<RestTableInterface, RequestTableOverrides>> & CustomAndRequiredFields
27
- ): Promise<apiReturn<ResponseDataType>> => {
25
+ request: RequestQueryBody<
26
+ RequestMethod,
27
+ RestTableInterface,
28
+ CustomAndRequiredFields,
29
+ RequestTableOverrides
30
+ >,
31
+ ): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> => {
28
32
 
29
33
  // SQL path if on Node with a provided pool
30
34
  if (isNode && config.mysqlPool) {
31
35
  const {SqlExecutor} = await import('./executors/SqlExecutor');
32
36
  const executor = new SqlExecutor<
37
+ RequestMethod,
33
38
  RestShortTableName,
34
39
  RestTableInterface,
35
40
  PrimaryKey,
36
41
  CustomAndRequiredFields,
37
- RequestTableOverrides,
38
- ResponseDataType
42
+ RequestTableOverrides
39
43
  >(config, request);
40
44
  return executor.execute();
41
45
  }
@@ -43,13 +47,14 @@ export default function restRequest<
43
47
  // HTTP path fallback
44
48
  const {HttpExecutor} = await import('./executors/HttpExecutor');
45
49
  const http = new HttpExecutor<
50
+ RequestMethod,
46
51
  RestShortTableName,
47
52
  RestTableInterface,
48
53
  PrimaryKey,
49
54
  CustomAndRequiredFields,
50
- RequestTableOverrides,
51
- ResponseDataType
55
+ RequestTableOverrides
52
56
  >(config, request);
53
57
  return http.execute();
54
58
  };
55
59
  }
60
+