@carbonorm/carbonnode 6.0.19 → 6.1.0
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 +46 -1
- package/dist/constants/C6Constants.d.ts +342 -338
- package/dist/executors/SqlExecutor.d.ts +8 -0
- package/dist/index.cjs.js +751 -272
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +744 -273
- package/dist/index.esm.js.map +1 -1
- package/dist/orm/builders/AggregateBuilder.d.ts +5 -1
- package/dist/orm/builders/ConditionBuilder.d.ts +2 -3
- package/dist/orm/builders/ExpressionSerializer.d.ts +22 -0
- package/dist/orm/builders/PaginationBuilder.d.ts +4 -6
- package/dist/orm/queryHelpers.d.ts +12 -1
- package/dist/types/mysqlTypes.d.ts +6 -1
- package/dist/types/ormInterfaces.d.ts +7 -5
- package/dist/utils/cacheManager.d.ts +3 -2
- package/package.json +2 -2
- package/scripts/assets/handlebars/C6.test.ts.handlebars +4 -4
- package/src/__tests__/cacheManager.test.ts +28 -0
- package/src/__tests__/expressServer.e2e.test.ts +26 -17
- package/src/__tests__/httpExecutorSingular.e2e.test.ts +53 -14
- package/src/__tests__/normalizeSingularRequest.test.ts +26 -8
- 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 +1 -1
- package/src/__tests__/sakila-db/C6.test.ts +4 -4
- package/src/__tests__/sakila-db/C6.ts +1 -1
- package/src/__tests__/sakila-db/sqlResponses/C6.actor.post.json +11 -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 +26 -7
- 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 +9 -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 +10 -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 +9 -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 +18 -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 +18 -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 +9 -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 +9 -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 +13 -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.join.json +10 -10
- package/src/__tests__/sakila-db/sqlResponses/C6.rental.post.json +14 -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__/sqlBuilders.complex.test.ts +62 -74
- package/src/__tests__/sqlBuilders.expressions.test.ts +58 -30
- package/src/__tests__/sqlBuilders.test.ts +68 -4
- package/src/__tests__/sqlExecutorPostUuid.test.ts +185 -0
- package/src/constants/C6Constants.ts +3 -1
- package/src/executors/HttpExecutor.ts +35 -6
- package/src/executors/SqlExecutor.ts +232 -4
- package/src/index.ts +1 -0
- package/src/orm/builders/AggregateBuilder.ts +67 -106
- package/src/orm/builders/ConditionBuilder.ts +69 -93
- package/src/orm/builders/ExpressionSerializer.ts +275 -0
- package/src/orm/builders/PaginationBuilder.ts +24 -34
- package/src/orm/queryHelpers.ts +29 -0
- package/src/types/mysqlTypes.ts +130 -9
- package/src/types/ormInterfaces.ts +7 -7
- package/src/utils/cacheManager.ts +6 -4
- package/src/utils/normalizeSingularRequest.ts +11 -4
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Executor } from "../../executors/Executor";
|
|
2
2
|
import { OrmGenerics } from "../../types/ormGenerics";
|
|
3
|
+
import { iSerializedExpression, tSqlParams } from "./ExpressionSerializer";
|
|
3
4
|
export declare abstract class AggregateBuilder<G extends OrmGenerics> extends Executor<G> {
|
|
4
5
|
protected selectAliases: Set<string>;
|
|
5
6
|
protected assertValidIdentifier(_identifier: string, _context: string): void;
|
|
6
|
-
|
|
7
|
+
protected isReferenceExpression(value: string): boolean;
|
|
8
|
+
protected isKnownFunction(functionName: string): boolean;
|
|
9
|
+
protected serializeExpression(expression: any, params?: tSqlParams, context?: string, contextColumn?: string): iSerializedExpression;
|
|
10
|
+
buildAggregateField(field: string | any[], params?: tSqlParams): string;
|
|
7
11
|
}
|
|
@@ -9,6 +9,7 @@ export declare abstract class ConditionBuilder<G extends OrmGenerics> extends Ag
|
|
|
9
9
|
protected registerAlias(alias: string, table: string): void;
|
|
10
10
|
protected assertValidIdentifier(identifier: string, context: string): void;
|
|
11
11
|
protected isColumnRef(ref: string): boolean;
|
|
12
|
+
protected isReferenceExpression(value: string): boolean;
|
|
12
13
|
abstract build(table: string): SqlBuilderResult;
|
|
13
14
|
execute(): Promise<DetermineResponseDataType<G['RequestMethod'], G['RestTableInterface']>>;
|
|
14
15
|
private readonly BOOLEAN_OPERATORS;
|
|
@@ -19,12 +20,10 @@ export declare abstract class ConditionBuilder<G extends OrmGenerics> extends Ag
|
|
|
19
20
|
private normalizeOperatorKey;
|
|
20
21
|
private formatOperator;
|
|
21
22
|
private isOperator;
|
|
22
|
-
private looksLikeSafeFunctionExpression;
|
|
23
23
|
private ensureWrapped;
|
|
24
24
|
private joinBooleanParts;
|
|
25
|
-
private normalizeFunctionField;
|
|
26
|
-
private buildFunctionCall;
|
|
27
25
|
private serializeOperand;
|
|
26
|
+
private isExpressionTuple;
|
|
28
27
|
private isPlainArrayLiteral;
|
|
29
28
|
private isPlainObjectLiteral;
|
|
30
29
|
private resolveColumnDefinition;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type tSqlParams = any[] | Record<string, any>;
|
|
2
|
+
export interface iSerializedExpression {
|
|
3
|
+
sql: string;
|
|
4
|
+
isReference: boolean;
|
|
5
|
+
isExpression: boolean;
|
|
6
|
+
isSubSelect: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface iExpressionSerializerHooks {
|
|
9
|
+
assertValidIdentifier(identifier: string, context: string): void;
|
|
10
|
+
isReference(value: string): boolean;
|
|
11
|
+
addParam?: (params: tSqlParams, column: string, value: any) => string;
|
|
12
|
+
buildScalarSubSelect?: (subRequest: any, params: tSqlParams) => string;
|
|
13
|
+
onAlias?: (alias: string) => void;
|
|
14
|
+
isKnownFunction?: (functionName: string) => boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface iExpressionSerializerOptions {
|
|
17
|
+
hooks: iExpressionSerializerHooks;
|
|
18
|
+
params?: tSqlParams;
|
|
19
|
+
context: string;
|
|
20
|
+
contextColumn?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const serializeSqlExpression: (value: any, opts: iExpressionSerializerOptions) => iSerializedExpression;
|
|
@@ -6,12 +6,10 @@ export declare abstract class PaginationBuilder<G extends OrmGenerics> extends J
|
|
|
6
6
|
*
|
|
7
7
|
* Accepted structures:
|
|
8
8
|
* ```ts
|
|
9
|
-
* ORDER:
|
|
10
|
-
*
|
|
11
|
-
* [property_units.
|
|
12
|
-
*
|
|
13
|
-
* [C6Constants.ST_DISTANCE_SPHERE]: [property_units.LOCATION, F(property_units.LOCATION, "pu_target")]
|
|
14
|
-
* }
|
|
9
|
+
* ORDER: [
|
|
10
|
+
* [property_units.UNIT_ID, "DESC"],
|
|
11
|
+
* [[C6Constants.ST_DISTANCE_SPHERE, property_units.LOCATION, F(property_units.LOCATION, "pu_target")], "ASC"],
|
|
12
|
+
* ]
|
|
15
13
|
* ```
|
|
16
14
|
*/
|
|
17
15
|
buildPaginationClause(pagination: any, params?: any[] | Record<string, any>): string;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { C6C } from "../constants/C6Constants";
|
|
2
|
+
import { OrderDirection, OrderTerm, SQLExpression, SQLKnownFunction } from "../types/mysqlTypes";
|
|
3
|
+
type DerivedTableSpec = Record<string, any> & {
|
|
4
|
+
[C6C.SUBSELECT]?: Record<string, any>;
|
|
5
|
+
[C6C.AS]?: string;
|
|
6
|
+
};
|
|
2
7
|
export declare const isDerivedTableKey: (key: string) => boolean;
|
|
3
8
|
export declare const resolveDerivedTable: (key: string) => DerivedTableSpec | undefined;
|
|
4
9
|
export declare const derivedTable: <T extends DerivedTableSpec>(spec: T) => T;
|
|
@@ -8,4 +13,10 @@ export declare const fieldEq: (leftCol: string, rightCol: string, leftAlias: str
|
|
|
8
13
|
export declare const distSphere: (fromCol: string, toCol: string, fromAlias: string, toAlias: string) => any[];
|
|
9
14
|
export declare const bbox: (minLng: number, minLat: number, maxLng: number, maxLat: number) => any[];
|
|
10
15
|
export declare const stContains: (envelope: string, shape: string) => any[];
|
|
16
|
+
export declare const fn: <Fn extends SQLKnownFunction>(functionName: Fn, ...args: SQLExpression[]) => [Fn, ...SQLExpression[]];
|
|
17
|
+
export declare const call: (functionName: string, ...args: SQLExpression[]) => [typeof C6C.CALL, string, ...SQLExpression[]];
|
|
18
|
+
export declare const alias: (expression: SQLExpression, aliasName: string) => [typeof C6C.AS, SQLExpression, string];
|
|
19
|
+
export declare const distinct: (expression: SQLExpression) => [typeof C6C.DISTINCT, SQLExpression];
|
|
20
|
+
export declare const lit: (value: any) => [typeof C6C.LIT, any];
|
|
21
|
+
export declare const order: (expression: SQLExpression, direction?: OrderDirection) => OrderTerm;
|
|
11
22
|
export {};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare const SQL_KNOWN_FUNCTIONS: readonly ["ADDDATE", "ADDTIME", "CONCAT", "CONVERT_TZ", "COUNT", "COUNT_ALL", "CURRENT_DATE", "CURRENT_TIMESTAMP", "DAY", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DAYNAME", "DAYOFMONTH", "DAYOFWEEK", "DAYOFYEAR", "DATE", "DATE_ADD", "DATEDIFF", "DATE_SUB", "DATE_FORMAT", "EXTRACT", "FROM_DAYS", "FROM_UNIXTIME", "GET_FORMAT", "GROUP_CONCAT", "HEX", "HOUR", "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "INTERVAL", "LOCALTIME", "LOCALTIMESTAMP", "MAKEDATE", "MAKETIME", "MAX", "MBRContains", "MICROSECOND", "MIN", "MINUTE", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MONTH", "MONTHNAME", "NOW", "POINT", "POLYGON", "SECOND", "SECOND_MICROSECOND", "ST_Area", "ST_AsBinary", "ST_AsText", "ST_Buffer", "ST_Contains", "ST_Crosses", "ST_Difference", "ST_Dimension", "ST_Disjoint", "ST_Distance", "ST_Distance_Sphere", "ST_EndPoint", "ST_Envelope", "ST_Equals", "ST_GeomFromGeoJSON", "ST_GeomFromText", "ST_GeomFromWKB", "ST_Intersects", "ST_Length", "ST_MakeEnvelope", "ST_Overlaps", "ST_Point", "ST_SetSRID", "ST_SRID", "ST_StartPoint", "ST_SymDifference", "ST_Touches", "ST_Union", "ST_Within", "ST_X", "ST_Y", "STR_TO_DATE", "SUBDATE", "SUBTIME", "SUM", "SYSDATE", "TIME", "TIME_FORMAT", "TIME_TO_SEC", "TIMEDIFF", "TIMESTAMP", "TIMESTAMPADD", "TIMESTAMPDIFF", "TO_DAYS", "TO_SECONDS", "TRANSACTION_TIMESTAMP", "UNHEX", "UNIX_TIMESTAMP", "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "WEEKDAY", "WEEKOFYEAR", "YEARWEEK"];
|
|
2
|
+
export type SQLKnownFunction = typeof SQL_KNOWN_FUNCTIONS[number];
|
|
3
|
+
export type SQLFunction = SQLKnownFunction;
|
|
2
4
|
export type SQLComparisonOperator = '=' | '!=' | '<' | '<=' | '>' | '>=' | 'IN' | 'NOT IN' | 'LIKE' | 'IS NULL' | 'IS NOT NULL' | 'BETWEEN' | 'LESS_THAN' | 'GREATER_THAN';
|
|
3
5
|
export type JoinType = 'INNER' | 'LEFT_OUTER' | 'RIGHT_OUTER';
|
|
4
6
|
export type OrderDirection = 'ASC' | 'DESC';
|
|
7
|
+
export type SQLExpression = string | number | boolean | null | SQLExpressionTuple;
|
|
8
|
+
export type SQLExpressionTuple = ['AS', SQLExpression, string] | ['DISTINCT', SQLExpression] | ['CALL', string, ...SQLExpression[]] | ['LIT', any] | ['PARAM', any] | ['SUBSELECT', Record<string, any>] | [SQLKnownFunction, ...SQLExpression[]];
|
|
9
|
+
export type OrderTerm = [SQLExpression, OrderDirection?];
|
|
@@ -2,7 +2,7 @@ import type { AxiosInstance, AxiosResponse } from "axios";
|
|
|
2
2
|
import type { Pool } from "mysql2/promise";
|
|
3
3
|
import { eFetchDependencies } from "./dynamicFetching";
|
|
4
4
|
import { Modify } from "./modifyTypes";
|
|
5
|
-
import { JoinType,
|
|
5
|
+
import { JoinType, OrderTerm, SQLComparisonOperator, SQLExpression } from "./mysqlTypes";
|
|
6
6
|
import type { CarbonReact, iStateAdapter } from "@carbonorm/carbonreact";
|
|
7
7
|
import type { OrmGenerics } from "./ormGenerics";
|
|
8
8
|
import { restOrm } from "../api/restOrm";
|
|
@@ -41,7 +41,7 @@ export type SubSelect<T extends {
|
|
|
41
41
|
};
|
|
42
42
|
export type SelectField<T extends {
|
|
43
43
|
[key: string]: any;
|
|
44
|
-
} = any> = keyof T |
|
|
44
|
+
} = any> = keyof T | SQLExpression | SubSelect<T>;
|
|
45
45
|
export type WhereClause<T = any> = Partial<T> | LogicalGroup<T> | ComparisonClause<T>;
|
|
46
46
|
export type LogicalGroup<T = any> = {
|
|
47
47
|
[logicalGroup: string]: Array<WhereClause<T>>;
|
|
@@ -54,10 +54,10 @@ export type JoinClause<T = any> = {
|
|
|
54
54
|
export type Join<T = any> = {
|
|
55
55
|
[K in JoinType]?: JoinClause<T>;
|
|
56
56
|
};
|
|
57
|
-
export type Pagination
|
|
57
|
+
export type Pagination = {
|
|
58
58
|
PAGE?: number;
|
|
59
59
|
LIMIT?: number | null;
|
|
60
|
-
ORDER?:
|
|
60
|
+
ORDER?: OrderTerm[];
|
|
61
61
|
};
|
|
62
62
|
export type RequestGetPutDeleteBody<T extends {
|
|
63
63
|
[key: string]: any;
|
|
@@ -67,7 +67,7 @@ export type RequestGetPutDeleteBody<T extends {
|
|
|
67
67
|
DELETE?: boolean;
|
|
68
68
|
WHERE?: WhereClause<T>;
|
|
69
69
|
JOIN?: Join<T>;
|
|
70
|
-
PAGINATION?: Pagination
|
|
70
|
+
PAGINATION?: Pagination;
|
|
71
71
|
};
|
|
72
72
|
export type RequestPostBody<T extends {
|
|
73
73
|
[key: string]: any;
|
|
@@ -80,6 +80,7 @@ export type iAPI<T extends {
|
|
|
80
80
|
}> = T & {
|
|
81
81
|
dataInsertMultipleRows?: T[];
|
|
82
82
|
cacheResults?: boolean;
|
|
83
|
+
skipReactBootstrap?: boolean;
|
|
83
84
|
fetchDependencies?: number | eFetchDependencies | Awaited<iGetC6RestResponse<any>>[];
|
|
84
85
|
debug?: boolean;
|
|
85
86
|
success?: string | ((r: AxiosResponse) => string | void);
|
|
@@ -105,6 +106,7 @@ export interface iCacheResponse<ResponseDataType = any> {
|
|
|
105
106
|
export interface iCacheAPI<ResponseDataType = any> {
|
|
106
107
|
requestArgumentsSerialized: string;
|
|
107
108
|
request: Promise<iCacheResponse<ResponseDataType>>;
|
|
109
|
+
allowListStatus?: "allowed" | "denied" | "not verified";
|
|
108
110
|
response?: iCacheResponse<ResponseDataType> & {
|
|
109
111
|
__carbonTiming?: {
|
|
110
112
|
start: number;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { iCacheAPI, iCacheResponse } from "../types/ormInterfaces";
|
|
2
2
|
import { LogContext } from "./logLevel";
|
|
3
|
+
import { SqlAllowListStatus } from "./logSql";
|
|
3
4
|
export declare const apiRequestCache: Map<string, iCacheAPI<any>>;
|
|
4
5
|
export declare const userCustomClearCache: (() => void)[];
|
|
5
6
|
export declare function clearCache(props?: {
|
|
6
7
|
ignoreWarning?: boolean;
|
|
7
8
|
}): void;
|
|
8
|
-
export declare function checkCache<ResponseDataType = any>(method: string, tableName: string | string[], requestData: any, logContext
|
|
9
|
+
export declare function checkCache<ResponseDataType = any>(method: string, tableName: string | string[], requestData: any, logContext?: LogContext, allowListStatus?: SqlAllowListStatus): Promise<iCacheResponse<ResponseDataType>> | false;
|
|
9
10
|
export declare function setCache<ResponseDataType = any>(method: string, tableName: string | string[], requestData: any, cacheEntry: iCacheAPI<ResponseDataType>): void;
|
|
10
|
-
export declare function evictCacheEntry(method: string, tableName: string | string[], requestData: any, logContext?: LogContext): boolean;
|
|
11
|
+
export declare function evictCacheEntry(method: string, tableName: string | string[], requestData: any, logContext?: LogContext, allowListStatus?: SqlAllowListStatus): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carbonorm/carbonnode",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"browser": "dist/index.umd.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"hooksPath": ".githooks"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@carbonorm/carbonreact": "^6.0.
|
|
23
|
+
"@carbonorm/carbonreact": "^6.0.4",
|
|
24
24
|
"buffer": "^6.0.3",
|
|
25
25
|
"geojson": "^0.5.0",
|
|
26
26
|
"handlebars": "^4.7.8",
|
|
@@ -101,7 +101,7 @@ function buildScalarValue(meta: any, columnName: string, seedRow: any) {
|
|
|
101
101
|
'geometrycollection',
|
|
102
102
|
];
|
|
103
103
|
if (geometryTypes.some((type) => mysqlType.includes(type))) {
|
|
104
|
-
return
|
|
104
|
+
return [C6.ST_GEOMFROMTEXT, [C6.LIT, 'POINT(0 0)']];
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
if (mysqlType === 'json') return {};
|
|
@@ -236,7 +236,7 @@ function buildUpdatedValue(meta: any, columnName: string, currentValue: any) {
|
|
|
236
236
|
|
|
237
237
|
if (mysqlType === 'year') return new Date().getFullYear();
|
|
238
238
|
if (geometryTypes.some((type) => mysqlType.includes(type))) {
|
|
239
|
-
return
|
|
239
|
+
return [C6.ST_GEOMFROMTEXT, [C6.LIT, 'POINT(1 1)']];
|
|
240
240
|
}
|
|
241
241
|
if (mysqlType === 'json') return { updated: true };
|
|
242
242
|
|
|
@@ -525,7 +525,7 @@ describe('sakila-db generated C6 bindings', () => {
|
|
|
525
525
|
restBinding.Get({
|
|
526
526
|
[C6.PAGINATION]: {
|
|
527
527
|
[C6.LIMIT]: 1,
|
|
528
|
-
[C6.ORDER]:
|
|
528
|
+
[C6.ORDER]: [[primaryFull, C6.DESC]],
|
|
529
529
|
},
|
|
530
530
|
cacheResults: false,
|
|
531
531
|
} as any)
|
|
@@ -550,7 +550,7 @@ describe('sakila-db generated C6 bindings', () => {
|
|
|
550
550
|
restBinding.Get({
|
|
551
551
|
[C6.PAGINATION]: {
|
|
552
552
|
[C6.LIMIT]: 1,
|
|
553
|
-
[C6.ORDER]:
|
|
553
|
+
[C6.ORDER]: [[primaryFull, C6.DESC]],
|
|
554
554
|
},
|
|
555
555
|
cacheResults: false,
|
|
556
556
|
} as any)
|
|
@@ -79,14 +79,42 @@ describe("cacheManager with map storage", () => {
|
|
|
79
79
|
},
|
|
80
80
|
},
|
|
81
81
|
} as any,
|
|
82
|
+
allowListStatus: "allowed",
|
|
82
83
|
final: true,
|
|
83
84
|
});
|
|
84
85
|
|
|
85
86
|
expect(evictCacheEntry("GET", "table", requestData, { verbose: true })).toBe(true);
|
|
86
87
|
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
87
88
|
expect(String(logSpy.mock.calls[0]?.[0] ?? "")).toContain("[CACHE EVICTED]");
|
|
89
|
+
expect(String(logSpy.mock.calls[0]?.[0] ?? "")).toContain("[VERIFIED]");
|
|
88
90
|
expect(String(logSpy.mock.calls[0]?.[0] ?? "")).toContain("[SELECT]");
|
|
89
91
|
|
|
90
92
|
logSpy.mockRestore();
|
|
91
93
|
});
|
|
94
|
+
|
|
95
|
+
it("logs verified for cache hits when allowlist is provided", () => {
|
|
96
|
+
const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
|
|
97
|
+
const mockRequest = Promise.resolve({ data: { rest: [] } }) as AxiosPromise;
|
|
98
|
+
|
|
99
|
+
setCache("GET", "table", requestData, {
|
|
100
|
+
requestArgumentsSerialized: "serialized",
|
|
101
|
+
request: mockRequest,
|
|
102
|
+
response: {
|
|
103
|
+
data: {
|
|
104
|
+
sql: {
|
|
105
|
+
sql: "SELECT * FROM table WHERE id = 1",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
} as any,
|
|
109
|
+
final: true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const cached = checkCache("GET", "table", requestData, { verbose: true }, "allowed");
|
|
113
|
+
expect(cached).toBe(mockRequest);
|
|
114
|
+
expect(logSpy).toHaveBeenCalledTimes(1);
|
|
115
|
+
expect(String(logSpy.mock.calls[0]?.[0] ?? "")).toContain("[CACHE HIT]");
|
|
116
|
+
expect(String(logSpy.mock.calls[0]?.[0] ?? "")).toContain("[VERIFIED]");
|
|
117
|
+
|
|
118
|
+
logSpy.mockRestore();
|
|
119
|
+
});
|
|
92
120
|
});
|
|
@@ -3,7 +3,7 @@ import axios from "axios";
|
|
|
3
3
|
import { AddressInfo } from "net";
|
|
4
4
|
import {describe, it, expect, beforeAll, afterAll} from "vitest";
|
|
5
5
|
import { restOrm } from "@carbonorm/carbonnode";
|
|
6
|
-
import {Actor, C6, Film_Actor} from "./sakila-db/C6
|
|
6
|
+
import {Actor, C6, Film_Actor, TABLES} from "./sakila-db/C6";
|
|
7
7
|
import {C6C} from "../constants/C6Constants";
|
|
8
8
|
import createTestServer from "./fixtures/createTestServer";
|
|
9
9
|
|
|
@@ -13,14 +13,14 @@ let restURL: string;
|
|
|
13
13
|
let axiosClient: ReturnType<typeof axios.create>;
|
|
14
14
|
const actorHttp = restOrm<any>(() => ({
|
|
15
15
|
C6,
|
|
16
|
-
restModel:
|
|
16
|
+
restModel: TABLES.actor,
|
|
17
17
|
restURL,
|
|
18
18
|
axios: axiosClient,
|
|
19
19
|
verbose: false,
|
|
20
20
|
}));
|
|
21
21
|
const filmActorHttp = restOrm<any>(() => ({
|
|
22
22
|
C6,
|
|
23
|
-
restModel:
|
|
23
|
+
restModel: TABLES.film_actor,
|
|
24
24
|
restURL,
|
|
25
25
|
axios: axiosClient,
|
|
26
26
|
verbose: false,
|
|
@@ -100,30 +100,33 @@ describe("ExpressHandler e2e", () => {
|
|
|
100
100
|
} as any);
|
|
101
101
|
|
|
102
102
|
let data = await actorRequest("GET", {
|
|
103
|
-
[C6C.WHERE]: {
|
|
103
|
+
[C6C.WHERE]: {
|
|
104
|
+
[Actor.FIRST_NAME]: [C6C.EQUAL, [C6C.LIT, first_name]],
|
|
105
|
+
[Actor.LAST_NAME]: [C6C.EQUAL, [C6C.LIT, last_name]],
|
|
106
|
+
},
|
|
104
107
|
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
105
108
|
} as any);
|
|
106
109
|
|
|
107
110
|
expect(data?.rest).toHaveLength(1);
|
|
108
|
-
const testId = data?.rest[0].actor_id;
|
|
111
|
+
const testId = Number(data?.rest[0].actor_id);
|
|
109
112
|
|
|
110
113
|
await actorRequest("PUT", {
|
|
111
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: testId },
|
|
114
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(testId)]] },
|
|
112
115
|
[C6C.UPDATE]: { first_name: "Updated" },
|
|
113
116
|
} as any);
|
|
114
117
|
|
|
115
118
|
data = await actorRequest("GET", {
|
|
116
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: testId },
|
|
119
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(testId)]] },
|
|
117
120
|
} as any);
|
|
118
121
|
expect(data?.rest).toHaveLength(1);
|
|
119
122
|
expect(data?.rest[0].first_name).toBe("Updated");
|
|
120
123
|
|
|
121
124
|
await actorRequest("DELETE", {
|
|
122
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: testId },
|
|
125
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(testId)]] },
|
|
123
126
|
[C6C.DELETE]: true,
|
|
124
127
|
} as any);
|
|
125
128
|
data = await actorRequest("GET", {
|
|
126
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: testId },
|
|
129
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(testId)]] },
|
|
127
130
|
cacheResults: false,
|
|
128
131
|
} as any);
|
|
129
132
|
expect(Array.isArray(data?.rest)).toBe(true);
|
|
@@ -142,20 +145,23 @@ describe("ExpressHandler e2e", () => {
|
|
|
142
145
|
const payload = { greeting: "hello", flags: [1, true] };
|
|
143
146
|
|
|
144
147
|
let data = await actorRequest("GET", {
|
|
145
|
-
[C6C.WHERE]: {
|
|
148
|
+
[C6C.WHERE]: {
|
|
149
|
+
[Actor.FIRST_NAME]: [C6C.EQUAL, [C6C.LIT, first_name]],
|
|
150
|
+
[Actor.LAST_NAME]: [C6C.EQUAL, [C6C.LIT, last_name]],
|
|
151
|
+
},
|
|
146
152
|
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
147
153
|
} as any);
|
|
148
154
|
|
|
149
|
-
const actorId = data?.rest?.[0]?.actor_id;
|
|
155
|
+
const actorId = Number(data?.rest?.[0]?.actor_id);
|
|
150
156
|
expect(actorId).toBeTruthy();
|
|
151
157
|
|
|
152
158
|
await actorRequest("PUT", {
|
|
153
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: actorId },
|
|
159
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(actorId)]] },
|
|
154
160
|
[C6C.UPDATE]: { first_name: payload },
|
|
155
161
|
} as any);
|
|
156
162
|
|
|
157
163
|
data = await actorRequest("GET", {
|
|
158
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: actorId },
|
|
164
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(actorId)]] },
|
|
159
165
|
} as any);
|
|
160
166
|
|
|
161
167
|
expect(data?.rest?.[0]?.first_name).toBe(JSON.stringify(payload));
|
|
@@ -171,11 +177,14 @@ describe("ExpressHandler e2e", () => {
|
|
|
171
177
|
} as any);
|
|
172
178
|
|
|
173
179
|
const data = await actorRequest("GET", {
|
|
174
|
-
[C6C.WHERE]: {
|
|
180
|
+
[C6C.WHERE]: {
|
|
181
|
+
[Actor.FIRST_NAME]: [C6C.EQUAL, [C6C.LIT, first_name]],
|
|
182
|
+
[Actor.LAST_NAME]: [C6C.EQUAL, [C6C.LIT, last_name]],
|
|
183
|
+
},
|
|
175
184
|
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
176
185
|
} as any);
|
|
177
186
|
|
|
178
|
-
const actorId = data?.rest?.[0]?.actor_id;
|
|
187
|
+
const actorId = Number(data?.rest?.[0]?.actor_id);
|
|
179
188
|
expect(actorId).toBeTruthy();
|
|
180
189
|
|
|
181
190
|
const operatorLike = { [C6C.GREATER_THAN]: "oops" } as any;
|
|
@@ -183,12 +192,12 @@ describe("ExpressHandler e2e", () => {
|
|
|
183
192
|
try {
|
|
184
193
|
const actorSql = restOrm<any>(() => ({
|
|
185
194
|
C6,
|
|
186
|
-
restModel:
|
|
195
|
+
restModel: TABLES.actor,
|
|
187
196
|
mysqlPool: pool,
|
|
188
197
|
verbose: false,
|
|
189
198
|
}));
|
|
190
199
|
await actorSql.Put({
|
|
191
|
-
[C6C.WHERE]: { [Actor.ACTOR_ID]: actorId },
|
|
200
|
+
[C6C.WHERE]: { [Actor.ACTOR_ID]: [C6C.EQUAL, [C6C.LIT, Number(actorId)]] },
|
|
192
201
|
[C6C.UPDATE]: { first_name: operatorLike },
|
|
193
202
|
} as any);
|
|
194
203
|
throw new Error('Expected PUT to reject for operator-like payload.');
|
|
@@ -2,8 +2,8 @@ import mysql from "mysql2/promise";
|
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { AddressInfo } from "net";
|
|
4
4
|
import { describe, it, expect, beforeAll, afterAll, vi } from "vitest";
|
|
5
|
-
import { restOrm } from "@carbonorm/carbonnode";
|
|
6
|
-
import {
|
|
5
|
+
import {iGetC6RestResponse, restOrm } from "@carbonorm/carbonnode";
|
|
6
|
+
import {Actor, C6, iActor, TABLES} from "./sakila-db/C6";
|
|
7
7
|
import { C6C } from "../constants/C6Constants";
|
|
8
8
|
import createTestServer from "./fixtures/createTestServer";
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ let restURL: string;
|
|
|
13
13
|
let axiosClient: ReturnType<typeof axios.create>;
|
|
14
14
|
const actorHttp = restOrm<any>(() => ({
|
|
15
15
|
C6,
|
|
16
|
-
restModel:
|
|
16
|
+
restModel: TABLES.actor,
|
|
17
17
|
restURL,
|
|
18
18
|
axios: axiosClient,
|
|
19
19
|
verbose: false,
|
|
@@ -76,23 +76,26 @@ describe("HttpExecutor singular e2e", () => {
|
|
|
76
76
|
step = "GET-complex";
|
|
77
77
|
// Fetch inserted id using complex query
|
|
78
78
|
let data = await actorRequest("GET", {
|
|
79
|
-
[C6C.WHERE]: {
|
|
79
|
+
[C6C.WHERE]: {
|
|
80
|
+
[Actor.FIRST_NAME]: [C6C.EQUAL, [C6C.LIT, first_name]],
|
|
81
|
+
[Actor.LAST_NAME]: [C6C.EQUAL, [C6C.LIT, last_name]],
|
|
82
|
+
},
|
|
80
83
|
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
81
84
|
});
|
|
82
85
|
|
|
83
86
|
expect(data.rest).toHaveLength(1);
|
|
84
|
-
const testId = data.rest[0].actor_id;
|
|
87
|
+
const testId = Number(data.rest[0].actor_id);
|
|
85
88
|
|
|
86
89
|
step = "GET-singular";
|
|
87
90
|
// GET singular
|
|
88
|
-
data = await actorRequest("GET", { actor_id: testId } as any);
|
|
91
|
+
data = await actorRequest("GET", { actor_id: Number(testId) } as any);
|
|
89
92
|
expect(data.rest).toHaveLength(1);
|
|
90
93
|
expect(data.rest[0].actor_id).toBe(testId);
|
|
91
94
|
|
|
92
95
|
step = "PUT-singular";
|
|
93
96
|
// PUT singular
|
|
94
|
-
await actorRequest("PUT", { actor_id: testId, first_name: "Updated" } as any);
|
|
95
|
-
data = await actorRequest("GET", { actor_id: testId, cacheResults: false } as any);
|
|
97
|
+
await actorRequest("PUT", { actor_id: Number(testId), first_name: "Updated" } as any);
|
|
98
|
+
data = await actorRequest("GET", { actor_id: Number(testId), cacheResults: false } as any);
|
|
96
99
|
expect(data.rest).toHaveLength(1);
|
|
97
100
|
expect(data.rest[0].first_name).toBe("Updated");
|
|
98
101
|
|
|
@@ -105,7 +108,7 @@ describe("HttpExecutor singular e2e", () => {
|
|
|
105
108
|
} as any;
|
|
106
109
|
const actorHttpWithReact = restOrm<any>(() => ({
|
|
107
110
|
C6,
|
|
108
|
-
restModel:
|
|
111
|
+
restModel: TABLES.actor,
|
|
109
112
|
restURL,
|
|
110
113
|
axios: axiosClient,
|
|
111
114
|
verbose: false,
|
|
@@ -123,8 +126,8 @@ describe("HttpExecutor singular e2e", () => {
|
|
|
123
126
|
|
|
124
127
|
step = "DELETE-singular";
|
|
125
128
|
// DELETE singular
|
|
126
|
-
await actorRequest("DELETE", { actor_id: testId } as any);
|
|
127
|
-
data = await actorRequest("GET", { actor_id: testId, cacheResults: false } as any);
|
|
129
|
+
await actorRequest("DELETE", { actor_id: Number(testId) } as any);
|
|
130
|
+
data = await actorRequest("GET", { actor_id: Number(testId), cacheResults: false } as any);
|
|
128
131
|
expect(Array.isArray(data.rest)).toBe(true);
|
|
129
132
|
expect(data.rest.length).toBe(0);
|
|
130
133
|
} catch (error: any) {
|
|
@@ -133,9 +136,9 @@ describe("HttpExecutor singular e2e", () => {
|
|
|
133
136
|
});
|
|
134
137
|
|
|
135
138
|
it("exposes next when pagination continues", async () => {
|
|
136
|
-
const data = await actorRequest("GET", {
|
|
139
|
+
const data: iGetC6RestResponse<iActor, {}> = await actorRequest("GET", {
|
|
137
140
|
[C6C.PAGINATION]: { [C6C.LIMIT]: 2 },
|
|
138
|
-
}
|
|
141
|
+
});
|
|
139
142
|
|
|
140
143
|
expect(Array.isArray(data.rest)).toBe(true);
|
|
141
144
|
expect(data.rest).toHaveLength(2);
|
|
@@ -148,7 +151,7 @@ describe("HttpExecutor singular e2e", () => {
|
|
|
148
151
|
});
|
|
149
152
|
|
|
150
153
|
it("exposes limit 1 does not expose next", async () => {
|
|
151
|
-
const data = await actorRequest("GET", {
|
|
154
|
+
const data: iGetC6RestResponse<iActor, {}> = await actorRequest("GET", {
|
|
152
155
|
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
153
156
|
} as any);
|
|
154
157
|
|
|
@@ -157,4 +160,40 @@ describe("HttpExecutor singular e2e", () => {
|
|
|
157
160
|
expect(typeof data.next).toBe("undefined");
|
|
158
161
|
|
|
159
162
|
});
|
|
163
|
+
|
|
164
|
+
it("skips reactBootstrap state sync when skipReactBootstrap is true", async () => {
|
|
165
|
+
const updateStub = vi.fn();
|
|
166
|
+
const reactBootstrap = {
|
|
167
|
+
updateRestfulObjectArrays: updateStub,
|
|
168
|
+
deleteRestfulObjectArrays: vi.fn(),
|
|
169
|
+
} as any;
|
|
170
|
+
|
|
171
|
+
const actorHttpWithReact = restOrm<any>(() => ({
|
|
172
|
+
C6,
|
|
173
|
+
restModel: TABLES.actor,
|
|
174
|
+
restURL,
|
|
175
|
+
axios: axiosClient,
|
|
176
|
+
verbose: false,
|
|
177
|
+
reactBootstrap,
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
const skipped = await actorHttpWithReact.Get({
|
|
181
|
+
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
182
|
+
skipReactBootstrap: true,
|
|
183
|
+
cacheResults: false,
|
|
184
|
+
} as any);
|
|
185
|
+
|
|
186
|
+
expect(Array.isArray(skipped.rest)).toBe(true);
|
|
187
|
+
expect(skipped.rest).toHaveLength(1);
|
|
188
|
+
expect(updateStub).not.toHaveBeenCalled();
|
|
189
|
+
|
|
190
|
+
const normal = await actorHttpWithReact.Get({
|
|
191
|
+
[C6C.PAGINATION]: { [C6C.LIMIT]: 1 },
|
|
192
|
+
cacheResults: false,
|
|
193
|
+
} as any);
|
|
194
|
+
|
|
195
|
+
expect(Array.isArray(normal.rest)).toBe(true);
|
|
196
|
+
expect(normal.rest).toHaveLength(1);
|
|
197
|
+
expect(updateStub).toHaveBeenCalledTimes(1);
|
|
198
|
+
});
|
|
160
199
|
});
|
|
@@ -33,13 +33,15 @@ function makeModel(table: string, pkShorts: string[], extraCols: string[] = []):
|
|
|
33
33
|
} as any;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
const litEq = (value: any) => [C6C.EQUAL, [C6C.LIT, value]];
|
|
37
|
+
|
|
36
38
|
describe('normalizeSingularRequest', () => {
|
|
37
39
|
it('converts GET singular T into WHERE by PK', () => {
|
|
38
40
|
const model = makeModel('actor', ['actor_id'], ['first_name']);
|
|
39
41
|
const req = { actor_id: 5 } as any;
|
|
40
42
|
const out = normalizeSingularRequest('GET', req, model);
|
|
41
43
|
expect(out).toHaveProperty(C6C.WHERE);
|
|
42
|
-
expect((out as any)[C6C.WHERE]).toEqual({ 'actor.actor_id': 5 });
|
|
44
|
+
expect((out as any)[C6C.WHERE]).toEqual({ 'actor.actor_id': litEq(5) });
|
|
43
45
|
});
|
|
44
46
|
|
|
45
47
|
it('converts DELETE singular T into DELETE:true and WHERE by PK', () => {
|
|
@@ -47,14 +49,14 @@ describe('normalizeSingularRequest', () => {
|
|
|
47
49
|
const req = { actor_id: 7 } as any;
|
|
48
50
|
const out = normalizeSingularRequest('DELETE', req, model);
|
|
49
51
|
expect((out as any)[C6C.DELETE]).toBe(true);
|
|
50
|
-
expect((out as any)[C6C.WHERE]).toEqual({ 'actor.actor_id': 7 });
|
|
52
|
+
expect((out as any)[C6C.WHERE]).toEqual({ 'actor.actor_id': litEq(7) });
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
it('converts PUT singular T into UPDATE (non-PK fields) and WHERE by PK', () => {
|
|
54
56
|
const model = makeModel('actor', ['actor_id'], ['first_name']);
|
|
55
57
|
const req = { actor_id: 9, first_name: 'NEW' } as any;
|
|
56
58
|
const out = normalizeSingularRequest('PUT', req, model);
|
|
57
|
-
expect((out as any)[C6C.WHERE]).toEqual({ 'actor.actor_id': 9 });
|
|
59
|
+
expect((out as any)[C6C.WHERE]).toEqual({ 'actor.actor_id': litEq(9) });
|
|
58
60
|
expect((out as any)[C6C.UPDATE]).toEqual({ first_name: 'NEW' });
|
|
59
61
|
});
|
|
60
62
|
|
|
@@ -76,7 +78,7 @@ describe('normalizeSingularRequest', () => {
|
|
|
76
78
|
const model = makeModel('link', ['from_id', 'to_id']);
|
|
77
79
|
const ok = { from_id: 1, to_id: 2 } as any;
|
|
78
80
|
const out = normalizeSingularRequest('GET', ok, model);
|
|
79
|
-
expect((out as any)[C6C.WHERE]).toEqual({ 'link.from_id': 1, 'link.to_id': 2 });
|
|
81
|
+
expect((out as any)[C6C.WHERE]).toEqual({ 'link.from_id': litEq(1), 'link.to_id': litEq(2) });
|
|
80
82
|
|
|
81
83
|
const missing = { from_id: 1 } as any;
|
|
82
84
|
expect(() => normalizeSingularRequest('DELETE', missing, model)).toThrow(/Missing: \[to_id\]/);
|
|
@@ -102,11 +104,27 @@ describe('normalizeSingularRequest', () => {
|
|
|
102
104
|
const out = normalizeSingularRequest('GET', req, model);
|
|
103
105
|
expect(out).toBe(req);
|
|
104
106
|
});
|
|
107
|
+
|
|
108
|
+
it('preserves skipReactBootstrap metadata across normalization', () => {
|
|
109
|
+
const model = makeModel('actor', ['actor_id'], ['first_name']);
|
|
110
|
+
const req = {
|
|
111
|
+
actor_id: 1,
|
|
112
|
+
first_name: 'S',
|
|
113
|
+
cacheResults: false,
|
|
114
|
+
skipReactBootstrap: true,
|
|
115
|
+
} as any;
|
|
116
|
+
|
|
117
|
+
const out = normalizeSingularRequest('PUT', req, model) as any;
|
|
118
|
+
expect(out.skipReactBootstrap).toBe(true);
|
|
119
|
+
expect(out.cacheResults).toBe(false);
|
|
120
|
+
expect(out[C6C.UPDATE]).toEqual({ first_name: 'S' });
|
|
121
|
+
});
|
|
122
|
+
|
|
105
123
|
it('accepts fully-qualified PK and maps WHERE/UPDATE to short keys', () => {
|
|
106
124
|
const model = makeModel('actor', ['actor_id'], ['first_name']);
|
|
107
125
|
const req = { 'actor.actor_id': 12, 'actor.first_name': 'FN' } as any;
|
|
108
126
|
const out = normalizeSingularRequest('PUT', req, model) as any;
|
|
109
|
-
expect(out[C6C.WHERE]).toEqual({ 'actor.actor_id': 12 });
|
|
127
|
+
expect(out[C6C.WHERE]).toEqual({ 'actor.actor_id': litEq(12) });
|
|
110
128
|
expect(out[C6C.UPDATE]).toEqual({ first_name: 'FN' });
|
|
111
129
|
});
|
|
112
130
|
|
|
@@ -114,7 +132,7 @@ describe('normalizeSingularRequest', () => {
|
|
|
114
132
|
const model = makeModel('actor', ['actor_id'], ['first_name']);
|
|
115
133
|
const req = { 'actor.actor_id': 44, first_name: 'Mix' } as any;
|
|
116
134
|
const out = normalizeSingularRequest('PUT', req, model) as any;
|
|
117
|
-
expect(out[C6C.WHERE]).toEqual({ 'actor.actor_id': 44 });
|
|
135
|
+
expect(out[C6C.WHERE]).toEqual({ 'actor.actor_id': litEq(44) });
|
|
118
136
|
expect(out[C6C.UPDATE]).toEqual({ first_name: 'Mix' });
|
|
119
137
|
});
|
|
120
138
|
|
|
@@ -123,13 +141,13 @@ describe('normalizeSingularRequest', () => {
|
|
|
123
141
|
const req = { 'actor.actor_id': 77 } as any;
|
|
124
142
|
const out = normalizeSingularRequest('DELETE', req, model) as any;
|
|
125
143
|
expect(out[C6C.DELETE]).toBe(true);
|
|
126
|
-
expect(out[C6C.WHERE]).toEqual({ 'actor.actor_id': 77 });
|
|
144
|
+
expect(out[C6C.WHERE]).toEqual({ 'actor.actor_id': litEq(77) });
|
|
127
145
|
});
|
|
128
146
|
|
|
129
147
|
it('supports composite PKs with fully-qualified keys', () => {
|
|
130
148
|
const model = makeModel('link', ['from_id', 'to_id']);
|
|
131
149
|
const req = { 'link.from_id': 1, 'link.to_id': 2, 'link.label': 'L' } as any;
|
|
132
150
|
const out = normalizeSingularRequest('PUT', req, model) as any;
|
|
133
|
-
expect(out[C6C.WHERE]).toEqual({ 'link.from_id': 1, 'link.to_id': 2 });
|
|
151
|
+
expect(out[C6C.WHERE]).toEqual({ 'link.from_id': litEq(1), 'link.to_id': litEq(2) });
|
|
134
152
|
});
|
|
135
153
|
});
|
|
@@ -1342,7 +1342,7 @@ export const TABLES = {
|
|
|
1342
1342
|
};
|
|
1343
1343
|
export const C6 = {
|
|
1344
1344
|
...C6Constants,
|
|
1345
|
-
C6VERSION: '6.0
|
|
1345
|
+
C6VERSION: '6.1.0',
|
|
1346
1346
|
IMPORT: async (tableName) => {
|
|
1347
1347
|
tableName = tableName.toLowerCase();
|
|
1348
1348
|
// if tableName is not a key in the TABLES object then throw an error
|