@carbonorm/carbonnode 3.0.13 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/C6Constants.d.ts +184 -0
- package/dist/api/builders/queryHelpers.d.ts +4 -0
- package/dist/api/builders/sqlBuilder.d.ts +17 -5
- package/dist/api/executors/Executor.d.ts +9 -15
- package/dist/api/executors/HttpExecutor.d.ts +7 -14
- package/dist/api/executors/SqlExecutor.d.ts +43 -14
- package/dist/api/orm/SqlBuilder.d.ts +17 -0
- package/dist/api/orm/builders/AggregateBuilder.d.ts +5 -0
- package/dist/api/orm/builders/ConditionBuilder.d.ts +11 -0
- package/dist/api/orm/builders/JoinBuilder.d.ts +5 -0
- package/dist/api/orm/builders/PaginationBuilder.d.ts +5 -0
- package/dist/api/orm/queries/DeleteQueryBuilder.d.ts +6 -0
- package/dist/api/orm/queries/SelectQueryBuilder.d.ts +6 -0
- package/dist/api/orm/queries/UpdateQueryBuilder.d.ts +6 -0
- package/dist/api/orm/queryHelpers.d.ts +4 -0
- package/dist/api/orm/utils/sqlUtils.d.ts +7 -0
- package/dist/api/restOrm.d.ts +3 -10
- package/dist/api/restRequest.d.ts +3 -10
- package/dist/api/types/ormGenerics.d.ts +13 -0
- package/dist/api/types/ormInterfaces.d.ts +23 -40
- package/dist/index.cjs.js +444 -249
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.esm.js +432 -249
- package/dist/index.esm.js.map +1 -1
- package/package.json +7 -2
- package/scripts/assets/handlebars/C6.ts.handlebars +2 -4
- package/scripts/generateRestBindings.cjs +1 -1
- package/scripts/generateRestBindings.ts +1 -1
- package/src/api/C6Constants.ts +38 -2
- package/src/api/executors/Executor.ts +18 -36
- package/src/api/executors/HttpExecutor.ts +46 -59
- package/src/api/executors/SqlExecutor.ts +89 -58
- package/src/api/handlers/ExpressHandler.ts +3 -2
- package/src/api/orm/builders/AggregateBuilder.ts +38 -0
- package/src/api/orm/builders/ConditionBuilder.ts +113 -0
- package/src/api/orm/builders/JoinBuilder.ts +25 -0
- package/src/api/orm/builders/PaginationBuilder.ts +28 -0
- package/src/api/orm/queries/DeleteQueryBuilder.ts +28 -0
- package/src/api/orm/queries/SelectQueryBuilder.ts +49 -0
- package/src/api/orm/queries/UpdateQueryBuilder.ts +42 -0
- package/src/api/orm/queryHelpers.ts +18 -0
- package/src/api/orm/utils/sqlUtils.ts +24 -0
- package/src/api/restOrm.ts +4 -14
- package/src/api/restRequest.ts +16 -34
- package/src/api/types/ormGenerics.ts +18 -0
- package/src/api/types/ormInterfaces.ts +28 -43
- package/src/index.ts +10 -1
- package/src/api/builders/sqlBuilder.ts +0 -223
|
@@ -5,6 +5,7 @@ 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 {OrmGenerics} from "../types/ormGenerics";
|
|
8
9
|
import {
|
|
9
10
|
apiReturn,
|
|
10
11
|
DELETE, DetermineResponseDataType,
|
|
@@ -12,7 +13,6 @@ import {
|
|
|
12
13
|
iCacheAPI,
|
|
13
14
|
iConstraint,
|
|
14
15
|
iGetC6RestResponse,
|
|
15
|
-
iRestMethods,
|
|
16
16
|
POST,
|
|
17
17
|
PUT, RequestQueryBody
|
|
18
18
|
} from "../types/ormInterfaces";
|
|
@@ -23,36 +23,24 @@ import {Executor} from "./Executor";
|
|
|
23
23
|
import {toastOptions, toastOptionsDevs} from "variables/toastOptions";
|
|
24
24
|
|
|
25
25
|
export class HttpExecutor<
|
|
26
|
-
|
|
27
|
-
RestShortTableName extends string = any,
|
|
28
|
-
RestTableInterface extends { [key: string]: any } = any,
|
|
29
|
-
PrimaryKey extends Extract<keyof RestTableInterface, string> = Extract<keyof RestTableInterface, string>,
|
|
30
|
-
CustomAndRequiredFields extends { [key: string]: any } = any,
|
|
31
|
-
RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
|
|
26
|
+
G extends OrmGenerics
|
|
32
27
|
>
|
|
33
|
-
extends Executor<
|
|
34
|
-
RequestMethod,
|
|
35
|
-
RestShortTableName,
|
|
36
|
-
RestTableInterface,
|
|
37
|
-
PrimaryKey,
|
|
38
|
-
CustomAndRequiredFields,
|
|
39
|
-
RequestTableOverrides
|
|
40
|
-
> {
|
|
28
|
+
extends Executor<G> {
|
|
41
29
|
|
|
42
30
|
public putState(
|
|
43
|
-
response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
|
|
31
|
+
response: AxiosResponse<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>,
|
|
44
32
|
request: RequestQueryBody<
|
|
45
|
-
RequestMethod,
|
|
46
|
-
RestTableInterface,
|
|
47
|
-
CustomAndRequiredFields,
|
|
48
|
-
RequestTableOverrides
|
|
33
|
+
G['RequestMethod'],
|
|
34
|
+
G['RestTableInterface'],
|
|
35
|
+
G['CustomAndRequiredFields'],
|
|
36
|
+
G['RequestTableOverrides']
|
|
49
37
|
>,
|
|
50
38
|
callback: () => void
|
|
51
39
|
) {
|
|
52
|
-
this.config.reactBootstrap?.updateRestfulObjectArrays<RestTableInterface>({
|
|
40
|
+
this.config.reactBootstrap?.updateRestfulObjectArrays<G['RestTableInterface']>({
|
|
53
41
|
callback,
|
|
54
42
|
dataOrCallback: [
|
|
55
|
-
removeInvalidKeys<RestTableInterface>({
|
|
43
|
+
removeInvalidKeys<G['RestTableInterface']>({
|
|
56
44
|
...request,
|
|
57
45
|
...response?.data?.rest,
|
|
58
46
|
}, this.config.C6.TABLES)
|
|
@@ -63,12 +51,12 @@ export class HttpExecutor<
|
|
|
63
51
|
}
|
|
64
52
|
|
|
65
53
|
public postState(
|
|
66
|
-
response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
|
|
54
|
+
response: AxiosResponse<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>,
|
|
67
55
|
request: RequestQueryBody<
|
|
68
|
-
RequestMethod,
|
|
69
|
-
RestTableInterface,
|
|
70
|
-
CustomAndRequiredFields,
|
|
71
|
-
RequestTableOverrides
|
|
56
|
+
G['RequestMethod'],
|
|
57
|
+
G['RestTableInterface'],
|
|
58
|
+
G['CustomAndRequiredFields'],
|
|
59
|
+
G['RequestTableOverrides']
|
|
72
60
|
>,
|
|
73
61
|
callback: () => void
|
|
74
62
|
) {
|
|
@@ -87,49 +75,49 @@ export class HttpExecutor<
|
|
|
87
75
|
|
|
88
76
|
}
|
|
89
77
|
|
|
90
|
-
this.config.reactBootstrap?.updateRestfulObjectArrays<RestTableInterface>({
|
|
78
|
+
this.config.reactBootstrap?.updateRestfulObjectArrays<G['RestTableInterface']>({
|
|
91
79
|
callback,
|
|
92
80
|
dataOrCallback: undefined !== request.dataInsertMultipleRows
|
|
93
81
|
? request.dataInsertMultipleRows.map((request, index) => {
|
|
94
|
-
return removeInvalidKeys<RestTableInterface>({
|
|
82
|
+
return removeInvalidKeys<G['RestTableInterface']>({
|
|
95
83
|
...request,
|
|
96
84
|
...(index === 0 ? response?.data?.rest : {}),
|
|
97
85
|
}, this.config.C6.TABLES)
|
|
98
86
|
})
|
|
99
87
|
: [
|
|
100
|
-
removeInvalidKeys<RestTableInterface>({
|
|
88
|
+
removeInvalidKeys<G['RestTableInterface']>({
|
|
101
89
|
...request,
|
|
102
90
|
...response?.data?.rest,
|
|
103
91
|
}, this.config.C6.TABLES)
|
|
104
92
|
],
|
|
105
93
|
stateKey: this.config.restModel.TABLE_NAME,
|
|
106
|
-
uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[]
|
|
94
|
+
uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof G['RestTableInterface'])[]
|
|
107
95
|
})
|
|
108
96
|
}
|
|
109
97
|
|
|
110
98
|
public deleteState(
|
|
111
|
-
_response: AxiosResponse<DetermineResponseDataType<RequestMethod, RestTableInterface>>,
|
|
99
|
+
_response: AxiosResponse<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>,
|
|
112
100
|
request: RequestQueryBody<
|
|
113
|
-
RequestMethod,
|
|
114
|
-
RestTableInterface,
|
|
115
|
-
CustomAndRequiredFields,
|
|
116
|
-
RequestTableOverrides
|
|
101
|
+
G['RequestMethod'],
|
|
102
|
+
G['RestTableInterface'],
|
|
103
|
+
G['CustomAndRequiredFields'],
|
|
104
|
+
G['RequestTableOverrides']
|
|
117
105
|
>,
|
|
118
106
|
callback: () => void
|
|
119
107
|
) {
|
|
120
|
-
this.config.reactBootstrap?.deleteRestfulObjectArrays<RestTableInterface>({
|
|
108
|
+
this.config.reactBootstrap?.deleteRestfulObjectArrays<G['RestTableInterface']>({
|
|
121
109
|
callback,
|
|
122
110
|
dataOrCallback: [
|
|
123
|
-
request as unknown as RestTableInterface,
|
|
111
|
+
request as unknown as G['RestTableInterface'],
|
|
124
112
|
],
|
|
125
113
|
stateKey: this.config.restModel.TABLE_NAME,
|
|
126
|
-
uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[]
|
|
114
|
+
uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof G['RestTableInterface'])[]
|
|
127
115
|
})
|
|
128
116
|
}
|
|
129
117
|
|
|
130
|
-
public async execute(): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> {
|
|
118
|
+
public async execute(): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> {
|
|
131
119
|
|
|
132
|
-
type ResponseDataType = DetermineResponseDataType<RequestMethod, RestTableInterface>;
|
|
120
|
+
type ResponseDataType = DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>;
|
|
133
121
|
|
|
134
122
|
const {
|
|
135
123
|
C6,
|
|
@@ -151,7 +139,7 @@ export class HttpExecutor<
|
|
|
151
139
|
});
|
|
152
140
|
|
|
153
141
|
|
|
154
|
-
const tableName = restModel.TABLE_NAME;
|
|
142
|
+
const tableName = restModel.TABLE_NAME as string;
|
|
155
143
|
|
|
156
144
|
const fullTableList = Array.isArray(tableName) ? tableName : [tableName];
|
|
157
145
|
|
|
@@ -218,7 +206,7 @@ export class HttpExecutor<
|
|
|
218
206
|
}
|
|
219
207
|
|
|
220
208
|
// this could return itself with a new page number, or undefined if the end is reached
|
|
221
|
-
const apiRequest = async (): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> => {
|
|
209
|
+
const apiRequest = async (): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> => {
|
|
222
210
|
|
|
223
211
|
const {
|
|
224
212
|
debug,
|
|
@@ -261,10 +249,10 @@ export class HttpExecutor<
|
|
|
261
249
|
if (undefined === query || null === query) {
|
|
262
250
|
|
|
263
251
|
query = {} as RequestQueryBody<
|
|
264
|
-
RequestMethod,
|
|
265
|
-
RestTableInterface,
|
|
266
|
-
CustomAndRequiredFields,
|
|
267
|
-
RequestTableOverrides
|
|
252
|
+
G['RequestMethod'],
|
|
253
|
+
G['RestTableInterface'],
|
|
254
|
+
G['CustomAndRequiredFields'],
|
|
255
|
+
G['RequestTableOverrides']
|
|
268
256
|
>
|
|
269
257
|
|
|
270
258
|
}
|
|
@@ -351,7 +339,7 @@ export class HttpExecutor<
|
|
|
351
339
|
|
|
352
340
|
let addBackPK: (() => void) | undefined;
|
|
353
341
|
|
|
354
|
-
let apiResponse: RestTableInterface[PrimaryKey] | string | boolean | number | undefined;
|
|
342
|
+
let apiResponse: G['RestTableInterface'][G['PrimaryKey']] | string | boolean | number | undefined;
|
|
355
343
|
|
|
356
344
|
let returnGetNextPageFunction = false;
|
|
357
345
|
|
|
@@ -427,10 +415,10 @@ export class HttpExecutor<
|
|
|
427
415
|
|
|
428
416
|
addBackPK = () => {
|
|
429
417
|
query ??= {} as RequestQueryBody<
|
|
430
|
-
RequestMethod,
|
|
431
|
-
RestTableInterface,
|
|
432
|
-
CustomAndRequiredFields,
|
|
433
|
-
RequestTableOverrides
|
|
418
|
+
G['RequestMethod'],
|
|
419
|
+
G['RestTableInterface'],
|
|
420
|
+
G['CustomAndRequiredFields'],
|
|
421
|
+
G['RequestTableOverrides']
|
|
434
422
|
>;
|
|
435
423
|
query[primaryKey] = removedPkValue;
|
|
436
424
|
}
|
|
@@ -470,10 +458,10 @@ export class HttpExecutor<
|
|
|
470
458
|
...(() => {
|
|
471
459
|
const convert = (data: any) =>
|
|
472
460
|
convertForRequestBody<
|
|
473
|
-
RequestMethod,
|
|
474
|
-
RestTableInterface,
|
|
475
|
-
CustomAndRequiredFields,
|
|
476
|
-
RequestTableOverrides
|
|
461
|
+
G['RequestMethod'],
|
|
462
|
+
G['RestTableInterface'],
|
|
463
|
+
G['CustomAndRequiredFields'],
|
|
464
|
+
G['RequestTableOverrides']
|
|
477
465
|
>(
|
|
478
466
|
data,
|
|
479
467
|
fullTableList,
|
|
@@ -574,7 +562,6 @@ export class HttpExecutor<
|
|
|
574
562
|
if (false === apiResponse) {
|
|
575
563
|
|
|
576
564
|
|
|
577
|
-
|
|
578
565
|
if (debug && isLocal()) {
|
|
579
566
|
|
|
580
567
|
toast.warning("DEVS: TestRestfulResponse returned false for (" + operatingTable + ").", toastOptionsDevs);
|
|
@@ -596,10 +583,10 @@ export class HttpExecutor<
|
|
|
596
583
|
if (undefined !== reactBootstrap && response) {
|
|
597
584
|
switch (requestMethod) {
|
|
598
585
|
case GET:
|
|
599
|
-
reactBootstrap.updateRestfulObjectArrays<RestTableInterface>({
|
|
586
|
+
reactBootstrap.updateRestfulObjectArrays<G['RestTableInterface']>({
|
|
600
587
|
dataOrCallback: Array.isArray(response.data.rest) ? response.data.rest : [response.data.rest],
|
|
601
588
|
stateKey: this.config.restModel.TABLE_NAME,
|
|
602
|
-
uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof RestTableInterface)[],
|
|
589
|
+
uniqueObjectId: this.config.restModel.PRIMARY_SHORT as (keyof G['RestTableInterface'])[],
|
|
603
590
|
callback
|
|
604
591
|
})
|
|
605
592
|
break;
|
|
@@ -1,32 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {SelectQueryBuilder} from "../orm/queries/SelectQueryBuilder";
|
|
2
|
+
import {OrmGenerics} from "../types/ormGenerics";
|
|
2
3
|
import {
|
|
3
4
|
apiReturn,
|
|
4
5
|
DetermineResponseDataType,
|
|
5
6
|
iPostC6RestResponse,
|
|
6
7
|
iPutC6RestResponse,
|
|
7
|
-
iDeleteC6RestResponse
|
|
8
|
-
iRestMethods
|
|
8
|
+
iDeleteC6RestResponse
|
|
9
9
|
} from "../types/ormInterfaces";
|
|
10
|
-
import
|
|
10
|
+
import namedPlaceholders from 'named-placeholders';
|
|
11
|
+
import {PoolConnection, RowDataPacket, ResultSetHeader} from 'mysql2/promise';
|
|
12
|
+
import {Buffer} from 'buffer';
|
|
13
|
+
import {Executor} from "./Executor";
|
|
11
14
|
|
|
12
15
|
export class SqlExecutor<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
RequestTableOverrides extends { [key in keyof RestTableInterface]: any } = { [key in keyof RestTableInterface]: any }
|
|
19
|
-
> extends SqlBuilder<
|
|
20
|
-
RequestMethod,
|
|
21
|
-
RestShortTableName,
|
|
22
|
-
RestTableInterface,
|
|
23
|
-
PrimaryKey,
|
|
24
|
-
CustomAndRequiredFields,
|
|
25
|
-
RequestTableOverrides
|
|
26
|
-
> {
|
|
27
|
-
|
|
28
|
-
async execute(): Promise<apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>> {
|
|
29
|
-
const { TABLE_NAME } = this.config.restModel;
|
|
16
|
+
G extends OrmGenerics
|
|
17
|
+
> extends Executor<G>{
|
|
18
|
+
|
|
19
|
+
async execute(): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> {
|
|
20
|
+
const {TABLE_NAME} = this.config.restModel;
|
|
30
21
|
const method = this.config.requestMethod;
|
|
31
22
|
|
|
32
23
|
console.log(`[SQL EXECUTOR] ▶️ Executing ${method} on table "${TABLE_NAME}"`);
|
|
@@ -36,25 +27,25 @@ export class SqlExecutor<
|
|
|
36
27
|
case 'GET': {
|
|
37
28
|
const rest = await this.select(TABLE_NAME, undefined, this.request);
|
|
38
29
|
console.log(`[SQL EXECUTOR] ✅ GET result:`, rest);
|
|
39
|
-
return
|
|
30
|
+
return rest as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
|
|
40
31
|
}
|
|
41
32
|
|
|
42
33
|
case 'POST': {
|
|
43
34
|
const result = await this.insert(TABLE_NAME, this.request);
|
|
44
35
|
console.log(`[SQL EXECUTOR] ✅ POST result:`, result);
|
|
45
|
-
const created: iPostC6RestResponse = {
|
|
46
|
-
return created as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
|
|
36
|
+
const created: iPostC6RestResponse = {rest: result, created: true};
|
|
37
|
+
return created as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
|
|
47
38
|
}
|
|
48
39
|
|
|
49
40
|
case 'PUT': {
|
|
50
41
|
const result = await this.update(TABLE_NAME, [], this.request);
|
|
51
42
|
console.log(`[SQL EXECUTOR] ✅ PUT result:`, result);
|
|
52
43
|
const updated: iPutC6RestResponse = {
|
|
53
|
-
|
|
44
|
+
...result,
|
|
54
45
|
updated: true,
|
|
55
|
-
rowCount:
|
|
46
|
+
rowCount: result.rest.affectedRows
|
|
56
47
|
};
|
|
57
|
-
return updated as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
|
|
48
|
+
return updated as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
|
|
58
49
|
}
|
|
59
50
|
|
|
60
51
|
case 'DELETE': {
|
|
@@ -63,9 +54,9 @@ export class SqlExecutor<
|
|
|
63
54
|
const deleted: iDeleteC6RestResponse = {
|
|
64
55
|
rest: result,
|
|
65
56
|
deleted: true,
|
|
66
|
-
rowCount:
|
|
57
|
+
rowCount: result.rest.affectedRows
|
|
67
58
|
};
|
|
68
|
-
return deleted as apiReturn<DetermineResponseDataType<RequestMethod, RestTableInterface>>;
|
|
59
|
+
return deleted as apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
|
|
69
60
|
}
|
|
70
61
|
|
|
71
62
|
default:
|
|
@@ -85,40 +76,58 @@ export class SqlExecutor<
|
|
|
85
76
|
}
|
|
86
77
|
}
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const formatted = this.formatSQLWithParams(sql.sql, sql.params);
|
|
92
|
-
console.log(`[SQL EXECUTOR] 🧠 Formatted SELECT SQL:`, formatted);
|
|
79
|
+
public serialize = (row: any) => Object.fromEntries(Object.entries(row).map(
|
|
80
|
+
([k, v]) => [k, Buffer.isBuffer(v) ? v.toString('hex').toUpperCase() : v]
|
|
81
|
+
));
|
|
93
82
|
|
|
83
|
+
async select(table: G['RestShortTableName'], primary: string | undefined, args: any) {
|
|
84
|
+
const QueryResult = (new SelectQueryBuilder(this.config, this.request)).build(table, args, primary);
|
|
85
|
+
console.log(`[SQL EXECUTOR] 🧠 Generated SELECT SQL:`, QueryResult);
|
|
86
|
+
const formatted = this.formatSQLWithParams(QueryResult.sql, QueryResult.params);
|
|
87
|
+
console.log(`[SQL EXECUTOR] 🧠 Formatted SELECT SQL:`, formatted);
|
|
88
|
+
const toUnnamed = namedPlaceholders();
|
|
89
|
+
const [sql, values] = toUnnamed(QueryResult.sql, QueryResult.params);
|
|
94
90
|
return await this.withConnection(async (conn) => {
|
|
95
|
-
const [rows] = await conn.query<RowDataPacket[]>(sql
|
|
91
|
+
const [rows] = await conn.query<RowDataPacket[]>(sql, values);
|
|
96
92
|
console.log(`[SQL EXECUTOR] 📦 Rows fetched:`, rows);
|
|
97
|
-
return
|
|
93
|
+
return {
|
|
94
|
+
rest: rows.map(this.serialize),
|
|
95
|
+
sql: {
|
|
96
|
+
sql, values
|
|
97
|
+
}
|
|
98
|
+
};
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
async insert
|
|
102
|
+
async insert(table: G['RestShortTableName'], data: Record<string, any>) {
|
|
102
103
|
const keys = Object.keys(data);
|
|
103
104
|
const values = keys.map(k => data[k]);
|
|
104
105
|
const placeholders = keys.map(() => '?').join(', ');
|
|
105
|
-
const sql = `INSERT INTO \`${table}\` (${keys.join(', ')})
|
|
106
|
+
const sql = `INSERT INTO \`${table}\` (${keys.join(', ')})
|
|
107
|
+
VALUES (${placeholders})`;
|
|
106
108
|
|
|
107
109
|
console.log(`[SQL EXECUTOR] 🧠 Generated INSERT SQL:`, sql);
|
|
108
110
|
console.log(`[SQL EXECUTOR] 🔢 Values:`, values);
|
|
109
111
|
|
|
110
112
|
return await this.withConnection(async (conn) => {
|
|
111
113
|
const [result] = await conn.execute<ResultSetHeader>(sql, values);
|
|
112
|
-
return
|
|
114
|
+
return {
|
|
115
|
+
rest: result,
|
|
116
|
+
sql: {
|
|
117
|
+
sql, placeholders
|
|
118
|
+
}
|
|
119
|
+
};
|
|
113
120
|
});
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
async update
|
|
123
|
+
async update(table: G['RestShortTableName'], primary: string[], data: Record<string, any>) {
|
|
117
124
|
if (!primary?.length) throw new Error('Primary key is required for update');
|
|
118
125
|
const keys = Object.keys(data);
|
|
119
126
|
const values = keys.map(k => data[k]);
|
|
120
127
|
const updates = keys.map(k => `\`${k}\` = ?`).join(', ');
|
|
121
|
-
const sql = `UPDATE \`${table}\`
|
|
128
|
+
const sql = `UPDATE \`${table}\`
|
|
129
|
+
SET ${updates}
|
|
130
|
+
WHERE \`${primary[0]}\` = ?`;
|
|
122
131
|
values.push(data[primary[0]]);
|
|
123
132
|
|
|
124
133
|
console.log(`[SQL EXECUTOR] 🧠 Generated UPDATE SQL:`, sql);
|
|
@@ -126,38 +135,60 @@ export class SqlExecutor<
|
|
|
126
135
|
|
|
127
136
|
return await this.withConnection(async (conn) => {
|
|
128
137
|
const [result] = await conn.execute<ResultSetHeader>(sql, values);
|
|
129
|
-
return
|
|
138
|
+
return {
|
|
139
|
+
rest:result,
|
|
140
|
+
sql: {
|
|
141
|
+
sql, values
|
|
142
|
+
}
|
|
143
|
+
};
|
|
130
144
|
});
|
|
131
145
|
}
|
|
132
146
|
|
|
133
|
-
async delete
|
|
147
|
+
async delete(table: G['RestShortTableName'], primary: string[], args: Record<string, any>) {
|
|
134
148
|
const key = primary?.[0];
|
|
135
149
|
if (!key || !args?.[key]) throw new Error('Primary key and value required for delete');
|
|
136
|
-
const sql = `DELETE
|
|
150
|
+
const sql = `DELETE
|
|
151
|
+
FROM \`${table}\`
|
|
152
|
+
WHERE \`${key}\` = ?`;
|
|
137
153
|
|
|
138
154
|
console.log(`[SQL EXECUTOR] 🧠 Generated DELETE SQL:`, sql);
|
|
139
155
|
console.log(`[SQL EXECUTOR] 🔢 Value:`, args[key]);
|
|
140
156
|
|
|
141
157
|
return await this.withConnection(async (conn) => {
|
|
142
158
|
const [result] = await conn.execute<ResultSetHeader>(sql, [args[key]]);
|
|
143
|
-
return
|
|
159
|
+
return {
|
|
160
|
+
rest: result,
|
|
161
|
+
sql: {
|
|
162
|
+
sql, args
|
|
163
|
+
}
|
|
164
|
+
};
|
|
144
165
|
});
|
|
145
166
|
}
|
|
146
167
|
|
|
168
|
+
public formatSQLWithParams(sql: string, params: any[] | { [key: string]: any }): string {
|
|
169
|
+
if (Array.isArray(params)) {
|
|
170
|
+
let index = 0;
|
|
171
|
+
return sql.replace(/\?/g, () => {
|
|
172
|
+
if (index >= params.length) return '?';
|
|
173
|
+
const val = params[index++];
|
|
174
|
+
return this.formatValue(val);
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
return sql.replace(/:([a-zA-Z0-9_]+)/g, (_, key) => {
|
|
178
|
+
const val = params[key];
|
|
179
|
+
return this.formatValue(val);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
147
183
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (Buffer.isBuffer(val)) return `UNHEX('${val.toString('hex')}')`;
|
|
156
|
-
if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
|
|
157
|
-
if (typeof val === 'number') return val.toString();
|
|
158
|
-
if (val instanceof Date) return `'${val.toISOString().slice(0, 19).replace('T', ' ')}'`;
|
|
159
|
-
return `'${JSON.stringify(val)}'`;
|
|
160
|
-
});
|
|
184
|
+
private formatValue(val: any): string {
|
|
185
|
+
if (val === null || val === undefined) return 'NULL';
|
|
186
|
+
if (Buffer.isBuffer(val)) return `UNHEX('${val.toString('hex')}')`;
|
|
187
|
+
if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
|
|
188
|
+
if (typeof val === 'number') return val.toString();
|
|
189
|
+
if (val instanceof Date) return `'${val.toISOString().slice(0, 19).replace('T', ' ')}'`;
|
|
190
|
+
return `'${JSON.stringify(val)}'`;
|
|
161
191
|
}
|
|
162
192
|
|
|
193
|
+
|
|
163
194
|
}
|
|
@@ -38,6 +38,7 @@ export function ExpressHandler({C6, mysqlPool}: { C6: iC6Object, mysqlPool: Pool
|
|
|
38
38
|
res.status(405).json({error: `Method ${method} not allowed`});
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
+
|
|
41
42
|
const response = await restRequest({
|
|
42
43
|
C6,
|
|
43
44
|
mysqlPool,
|
|
@@ -45,10 +46,10 @@ export function ExpressHandler({C6, mysqlPool}: { C6: iC6Object, mysqlPool: Pool
|
|
|
45
46
|
restModel: C6.TABLES[table]
|
|
46
47
|
})(payload);
|
|
47
48
|
|
|
48
|
-
console.log('response', JSON.stringify(response));
|
|
49
|
-
|
|
50
49
|
res.status(200).json({success: true, ...response});
|
|
50
|
+
|
|
51
51
|
} catch (err) {
|
|
52
|
+
res.status(500).json({success: false, error: err});
|
|
52
53
|
next(err);
|
|
53
54
|
}
|
|
54
55
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import isVerbose from "../../../variables/isVerbose";
|
|
2
|
+
import {Executor} from "../../executors/Executor";
|
|
3
|
+
import {OrmGenerics} from "../../types/ormGenerics";
|
|
4
|
+
|
|
5
|
+
export abstract class AggregateBuilder<G extends OrmGenerics> extends Executor<G>{
|
|
6
|
+
buildAggregateField(field: string | any[]): string {
|
|
7
|
+
if (typeof field === 'string') {
|
|
8
|
+
return field;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (!Array.isArray(field) || field.length === 0) {
|
|
12
|
+
throw new Error('Invalid SELECT field entry');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let [fn, ...args] = field;
|
|
16
|
+
let alias: string | undefined;
|
|
17
|
+
|
|
18
|
+
if (args.length >= 2 && String(args[args.length - 2]).toUpperCase() === 'AS') {
|
|
19
|
+
alias = String(args.pop());
|
|
20
|
+
args.pop();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const F = String(fn).toUpperCase();
|
|
24
|
+
const argList = args.map(arg =>
|
|
25
|
+
Array.isArray(arg) ? this.buildAggregateField(arg) : arg
|
|
26
|
+
).join(', ');
|
|
27
|
+
|
|
28
|
+
let expr = `${F}(${argList})`;
|
|
29
|
+
|
|
30
|
+
if (alias) {
|
|
31
|
+
expr += ` AS ${alias}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
isVerbose() && console.log(`[SELECT] ${expr}`);
|
|
35
|
+
|
|
36
|
+
return expr;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {C6Constants} from "api/C6Constants";
|
|
2
|
+
import isVerbose from "../../../variables/isVerbose";
|
|
3
|
+
import {OrmGenerics} from "../../types/ormGenerics";
|
|
4
|
+
import {apiReturn, DetermineResponseDataType} from "../../types/ormInterfaces";
|
|
5
|
+
import {convertHexIfBinary} from "../utils/sqlUtils";
|
|
6
|
+
import {AggregateBuilder} from "./AggregateBuilder";
|
|
7
|
+
|
|
8
|
+
export class ConditionBuilder<
|
|
9
|
+
G extends OrmGenerics
|
|
10
|
+
> extends AggregateBuilder<G> {
|
|
11
|
+
execute(): Promise<apiReturn<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>> {
|
|
12
|
+
throw new Error("Method not implemented.");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private readonly OPERATORS = new Set([
|
|
16
|
+
'=', '!=', '<', '<=', '>', '>=',
|
|
17
|
+
'LIKE', 'NOT LIKE', 'IN', 'NOT IN',
|
|
18
|
+
'IS', 'IS NOT', 'BETWEEN', 'NOT BETWEEN',
|
|
19
|
+
C6Constants.MATCH_AGAINST
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
private validateOperator(op: string) {
|
|
23
|
+
if (!this.OPERATORS.has(op)) {
|
|
24
|
+
throw new Error(`Invalid or unsupported SQL operator detected: '${op}'`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private addParam(
|
|
29
|
+
params: any[] | Record<string, any>,
|
|
30
|
+
column: string,
|
|
31
|
+
value: any
|
|
32
|
+
): string {
|
|
33
|
+
const columnDef = this.config.C6[column.split('.')[0]]?.TYPE_VALIDATION?.[column];
|
|
34
|
+
const val = convertHexIfBinary(column, value, columnDef);
|
|
35
|
+
|
|
36
|
+
if (this.useNamedParams) {
|
|
37
|
+
const key = `param${Object.keys(params).length}`;
|
|
38
|
+
(params as Record<string, any>)[key] = val;
|
|
39
|
+
return `:${key}`;
|
|
40
|
+
} else {
|
|
41
|
+
(params as any[]).push(val);
|
|
42
|
+
return '?';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
buildBooleanJoinedConditions(
|
|
47
|
+
set: any,
|
|
48
|
+
andMode: boolean = true,
|
|
49
|
+
params: any[] | Record<string, any> = []
|
|
50
|
+
): string {
|
|
51
|
+
const booleanOperator = andMode ? 'AND' : 'OR';
|
|
52
|
+
|
|
53
|
+
const addCondition = (column: string, op: string, value: any): string => {
|
|
54
|
+
this.validateOperator(op);
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if (op === C6Constants.MATCH_AGAINST && Array.isArray(value)) {
|
|
58
|
+
const [search, mode] = value;
|
|
59
|
+
const paramName = this.useNamedParams ? `param${Object.keys(params).length}` : null;
|
|
60
|
+
if (this.useNamedParams) {
|
|
61
|
+
params[paramName!] = search;
|
|
62
|
+
} else {
|
|
63
|
+
params.push(search);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let againstClause: string;
|
|
67
|
+
|
|
68
|
+
switch ((mode || '').toUpperCase()) {
|
|
69
|
+
case 'BOOLEAN':
|
|
70
|
+
againstClause = this.useNamedParams ? `AGAINST(:${paramName} IN BOOLEAN MODE)` : `AGAINST(? IN BOOLEAN MODE)`;
|
|
71
|
+
break;
|
|
72
|
+
case 'WITH QUERY EXPANSION':
|
|
73
|
+
againstClause = this.useNamedParams ? `AGAINST(:${paramName} WITH QUERY EXPANSION)` : `AGAINST(? WITH QUERY EXPANSION)`;
|
|
74
|
+
break;
|
|
75
|
+
default: // NATURAL or undefined
|
|
76
|
+
againstClause = this.useNamedParams ? `AGAINST(:${paramName})` : `AGAINST(?)`;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const matchClause = `(MATCH(${column}) ${againstClause})`;
|
|
81
|
+
isVerbose() && console.log(`[MATCH_AGAINST] ${matchClause}`);
|
|
82
|
+
return matchClause;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// handle other operators
|
|
86
|
+
return `( ${column} ${op} ${this.addParam(params, column, value)} )`;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const parts: string[] = [];
|
|
90
|
+
|
|
91
|
+
if (typeof set === 'object' && !Array.isArray(set)) {
|
|
92
|
+
for (const [key, value] of Object.entries(set)) {
|
|
93
|
+
if (typeof value === 'object' && value !== null && Object.keys(value).length === 1) {
|
|
94
|
+
const [op, val] = Object.entries(value)[0];
|
|
95
|
+
parts.push(addCondition(key, op, val));
|
|
96
|
+
} else {
|
|
97
|
+
parts.push(addCondition(key, '=', value));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const clause = parts.join(` ${booleanOperator} `);
|
|
103
|
+
return clause ? `(${clause})` : '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
buildWhereClause(whereArg: any, params: any[] | Record<string, any>): string {
|
|
107
|
+
const clause = this.buildBooleanJoinedConditions(whereArg, true, params);
|
|
108
|
+
if (!clause) return '';
|
|
109
|
+
const trimmed = clause.replace(/^\((.*)\)$/, '$1');
|
|
110
|
+
isVerbose() && console.log(`[WHERE] ${trimmed}`);
|
|
111
|
+
return ` WHERE ${trimmed}`;
|
|
112
|
+
}
|
|
113
|
+
}
|