@carbonorm/carbonnode 3.7.6 → 3.7.7
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/README.md +1 -1
- package/dist/api/orm/builders/AggregateBuilder.d.ts +1 -0
- package/dist/api/types/ormInterfaces.d.ts +2 -2
- package/dist/api/utils/cacheManager.d.ts +1 -1
- package/dist/api/utils/normalizeSingularRequest.d.ts +10 -0
- package/dist/index.cjs.js +175 -32
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +176 -34
- package/dist/index.esm.js.map +1 -1
- package/package.json +11 -6
- package/scripts/assets/handlebars/C6.test.ts.handlebars +55 -80
- package/scripts/assets/handlebars/C6.ts.handlebars +28 -2
- package/scripts/generateRestBindings.cjs +17 -6
- package/scripts/generateRestBindings.ts +22 -8
- package/src/__tests__/fixtures/c6.fixture.ts +74 -0
- package/src/__tests__/normalizeSingularRequest.test.ts +95 -0
- package/src/__tests__/sakila-db/C6.js +1487 -0
- package/src/__tests__/sakila-db/C6.test.ts +63 -0
- package/src/__tests__/sakila-db/C6.ts +2206 -0
- package/src/__tests__/sakila-db/sakila-data.sql +46444 -0
- package/src/__tests__/sakila-db/sakila-schema.sql +686 -0
- package/src/__tests__/sakila-db/sakila.mwb +0 -0
- package/src/__tests__/sakila.generated.test.ts +46 -0
- package/src/__tests__/sqlBuilders.complex.test.ts +134 -0
- package/src/__tests__/sqlBuilders.test.ts +121 -0
- package/src/api/convertForRequestBody.ts +1 -1
- package/src/api/executors/HttpExecutor.ts +14 -3
- package/src/api/executors/SqlExecutor.ts +14 -1
- package/src/api/orm/builders/AggregateBuilder.ts +3 -0
- package/src/api/orm/builders/ConditionBuilder.ts +34 -11
- package/src/api/orm/builders/PaginationBuilder.ts +10 -4
- package/src/api/orm/queries/SelectQueryBuilder.ts +3 -0
- package/src/api/orm/queries/UpdateQueryBuilder.ts +2 -1
- package/src/api/types/ormInterfaces.ts +3 -4
- package/src/api/utils/cacheManager.ts +1 -1
- package/src/api/utils/normalizeSingularRequest.ts +138 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { C6Constants as C6C } from "../C6Constants";
|
|
2
|
+
import { C6RestfulModel, iRestMethods, RequestQueryBody } from "../types/ormInterfaces";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Converts a singular T-shaped request into complex ORM format for GET/PUT/DELETE
|
|
6
|
+
* Enforces that all primary keys are present for singular syntax and that the table has PKs.
|
|
7
|
+
* Optionally accepts a previously removed primary key (key/value) to reconstruct WHERE.
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeSingularRequest<
|
|
10
|
+
Method extends iRestMethods,
|
|
11
|
+
T extends Record<string, any>,
|
|
12
|
+
Custom extends Record<string, any> = {},
|
|
13
|
+
Overrides extends Record<string, any> = {}
|
|
14
|
+
> (
|
|
15
|
+
requestMethod: Method,
|
|
16
|
+
request: RequestQueryBody<Method, T, Custom, Overrides>,
|
|
17
|
+
restModel: C6RestfulModel<string, T, any>,
|
|
18
|
+
removedPrimary?: { key: string; value: any }
|
|
19
|
+
): RequestQueryBody<Method, T, Custom, Overrides> {
|
|
20
|
+
if (request == null || typeof request !== 'object') return request;
|
|
21
|
+
|
|
22
|
+
const specialKeys = new Set([
|
|
23
|
+
C6C.SELECT,
|
|
24
|
+
C6C.UPDATE,
|
|
25
|
+
C6C.DELETE,
|
|
26
|
+
C6C.WHERE,
|
|
27
|
+
C6C.JOIN,
|
|
28
|
+
C6C.PAGINATION,
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
// Determine if the request is already complex (has any special key besides PAGINATION)
|
|
32
|
+
const keys = Object.keys(request as any);
|
|
33
|
+
const hasComplexKeys = keys.some(k => k !== C6C.PAGINATION && specialKeys.has(k));
|
|
34
|
+
if (hasComplexKeys) return request; // already complex
|
|
35
|
+
|
|
36
|
+
// We treat it as singular when it's not complex.
|
|
37
|
+
// For GET, PUT, DELETE only
|
|
38
|
+
if (!(requestMethod === C6C.GET || requestMethod === C6C.PUT || requestMethod === C6C.DELETE)) {
|
|
39
|
+
return request;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const pkShorts: string[] = Array.isArray(restModel.PRIMARY_SHORT) ? [...restModel.PRIMARY_SHORT] : [];
|
|
43
|
+
if (!pkShorts.length) {
|
|
44
|
+
throw new Error(`Table (${restModel.TABLE_NAME}) has no primary key; singular request syntax is not allowed.`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Build pk map from request + possibly removed primary key
|
|
48
|
+
const pkValues: Record<string, any> = {};
|
|
49
|
+
for (const pk of pkShorts) {
|
|
50
|
+
const fromRequest = (request as any)[pk];
|
|
51
|
+
if (fromRequest !== undefined && fromRequest !== null) {
|
|
52
|
+
pkValues[pk] = fromRequest;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (removedPrimary && removedPrimary.key === pk) {
|
|
56
|
+
pkValues[pk] = removedPrimary.value;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const missing = pkShorts.filter(pk => !(pk in pkValues));
|
|
62
|
+
if (missing.length) {
|
|
63
|
+
throw new Error(`Singular request requires all primary key(s) [${pkShorts.join(', ')}] for table (${restModel.TABLE_NAME}). Missing: [${missing.join(', ')}]`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Strip API metadata that should remain at root
|
|
67
|
+
const {
|
|
68
|
+
dataInsertMultipleRows,
|
|
69
|
+
cacheResults,
|
|
70
|
+
fetchDependencies,
|
|
71
|
+
debug,
|
|
72
|
+
success,
|
|
73
|
+
error,
|
|
74
|
+
...rest
|
|
75
|
+
} = request as any;
|
|
76
|
+
|
|
77
|
+
if (requestMethod === C6C.GET) {
|
|
78
|
+
const normalized: any = {
|
|
79
|
+
WHERE: { ...pkValues },
|
|
80
|
+
};
|
|
81
|
+
// Preserve pagination if any was added previously
|
|
82
|
+
if ((request as any)[C6C.PAGINATION]) {
|
|
83
|
+
normalized[C6C.PAGINATION] = (request as any)[C6C.PAGINATION];
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
...normalized,
|
|
87
|
+
dataInsertMultipleRows,
|
|
88
|
+
cacheResults,
|
|
89
|
+
fetchDependencies,
|
|
90
|
+
debug,
|
|
91
|
+
success,
|
|
92
|
+
error,
|
|
93
|
+
} as any;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (requestMethod === C6C.DELETE) {
|
|
97
|
+
const normalized: any = {
|
|
98
|
+
[C6C.DELETE]: true,
|
|
99
|
+
WHERE: { ...pkValues },
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
...normalized,
|
|
103
|
+
dataInsertMultipleRows,
|
|
104
|
+
cacheResults,
|
|
105
|
+
fetchDependencies,
|
|
106
|
+
debug,
|
|
107
|
+
success,
|
|
108
|
+
error,
|
|
109
|
+
} as any;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// PUT
|
|
113
|
+
const updateBody: Record<string, any> = {};
|
|
114
|
+
for (const k of Object.keys(rest)) {
|
|
115
|
+
if (pkShorts.includes(k)) continue; // don't update PK columns
|
|
116
|
+
// Skip special request keys if any slipped through
|
|
117
|
+
if (specialKeys.has(k)) continue;
|
|
118
|
+
updateBody[k] = (rest as any)[k];
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(updateBody).length === 0) {
|
|
121
|
+
throw new Error(`Singular PUT request for table (${restModel.TABLE_NAME}) must include at least one non-primary field to update.`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const normalized: any = {
|
|
125
|
+
[C6C.UPDATE]: updateBody,
|
|
126
|
+
WHERE: { ...pkValues },
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
...normalized,
|
|
131
|
+
dataInsertMultipleRows,
|
|
132
|
+
cacheResults,
|
|
133
|
+
fetchDependencies,
|
|
134
|
+
debug,
|
|
135
|
+
success,
|
|
136
|
+
error,
|
|
137
|
+
} as any;
|
|
138
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ export * from "./api/utils/apiHelpers";
|
|
|
35
35
|
export * from "./api/utils/cacheManager";
|
|
36
36
|
export * from "./api/utils/determineRuntimeJsType";
|
|
37
37
|
export * from "./api/utils/logger";
|
|
38
|
+
export * from "./api/utils/normalizeSingularRequest";
|
|
38
39
|
export * from "./api/utils/sortAndSerializeQueryObject";
|
|
39
40
|
export * from "./api/utils/testHelpers";
|
|
40
41
|
export * from "./api/utils/toastNotifier";
|