prostgles-server 4.2.323 → 4.2.324
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/DBSchemaBuilder.d.ts.map +1 -1
- package/dist/DBSchemaBuilder.js +10 -11
- package/dist/DBSchemaBuilder.js.map +1 -1
- package/dist/DboBuilder/DboBuilder.d.ts.map +1 -1
- package/dist/DboBuilder/DboBuilder.js +6 -4
- package/dist/DboBuilder/DboBuilder.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/getValidatedSubscribeOptions.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/getValidatedSubscribeOptions.js +1 -2
- package/dist/DboBuilder/ViewHandler/getValidatedSubscribeOptions.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/parseFieldFilter.js +3 -3
- package/dist/DboBuilder/ViewHandler/parseFieldFilter.js.map +1 -1
- package/dist/DboBuilder/ViewHandler/prepareSortItems.d.ts.map +1 -1
- package/dist/DboBuilder/ViewHandler/prepareSortItems.js +1 -2
- package/dist/DboBuilder/ViewHandler/prepareSortItems.js.map +1 -1
- package/dist/{JSONBValidation/validate_jsonb_schema_sql.d.ts → JSONBSchemaValidation/validateJSONBSchemaSQL.d.ts} +1 -1
- package/dist/JSONBSchemaValidation/validateJSONBSchemaSQL.d.ts.map +1 -0
- package/dist/{JSONBValidation/validate_jsonb_schema_sql.js → JSONBSchemaValidation/validateJSONBSchemaSQL.js} +1 -1
- package/dist/{JSONBValidation/validate_jsonb_schema_sql.js.map → JSONBSchemaValidation/validateJSONBSchemaSQL.js.map} +1 -1
- package/dist/TableConfig/getColumnDefinitionQuery.js +3 -3
- package/dist/TableConfig/getColumnDefinitionQuery.js.map +1 -1
- package/dist/TableConfig/getTableColumnQueries.js +2 -2
- package/dist/TableConfig/getTableColumnQueries.js.map +1 -1
- package/lib/DBSchemaBuilder.ts +10 -10
- package/lib/DboBuilder/DboBuilder.ts +3 -1
- package/lib/DboBuilder/ViewHandler/getValidatedSubscribeOptions.ts +6 -2
- package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +4 -4
- package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +8 -2
- package/lib/TableConfig/getColumnDefinitionQuery.ts +1 -1
- package/lib/TableConfig/getTableColumnQueries.ts +1 -1
- package/package.json +2 -3
- package/dist/JSONBValidation/JSONBValidation.d.ts +0 -18
- package/dist/JSONBValidation/JSONBValidation.d.ts.map +0 -1
- package/dist/JSONBValidation/JSONBValidation.js +0 -230
- package/dist/JSONBValidation/JSONBValidation.js.map +0 -1
- package/dist/JSONBValidation/JSONBValidation.spec.d.ts +0 -2
- package/dist/JSONBValidation/JSONBValidation.spec.d.ts.map +0 -1
- package/dist/JSONBValidation/JSONBValidation.spec.js +0 -68
- package/dist/JSONBValidation/JSONBValidation.spec.js.map +0 -1
- package/dist/JSONBValidation/getJSONBSchemaTSTypes.d.ts +0 -8
- package/dist/JSONBValidation/getJSONBSchemaTSTypes.d.ts.map +0 -1
- package/dist/JSONBValidation/getJSONBSchemaTSTypes.js +0 -120
- package/dist/JSONBValidation/getJSONBSchemaTSTypes.js.map +0 -1
- package/dist/JSONBValidation/validate_jsonb_schema_sql.d.ts.map +0 -1
- package/lib/JSONBValidation/JSONBValidation.spec.ts +0 -129
- package/lib/JSONBValidation/JSONBValidation.ts +0 -246
- package/lib/JSONBValidation/getJSONBSchemaTSTypes.ts +0 -139
- /package/lib/{JSONBValidation/validate_jsonb_schema_sql.ts → JSONBSchemaValidation/validateJSONBSchemaSQL.ts} +0 -0
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { test, describe } from "node:test";
|
|
2
|
-
import type { JSONB } from "prostgles-types";
|
|
3
|
-
import { strict as assert } from "assert";
|
|
4
|
-
import { getJSONBObjectSchemaValidationError } from "./JSONBValidation";
|
|
5
|
-
|
|
6
|
-
void describe("JSONBValidation", async () => {
|
|
7
|
-
await test("getJSONBObjectSchemaValidationError", () => {
|
|
8
|
-
const schema: JSONB.ObjectType = {
|
|
9
|
-
type: {
|
|
10
|
-
name: "string",
|
|
11
|
-
age: { type: "integer", nullable: true },
|
|
12
|
-
address: {
|
|
13
|
-
type: {
|
|
14
|
-
street: "string",
|
|
15
|
-
city: "string",
|
|
16
|
-
street_number: { type: "integer", optional: true },
|
|
17
|
-
t: { enum: ["a", "b", "c"], optional: true },
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
const obj = {
|
|
23
|
-
name: "John Doe",
|
|
24
|
-
age: 30,
|
|
25
|
-
address: {
|
|
26
|
-
street: "123 Main St",
|
|
27
|
-
city: "New York",
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
assert.deepStrictEqual(getJSONBObjectSchemaValidationError(schema.type, null, "test"), {
|
|
31
|
-
error: "Expecting test to be an object",
|
|
32
|
-
});
|
|
33
|
-
assert.deepStrictEqual(getJSONBObjectSchemaValidationError(schema.type, {}, "test"), {
|
|
34
|
-
error: "name is of invalid type. Expecting string",
|
|
35
|
-
});
|
|
36
|
-
assert.deepStrictEqual(getJSONBObjectSchemaValidationError(schema.type, obj, "test"), {
|
|
37
|
-
data: obj,
|
|
38
|
-
});
|
|
39
|
-
assert.deepStrictEqual(
|
|
40
|
-
getJSONBObjectSchemaValidationError(schema.type, { ...obj, age: null }, "test"),
|
|
41
|
-
{
|
|
42
|
-
data: { ...obj, age: null },
|
|
43
|
-
}
|
|
44
|
-
);
|
|
45
|
-
assert.deepStrictEqual(
|
|
46
|
-
getJSONBObjectSchemaValidationError(schema.type, { ...obj, age: 22.2 }, "test"),
|
|
47
|
-
{
|
|
48
|
-
error: "age is of invalid type. Expecting null | integer",
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
assert.deepStrictEqual(
|
|
52
|
-
getJSONBObjectSchemaValidationError(
|
|
53
|
-
schema.type,
|
|
54
|
-
{ ...obj, address: { ...obj.address, city: 22 } },
|
|
55
|
-
"test"
|
|
56
|
-
),
|
|
57
|
-
{
|
|
58
|
-
error: "address.city is of invalid type. Expecting string",
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
|
-
assert.deepStrictEqual(
|
|
62
|
-
getJSONBObjectSchemaValidationError(
|
|
63
|
-
schema.type,
|
|
64
|
-
{ ...obj, address: { ...obj.address, street_number: 22.22 } },
|
|
65
|
-
"test"
|
|
66
|
-
),
|
|
67
|
-
{ error: "address.street_number is of invalid type. Expecting undefined | integer" }
|
|
68
|
-
);
|
|
69
|
-
assert.deepStrictEqual(
|
|
70
|
-
getJSONBObjectSchemaValidationError(
|
|
71
|
-
schema.type,
|
|
72
|
-
{ ...obj, address: { ...obj.address, street_number: undefined } },
|
|
73
|
-
"test"
|
|
74
|
-
),
|
|
75
|
-
{ data: { ...obj, address: { ...obj.address, street_number: undefined } } }
|
|
76
|
-
);
|
|
77
|
-
assert.deepStrictEqual(
|
|
78
|
-
getJSONBObjectSchemaValidationError(
|
|
79
|
-
schema.type,
|
|
80
|
-
{ ...obj, address: { ...obj.address, t: "c" } },
|
|
81
|
-
"test"
|
|
82
|
-
),
|
|
83
|
-
{ data: { ...obj, address: { ...obj.address, t: "c" } } }
|
|
84
|
-
);
|
|
85
|
-
assert.deepStrictEqual(
|
|
86
|
-
getJSONBObjectSchemaValidationError(
|
|
87
|
-
schema.type,
|
|
88
|
-
{ ...obj, address: { ...obj.address, t: 2 } },
|
|
89
|
-
"test"
|
|
90
|
-
),
|
|
91
|
-
{ error: 'address.t is of invalid type. Expecting undefined | "a" | "b" | "c"' }
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
await test("getJSONBObjectSchemaValidationError oneOf record", () => {
|
|
95
|
-
assert.deepStrictEqual(
|
|
96
|
-
getJSONBObjectSchemaValidationError(
|
|
97
|
-
{
|
|
98
|
-
d: { record: { keysEnum: ["a", "b"], values: "boolean" } },
|
|
99
|
-
o: { optional: true, oneOf: ["number", "string[]"] },
|
|
100
|
-
},
|
|
101
|
-
{ d: { a: true, b: 1 } },
|
|
102
|
-
"test"
|
|
103
|
-
),
|
|
104
|
-
{ error: "d.b is of invalid type. Expecting boolean" }
|
|
105
|
-
);
|
|
106
|
-
assert.deepStrictEqual(
|
|
107
|
-
getJSONBObjectSchemaValidationError(
|
|
108
|
-
{
|
|
109
|
-
d: { record: { keysEnum: ["a", "b"], values: "boolean" } },
|
|
110
|
-
o: { optional: true, oneOf: ["number", "string[]"] },
|
|
111
|
-
},
|
|
112
|
-
{ d: { a: true, b: true }, o: false },
|
|
113
|
-
"test"
|
|
114
|
-
),
|
|
115
|
-
{ error: "o is of invalid type. Expecting undefined | number | string[]" }
|
|
116
|
-
);
|
|
117
|
-
assert.deepStrictEqual(
|
|
118
|
-
getJSONBObjectSchemaValidationError(
|
|
119
|
-
{
|
|
120
|
-
d: { record: { keysEnum: ["a", "b"], values: "boolean" } },
|
|
121
|
-
o: { optional: true, oneOf: ["number", "string[]"] },
|
|
122
|
-
},
|
|
123
|
-
{ d: { a: true, b: true }, o: ["str"] },
|
|
124
|
-
"test"
|
|
125
|
-
),
|
|
126
|
-
{ data: { d: { a: true, b: true }, o: ["str"] } }
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { getKeys, getObjectEntries, isDefined, isEmpty, isObject, JSONB } from "prostgles-types";
|
|
2
|
-
|
|
3
|
-
export const getFieldTypeObj = (rawFieldType: JSONB.FieldType): JSONB.FieldTypeObj => {
|
|
4
|
-
if (typeof rawFieldType === "string") return { type: rawFieldType };
|
|
5
|
-
|
|
6
|
-
return rawFieldType;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
type DataType = JSONB.FieldTypeObj["type"];
|
|
10
|
-
type ElementType<T extends DataType> = T extends `${infer E}[]` ? E : never;
|
|
11
|
-
type ArrayTypes = Extract<DataType, `${string}[]`>;
|
|
12
|
-
type NonArrayTypes = Extract<Exclude<DataType, ArrayTypes>, string>;
|
|
13
|
-
const PRIMITIVE_VALIDATORS: Record<NonArrayTypes, (val: any) => boolean> = {
|
|
14
|
-
string: (val) => typeof val === "string",
|
|
15
|
-
number: (val) => typeof val === "number" && Number.isFinite(val),
|
|
16
|
-
integer: (val) => typeof val === "number" && Number.isInteger(val),
|
|
17
|
-
boolean: (val) => typeof val === "boolean",
|
|
18
|
-
time: (val) => typeof val === "string",
|
|
19
|
-
timestamp: (val) => typeof val === "string",
|
|
20
|
-
any: (val) => typeof val !== "function" && typeof val !== "symbol",
|
|
21
|
-
Date: (val) => typeof val === "string",
|
|
22
|
-
Lookup: () => {
|
|
23
|
-
throw new Error("Lookup type is not supported for validation");
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
const PRIMITIVE_VALIDATORS_KEYS = getKeys(PRIMITIVE_VALIDATORS);
|
|
27
|
-
const getElementType = <T extends DataType>(type: T): undefined | ElementType<T> => {
|
|
28
|
-
if (typeof type === "string" && type.endsWith("[]")) {
|
|
29
|
-
const elementType = type.slice(0, -2);
|
|
30
|
-
if (!PRIMITIVE_VALIDATORS_KEYS.includes(elementType as NonArrayTypes)) {
|
|
31
|
-
throw new Error(`Unknown array field type ${type}`);
|
|
32
|
-
}
|
|
33
|
-
return elementType as ElementType<T>;
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const getValidator = (type: Extract<DataType, string>) => {
|
|
38
|
-
const elem = getElementType(type);
|
|
39
|
-
if (elem) {
|
|
40
|
-
const validator = PRIMITIVE_VALIDATORS[elem];
|
|
41
|
-
return {
|
|
42
|
-
isArray: true,
|
|
43
|
-
validator: (v: any) => Array.isArray(v) && v.every((v) => validator(v)),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
const validator = PRIMITIVE_VALIDATORS[type as NonArrayTypes];
|
|
47
|
-
if (!(validator as any)) {
|
|
48
|
-
throw new Error(`Unknown field type ${type}`);
|
|
49
|
-
}
|
|
50
|
-
return { isArray: false, validator };
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const getPropertyValidationError = (
|
|
54
|
-
value: any,
|
|
55
|
-
rawFieldType: JSONB.FieldType,
|
|
56
|
-
path: string[] = []
|
|
57
|
-
): string | undefined => {
|
|
58
|
-
const err = `${path.join(".")} is of invalid type. Expecting ${getTypeDescription(rawFieldType).replaceAll("\n", "")}`;
|
|
59
|
-
const fieldType = getFieldTypeObj(rawFieldType);
|
|
60
|
-
|
|
61
|
-
const { type, allowedValues, nullable, optional } = fieldType;
|
|
62
|
-
if (nullable && value === null) return;
|
|
63
|
-
if (optional && value === undefined) return;
|
|
64
|
-
if (allowedValues) {
|
|
65
|
-
throw new Error(`Allowed values are not supported for validation`);
|
|
66
|
-
}
|
|
67
|
-
if (type) {
|
|
68
|
-
if (isObject(type)) {
|
|
69
|
-
if (!isObject(value)) {
|
|
70
|
-
return err;
|
|
71
|
-
}
|
|
72
|
-
for (const [subKey, subSchema] of getObjectEntries(type)) {
|
|
73
|
-
const error = getPropertyValidationError(value[subKey], subSchema, [...path, subKey]);
|
|
74
|
-
if (error !== undefined) {
|
|
75
|
-
return error;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const { validator } = getValidator(type);
|
|
82
|
-
const isValid = validator(value);
|
|
83
|
-
if (!isValid) {
|
|
84
|
-
return err;
|
|
85
|
-
}
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (fieldType.enum) {
|
|
90
|
-
const otherOptions = [];
|
|
91
|
-
if (fieldType.nullable) otherOptions.push(null);
|
|
92
|
-
if (fieldType.optional) otherOptions.push(undefined);
|
|
93
|
-
// err += `one of: ${JSON.stringify([...fieldType.enum, ...otherOptions]).slice(1, -1)}`;
|
|
94
|
-
|
|
95
|
-
if (!fieldType.enum.includes(value)) return err;
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const arrayOf =
|
|
100
|
-
fieldType.arrayOf ?? (fieldType.arrayOfType ? { type: fieldType.arrayOfType } : undefined);
|
|
101
|
-
if (arrayOf) {
|
|
102
|
-
if (!Array.isArray(value)) {
|
|
103
|
-
return err + " to be an array";
|
|
104
|
-
}
|
|
105
|
-
const error = value
|
|
106
|
-
.map((element, i) => {
|
|
107
|
-
return getPropertyValidationError(element, arrayOf, [...path, `${i}`]);
|
|
108
|
-
})
|
|
109
|
-
.filter(isDefined)[0];
|
|
110
|
-
if (error !== undefined) {
|
|
111
|
-
return `${err}. Error at index ${path.length > 0 ? path.join(".") + "." : ""}\n\n${error}`;
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const oneOf = fieldType.oneOf ?? fieldType.oneOfType?.map((type) => ({ type }));
|
|
117
|
-
if (oneOf) {
|
|
118
|
-
if (!oneOf.length) {
|
|
119
|
-
return err + "to not be empty";
|
|
120
|
-
}
|
|
121
|
-
let firstError: string | undefined;
|
|
122
|
-
const validMember = oneOf.find((member) => {
|
|
123
|
-
const error = getPropertyValidationError(value, member, path);
|
|
124
|
-
firstError ??= error;
|
|
125
|
-
return error === undefined;
|
|
126
|
-
});
|
|
127
|
-
if (validMember) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
return err;
|
|
131
|
-
}
|
|
132
|
-
if (fieldType.record) {
|
|
133
|
-
const { keysEnum, partial, values: valuesSchema } = fieldType.record;
|
|
134
|
-
if (!isObject(value)) {
|
|
135
|
-
return err + "object";
|
|
136
|
-
}
|
|
137
|
-
if (partial && isEmpty(value)) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const valueKeys = getKeys(value);
|
|
141
|
-
const missingKey = partial ? undefined : keysEnum?.find((key) => !valueKeys.includes(key));
|
|
142
|
-
if (missingKey !== undefined) {
|
|
143
|
-
return `${err} to have key ${missingKey}`;
|
|
144
|
-
}
|
|
145
|
-
const extraKeys = keysEnum && valueKeys.filter((key) => !keysEnum.includes(key));
|
|
146
|
-
if (extraKeys?.length) {
|
|
147
|
-
return `${err} has extra keys: ${extraKeys}`;
|
|
148
|
-
}
|
|
149
|
-
if (valuesSchema) {
|
|
150
|
-
for (const [propKey, propValue] of Object.entries(value)) {
|
|
151
|
-
const valError = getPropertyValidationError(propValue, valuesSchema, [...path, propKey]);
|
|
152
|
-
if (valError !== undefined) {
|
|
153
|
-
return `${valError}`;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
return `Could not validate field type. Some logic might be missing: ${JSON.stringify(fieldType)}`;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const getTypeDescription = (schema: JSONB.FieldType): string => {
|
|
163
|
-
const schemaObj = getFieldTypeObj(schema);
|
|
164
|
-
const { type, nullable, optional, record } = schemaObj;
|
|
165
|
-
const oneOf = schemaObj.oneOf ?? schemaObj.oneOfType?.map((type) => ({ type }));
|
|
166
|
-
const allowedTypes: any[] = [];
|
|
167
|
-
if (nullable) allowedTypes.push("null");
|
|
168
|
-
if (optional) allowedTypes.push("undefined");
|
|
169
|
-
if (typeof type === "string") {
|
|
170
|
-
allowedTypes.push(type);
|
|
171
|
-
} else if (type) {
|
|
172
|
-
if (isObject(type)) {
|
|
173
|
-
const keyOpts: string[] = [];
|
|
174
|
-
Object.entries(type).forEach(([key, value]) => {
|
|
175
|
-
keyOpts.push(`${key}: ${getTypeDescription(value)}`);
|
|
176
|
-
});
|
|
177
|
-
allowedTypes.push(`{ ${keyOpts.join("; ")} }`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
schemaObj.enum?.forEach((v) => {
|
|
181
|
-
if (v === null) {
|
|
182
|
-
allowedTypes.push("null");
|
|
183
|
-
} else if (v === undefined) {
|
|
184
|
-
allowedTypes.push("undefined");
|
|
185
|
-
} else if (typeof v === "string") {
|
|
186
|
-
allowedTypes.push(JSON.stringify(v));
|
|
187
|
-
} else {
|
|
188
|
-
allowedTypes.push(v);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
oneOf?.forEach((v) => {
|
|
192
|
-
const type = getTypeDescription(v);
|
|
193
|
-
allowedTypes.push(type);
|
|
194
|
-
});
|
|
195
|
-
if (record) {
|
|
196
|
-
const { keysEnum, partial, values } = record;
|
|
197
|
-
const optional = partial ? "?" : "";
|
|
198
|
-
const valueType = !values ? "any" : getTypeDescription(values);
|
|
199
|
-
if (keysEnum) {
|
|
200
|
-
allowedTypes.push(`{ [${keysEnum.join(" | ")}]${optional}: ${valueType} }`);
|
|
201
|
-
} else {
|
|
202
|
-
allowedTypes.push(`{ [key: string]${optional}: ${valueType} }`);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return allowedTypes.join(" | ");
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
export const getJSONBObjectSchemaValidationError = <S extends JSONB.ObjectType["type"]>(
|
|
210
|
-
schema: S,
|
|
211
|
-
obj: any,
|
|
212
|
-
objName: string,
|
|
213
|
-
optional = false
|
|
214
|
-
): { error: string; data?: undefined } | { error?: undefined; data: JSONB.GetObjectType<S> } => {
|
|
215
|
-
if (obj === undefined && !optional) return { error: `Expecting ${objName} to be defined` };
|
|
216
|
-
if (!isObject(obj)) {
|
|
217
|
-
return { error: `Expecting ${objName} to be an object` };
|
|
218
|
-
}
|
|
219
|
-
for (const [k, objSchema] of Object.entries(schema)) {
|
|
220
|
-
const error = getPropertyValidationError(obj[k], objSchema, [k]);
|
|
221
|
-
if (error) {
|
|
222
|
-
return { error };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return { data: obj as JSONB.GetObjectType<S> };
|
|
226
|
-
};
|
|
227
|
-
export const validateJSONBObjectAgainstSchema = <S extends JSONB.ObjectType["type"]>(
|
|
228
|
-
schema: S,
|
|
229
|
-
obj: any,
|
|
230
|
-
objName: string,
|
|
231
|
-
optional = false
|
|
232
|
-
): obj is JSONB.GetObjectType<S> => {
|
|
233
|
-
const { error } = getJSONBObjectSchemaValidationError(schema, obj, objName, optional);
|
|
234
|
-
return error === undefined;
|
|
235
|
-
};
|
|
236
|
-
export const assertJSONBObjectAgainstSchema = <S extends JSONB.ObjectType["type"]>(
|
|
237
|
-
schema: S,
|
|
238
|
-
obj: any,
|
|
239
|
-
objName: string,
|
|
240
|
-
optional = false
|
|
241
|
-
): asserts obj is JSONB.GetObjectType<S> => {
|
|
242
|
-
const { error } = getJSONBObjectSchemaValidationError(schema, obj, objName, optional);
|
|
243
|
-
if (error) {
|
|
244
|
-
throw new Error(error);
|
|
245
|
-
}
|
|
246
|
-
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { getKeys, isObject, type JSONB, type TableSchema } from "prostgles-types";
|
|
2
|
-
import { postgresToTsType } from "../DboBuilder/DboBuilder";
|
|
3
|
-
import { asValue } from "../PubSubManager/PubSubManagerUtils";
|
|
4
|
-
import { getFieldTypeObj } from "./JSONBValidation";
|
|
5
|
-
|
|
6
|
-
type ColOpts = { nullable?: boolean };
|
|
7
|
-
|
|
8
|
-
export function getJSONBSchemaTSTypes(
|
|
9
|
-
schema: JSONB.JSONBSchema,
|
|
10
|
-
colOpts: ColOpts,
|
|
11
|
-
outerLeading = "",
|
|
12
|
-
tables: TableSchema[]
|
|
13
|
-
): string {
|
|
14
|
-
return getJSONBTSTypes(
|
|
15
|
-
tables,
|
|
16
|
-
{ ...(schema as JSONB.FieldTypeObj), nullable: colOpts.nullable },
|
|
17
|
-
undefined,
|
|
18
|
-
outerLeading
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const getJSONBTSTypes = (
|
|
23
|
-
tables: TableSchema[],
|
|
24
|
-
rawFieldType: JSONB.FieldType,
|
|
25
|
-
isOneOf = false,
|
|
26
|
-
innerLeading = "",
|
|
27
|
-
depth = 0
|
|
28
|
-
): string => {
|
|
29
|
-
const fieldType = getFieldTypeObj(rawFieldType);
|
|
30
|
-
const nullType = fieldType.nullable ? `null | ` : "";
|
|
31
|
-
if (fieldType.lookup) {
|
|
32
|
-
const l = fieldType.lookup;
|
|
33
|
-
if (l.type === "data-def") {
|
|
34
|
-
return `${fieldType.nullable ? `null |` : ""} ${getJSONBTSTypes(tables, {
|
|
35
|
-
type: {
|
|
36
|
-
table: "string",
|
|
37
|
-
column: "string",
|
|
38
|
-
filter: { record: {}, optional: true },
|
|
39
|
-
isArray: { type: "boolean", optional: true },
|
|
40
|
-
searchColumns: { type: "string[]", optional: true },
|
|
41
|
-
isFullRow: {
|
|
42
|
-
optional: true,
|
|
43
|
-
type: {
|
|
44
|
-
displayColumns: { type: "string[]", optional: true },
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
showInRowCard: { optional: true, record: {} },
|
|
48
|
-
},
|
|
49
|
-
})}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const isSChema = l.type === "schema";
|
|
53
|
-
let type =
|
|
54
|
-
isSChema ?
|
|
55
|
-
l.object === "table" ?
|
|
56
|
-
"string"
|
|
57
|
-
: `{ "table": string; "column": string; }`
|
|
58
|
-
: "";
|
|
59
|
-
if (!isSChema) {
|
|
60
|
-
const cols = tables.find((t) => t.name === l.table)?.columns;
|
|
61
|
-
if (!l.isFullRow) {
|
|
62
|
-
type = postgresToTsType(cols?.find((c) => c.name === l.column)?.udt_name ?? "text");
|
|
63
|
-
} else {
|
|
64
|
-
type =
|
|
65
|
-
!cols ? "any" : (
|
|
66
|
-
`{ ${cols.map((c) => `${JSON.stringify(c.name)}: ${c.is_nullable ? "null | " : ""} ${postgresToTsType(c.udt_name)}; `).join(" ")} }`
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return `${fieldType.nullable ? `null | ` : ""}${type}${l.isArray ? "[]" : ""}`;
|
|
71
|
-
} else if (typeof fieldType.type === "string") {
|
|
72
|
-
if (fieldType.type.toLowerCase().includes("lookup")) {
|
|
73
|
-
throw new Error(`getJSONBTSTypes: Lookup type not handled correctly`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Primitives */
|
|
77
|
-
const correctType = fieldType.type
|
|
78
|
-
.replace("integer", "number")
|
|
79
|
-
.replace("time", "string")
|
|
80
|
-
.replace("timestamp", "string")
|
|
81
|
-
.replace("Date", "string");
|
|
82
|
-
|
|
83
|
-
if (fieldType.allowedValues && fieldType.type.endsWith("[]")) {
|
|
84
|
-
return nullType + ` (${fieldType.allowedValues.map((v) => JSON.stringify(v)).join(" | ")})[]`;
|
|
85
|
-
}
|
|
86
|
-
return nullType + correctType;
|
|
87
|
-
|
|
88
|
-
/** Object */
|
|
89
|
-
} else if (isObject(fieldType.type)) {
|
|
90
|
-
const addSemicolonIfMissing = (v: string) => (v.trim().endsWith(";") ? v : v.trim() + ";");
|
|
91
|
-
const { type } = fieldType;
|
|
92
|
-
const spacing = isOneOf ? " " : " ";
|
|
93
|
-
let objDef =
|
|
94
|
-
` {${spacing}` +
|
|
95
|
-
getKeys(type)
|
|
96
|
-
.map((key) => {
|
|
97
|
-
const fieldType = getFieldTypeObj(type[key]!);
|
|
98
|
-
const escapedKey = isValidIdentifier(key) ? key : JSON.stringify(key);
|
|
99
|
-
return (
|
|
100
|
-
`${spacing}${escapedKey}${fieldType.optional ? "?" : ""}: ` +
|
|
101
|
-
addSemicolonIfMissing(getJSONBTSTypes(tables, fieldType, true, undefined, depth + 1))
|
|
102
|
-
);
|
|
103
|
-
})
|
|
104
|
-
.join(" ") +
|
|
105
|
-
`${spacing}}`;
|
|
106
|
-
if (!isOneOf) {
|
|
107
|
-
objDef = addSemicolonIfMissing(objDef);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Keep single line */
|
|
111
|
-
if (isOneOf) {
|
|
112
|
-
objDef = objDef.split("\n").join("");
|
|
113
|
-
}
|
|
114
|
-
return nullType + objDef;
|
|
115
|
-
} else if (fieldType.enum) {
|
|
116
|
-
return nullType + fieldType.enum.map((v) => asValue(v)).join(" | ");
|
|
117
|
-
} else if (fieldType.oneOf || fieldType.oneOfType) {
|
|
118
|
-
const oneOf = fieldType.oneOf || fieldType.oneOfType.map((type) => ({ type }));
|
|
119
|
-
return (
|
|
120
|
-
(fieldType.nullable ? `\n${innerLeading} | null` : "") +
|
|
121
|
-
oneOf
|
|
122
|
-
.map((v) => `\n${innerLeading} | ` + getJSONBTSTypes(tables, v, true, undefined, depth + 1))
|
|
123
|
-
.join("")
|
|
124
|
-
);
|
|
125
|
-
} else if (fieldType.arrayOf || fieldType.arrayOfType) {
|
|
126
|
-
const arrayOf = fieldType.arrayOf || { type: fieldType.arrayOfType };
|
|
127
|
-
return `${fieldType.nullable ? `null | ` : ""} ( ${getJSONBTSTypes(tables, arrayOf, true, undefined, depth + 1)} )[]`;
|
|
128
|
-
} else if (fieldType.record) {
|
|
129
|
-
const { keysEnum, values, partial } = fieldType.record;
|
|
130
|
-
// TODO: ensure props with undefined values are not allowed in the TS type (strict union)
|
|
131
|
-
const getRecord = (v: string) => (partial ? `Partial<Record<${v}>>` : `Record<${v}>`);
|
|
132
|
-
return `${fieldType.nullable ? `null |` : ""} ${getRecord(`${keysEnum?.map((v) => asValue(v)).join(" | ") ?? "string"}, ${!values ? "any" : getJSONBTSTypes(tables, values, true, undefined, depth + 1)}`)}`;
|
|
133
|
-
} else throw "Unexpected getSchemaTSTypes: " + JSON.stringify({ fieldType }, null, 2);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const isValidIdentifier = (str: string) => {
|
|
137
|
-
const identifierRegex = /^[A-Za-z$_][A-Za-z0-9$_]*$/;
|
|
138
|
-
return identifierRegex.test(str);
|
|
139
|
-
};
|
|
File without changes
|