metal-orm 1.0.79 → 1.0.81
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/index.cjs +795 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -2
- package/dist/index.d.ts +174 -2
- package/dist/index.js +788 -5
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
- package/src/core/dialect/postgres/index.ts +9 -5
- package/src/core/execution/db-executor.ts +5 -4
- package/src/core/execution/executors/mysql-executor.ts +9 -7
- package/src/index.ts +6 -4
- package/src/openapi/index.ts +4 -0
- package/src/openapi/query-parameters.ts +206 -0
- package/src/openapi/schema-extractor.ts +586 -0
- package/src/openapi/schema-types.ts +158 -0
- package/src/openapi/type-mappers.ts +227 -0
- package/src/orm/unit-of-work.ts +25 -13
- package/src/query-builder/select.ts +35 -11
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAPI 3.1 JSON Schema type representation
|
|
3
|
+
*/
|
|
4
|
+
export type JsonSchemaType =
|
|
5
|
+
| 'string'
|
|
6
|
+
| 'number'
|
|
7
|
+
| 'integer'
|
|
8
|
+
| 'boolean'
|
|
9
|
+
| 'object'
|
|
10
|
+
| 'array'
|
|
11
|
+
| 'null';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Common OpenAPI 3.1 JSON Schema formats
|
|
15
|
+
*/
|
|
16
|
+
export type JsonSchemaFormat =
|
|
17
|
+
| 'date-time'
|
|
18
|
+
| 'date'
|
|
19
|
+
| 'time'
|
|
20
|
+
| 'email'
|
|
21
|
+
| 'uuid'
|
|
22
|
+
| 'uri'
|
|
23
|
+
| 'binary'
|
|
24
|
+
| 'base64';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* OpenAPI 3.1 JSON Schema property definition
|
|
28
|
+
*/
|
|
29
|
+
export interface JsonSchemaProperty {
|
|
30
|
+
type?: JsonSchemaType | JsonSchemaType[];
|
|
31
|
+
format?: JsonSchemaFormat;
|
|
32
|
+
description?: string;
|
|
33
|
+
nullable?: boolean;
|
|
34
|
+
readOnly?: boolean;
|
|
35
|
+
writeOnly?: boolean;
|
|
36
|
+
minimum?: number;
|
|
37
|
+
maximum?: number;
|
|
38
|
+
minLength?: number;
|
|
39
|
+
maxLength?: number;
|
|
40
|
+
pattern?: string;
|
|
41
|
+
enum?: (string | number | boolean)[];
|
|
42
|
+
default?: unknown;
|
|
43
|
+
example?: unknown;
|
|
44
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
45
|
+
required?: string[];
|
|
46
|
+
items?: JsonSchemaProperty;
|
|
47
|
+
$ref?: string;
|
|
48
|
+
anyOf?: JsonSchemaProperty[];
|
|
49
|
+
allOf?: JsonSchemaProperty[];
|
|
50
|
+
oneOf?: JsonSchemaProperty[];
|
|
51
|
+
[key: string]: unknown;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* OpenAPI 3.1 parameter definition
|
|
56
|
+
*/
|
|
57
|
+
export interface OpenApiParameter {
|
|
58
|
+
name: string;
|
|
59
|
+
in: 'query' | 'path' | 'header' | 'cookie';
|
|
60
|
+
description?: string;
|
|
61
|
+
required?: boolean;
|
|
62
|
+
deprecated?: boolean;
|
|
63
|
+
allowEmptyValue?: boolean;
|
|
64
|
+
style?: string;
|
|
65
|
+
explode?: boolean;
|
|
66
|
+
schema?: JsonSchemaProperty;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Complete OpenAPI 3.1 Schema for an entity or query result
|
|
72
|
+
*/
|
|
73
|
+
export interface OpenApiSchema {
|
|
74
|
+
type: 'object';
|
|
75
|
+
properties: Record<string, JsonSchemaProperty>;
|
|
76
|
+
required: string[];
|
|
77
|
+
description?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Column-level schema flags
|
|
82
|
+
*/
|
|
83
|
+
export interface ColumnSchemaOptions {
|
|
84
|
+
/** Include description from column comments */
|
|
85
|
+
includeDescriptions?: boolean;
|
|
86
|
+
/** Include enum values for enum columns */
|
|
87
|
+
includeEnums?: boolean;
|
|
88
|
+
/** Include column examples if available */
|
|
89
|
+
includeExamples?: boolean;
|
|
90
|
+
/** Include column defaults */
|
|
91
|
+
includeDefaults?: boolean;
|
|
92
|
+
/** Include nullable flag when applicable */
|
|
93
|
+
includeNullable?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Output schema generation options (query result)
|
|
98
|
+
*/
|
|
99
|
+
export interface OutputSchemaOptions extends ColumnSchemaOptions {
|
|
100
|
+
/** Use selected columns only (from select/include) vs full entity */
|
|
101
|
+
mode?: 'selected' | 'full';
|
|
102
|
+
/** Maximum depth for relation recursion */
|
|
103
|
+
maxDepth?: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type InputRelationMode = 'ids' | 'objects' | 'mixed';
|
|
107
|
+
export type InputSchemaMode = 'create' | 'update';
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Input schema generation options (write payloads)
|
|
111
|
+
*/
|
|
112
|
+
export interface InputSchemaOptions extends ColumnSchemaOptions {
|
|
113
|
+
/** Create vs update payload shape */
|
|
114
|
+
mode?: InputSchemaMode;
|
|
115
|
+
/** Include relation payloads */
|
|
116
|
+
includeRelations?: boolean;
|
|
117
|
+
/** How relations are represented (ids, nested objects, or both) */
|
|
118
|
+
relationMode?: InputRelationMode;
|
|
119
|
+
/** Maximum depth for relation recursion */
|
|
120
|
+
maxDepth?: number;
|
|
121
|
+
/** Omit read-only/generated columns from input */
|
|
122
|
+
omitReadOnly?: boolean;
|
|
123
|
+
/** Exclude primary key columns from input */
|
|
124
|
+
excludePrimaryKey?: boolean;
|
|
125
|
+
/** Require primary key columns on update payloads */
|
|
126
|
+
requirePrimaryKey?: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Schema generation options
|
|
131
|
+
*/
|
|
132
|
+
export interface SchemaOptions extends OutputSchemaOptions {
|
|
133
|
+
/** Input schema options, or false to skip input generation */
|
|
134
|
+
input?: InputSchemaOptions | false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Input + output schema bundle
|
|
139
|
+
*/
|
|
140
|
+
export interface OpenApiSchemaBundle {
|
|
141
|
+
output: OpenApiSchema;
|
|
142
|
+
input?: OpenApiSchema;
|
|
143
|
+
parameters?: OpenApiParameter[];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Schema extraction context for handling circular references
|
|
148
|
+
*/
|
|
149
|
+
export interface SchemaExtractionContext {
|
|
150
|
+
/** Set of already visited tables to detect cycles */
|
|
151
|
+
visitedTables: Set<string>;
|
|
152
|
+
/** Map of table names to their generated schemas */
|
|
153
|
+
schemaCache: Map<string, OpenApiSchema>;
|
|
154
|
+
/** Current extraction depth */
|
|
155
|
+
depth: number;
|
|
156
|
+
/** Maximum depth to recurse */
|
|
157
|
+
maxDepth: number;
|
|
158
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import type { ColumnDef } from '../schema/column-types.js';
|
|
2
|
+
import type { ColumnSchemaOptions, JsonSchemaProperty, JsonSchemaType, JsonSchemaFormat } from './schema-types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Maps SQL column types to OpenAPI JSON Schema types
|
|
6
|
+
*/
|
|
7
|
+
export const mapColumnType = (
|
|
8
|
+
column: ColumnDef,
|
|
9
|
+
options: ColumnSchemaOptions = {}
|
|
10
|
+
): JsonSchemaProperty => {
|
|
11
|
+
const resolved = resolveColumnOptions(options);
|
|
12
|
+
const sqlType = normalizeType(column.type);
|
|
13
|
+
const baseSchema = mapSqlTypeToBaseSchema(sqlType, column);
|
|
14
|
+
|
|
15
|
+
const schema: JsonSchemaProperty = {
|
|
16
|
+
...baseSchema,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (resolved.includeDescriptions && column.comment) {
|
|
20
|
+
schema.description = column.comment;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (resolved.includeNullable) {
|
|
24
|
+
schema.nullable = !column.notNull && !column.primary;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if ((sqlType === 'varchar' || sqlType === 'char') && column.args) {
|
|
28
|
+
schema.maxLength = column.args[0] as number | undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if ((sqlType === 'decimal' || sqlType === 'float') && column.args) {
|
|
32
|
+
if (column.args.length >= 1) {
|
|
33
|
+
schema.minimum = -(10 ** (column.args[0] as number));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!resolved.includeEnums) {
|
|
38
|
+
delete schema.enum;
|
|
39
|
+
} else if (sqlType === 'enum' && column.args && column.args.length > 0) {
|
|
40
|
+
schema.enum = column.args as (string | number | boolean)[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (resolved.includeDefaults && column.default !== undefined) {
|
|
44
|
+
schema.default = column.default;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return schema;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const normalizeType = (type: string): string => {
|
|
51
|
+
return type.toLowerCase();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const mapSqlTypeToBaseSchema = (
|
|
55
|
+
sqlType: string,
|
|
56
|
+
column: ColumnDef
|
|
57
|
+
): Omit<JsonSchemaProperty, 'nullable' | 'description'> => {
|
|
58
|
+
const type = normalizeType(sqlType);
|
|
59
|
+
|
|
60
|
+
const hasCustomTsType = column.tsType !== undefined;
|
|
61
|
+
|
|
62
|
+
switch (type) {
|
|
63
|
+
case 'int':
|
|
64
|
+
case 'integer':
|
|
65
|
+
case 'bigint':
|
|
66
|
+
return {
|
|
67
|
+
type: hasCustomTsType ? inferTypeFromTsType(column.tsType) : ('integer' as JsonSchemaType),
|
|
68
|
+
format: type === 'bigint' ? 'int64' : 'int32',
|
|
69
|
+
minimum: column.autoIncrement ? 1 : undefined,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
case 'decimal':
|
|
73
|
+
case 'float':
|
|
74
|
+
case 'double':
|
|
75
|
+
return {
|
|
76
|
+
type: hasCustomTsType ? inferTypeFromTsType(column.tsType) : ('number' as JsonSchemaType),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
case 'varchar':
|
|
80
|
+
return {
|
|
81
|
+
type: 'string' as JsonSchemaType,
|
|
82
|
+
minLength: column.notNull ? 1 : undefined,
|
|
83
|
+
maxLength: column.args?.[0] as number | undefined,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
case 'text':
|
|
87
|
+
return {
|
|
88
|
+
type: 'string' as JsonSchemaType,
|
|
89
|
+
minLength: column.notNull ? 1 : undefined,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
case 'char':
|
|
93
|
+
return {
|
|
94
|
+
type: 'string' as JsonSchemaType,
|
|
95
|
+
minLength: column.notNull ? column.args?.[0] as number || 1 : undefined,
|
|
96
|
+
maxLength: column.args?.[0] as number,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
case 'boolean':
|
|
100
|
+
return {
|
|
101
|
+
type: 'boolean' as JsonSchemaType,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
case 'json':
|
|
105
|
+
return {
|
|
106
|
+
anyOf: [
|
|
107
|
+
{ type: 'object' as JsonSchemaType },
|
|
108
|
+
{ type: 'array' as JsonSchemaType },
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
case 'blob':
|
|
113
|
+
case 'binary':
|
|
114
|
+
case 'varbinary':
|
|
115
|
+
return {
|
|
116
|
+
type: 'string' as JsonSchemaType,
|
|
117
|
+
format: 'base64' as JsonSchemaFormat,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
case 'date':
|
|
121
|
+
return {
|
|
122
|
+
type: 'string' as JsonSchemaType,
|
|
123
|
+
format: 'date' as JsonSchemaFormat,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
case 'datetime':
|
|
127
|
+
case 'timestamp':
|
|
128
|
+
return {
|
|
129
|
+
type: 'string' as JsonSchemaType,
|
|
130
|
+
format: 'date-time' as JsonSchemaFormat,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
case 'timestamptz':
|
|
134
|
+
return {
|
|
135
|
+
type: 'string' as JsonSchemaType,
|
|
136
|
+
format: 'date-time' as JsonSchemaFormat,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
case 'uuid':
|
|
140
|
+
return {
|
|
141
|
+
type: 'string' as JsonSchemaType,
|
|
142
|
+
format: 'uuid' as JsonSchemaFormat,
|
|
143
|
+
pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
case 'enum':
|
|
147
|
+
return {
|
|
148
|
+
type: 'string' as JsonSchemaType,
|
|
149
|
+
enum: (column.args as (string | number | boolean)[]) || [],
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
default:
|
|
153
|
+
if (column.dialectTypes?.postgres && column.dialectTypes.postgres === 'bytea') {
|
|
154
|
+
return {
|
|
155
|
+
type: 'string' as JsonSchemaType,
|
|
156
|
+
format: 'base64' as JsonSchemaFormat,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
type: 'string' as JsonSchemaType,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const inferTypeFromTsType = (tsType: unknown): JsonSchemaType => {
|
|
167
|
+
if (typeof tsType === 'string') {
|
|
168
|
+
if (tsType === 'number') return 'number' as JsonSchemaType;
|
|
169
|
+
if (tsType === 'string') return 'string' as JsonSchemaType;
|
|
170
|
+
if (tsType === 'boolean') return 'boolean' as JsonSchemaType;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (typeof tsType === 'function') {
|
|
174
|
+
const typeStr = tsType.name?.toLowerCase();
|
|
175
|
+
if (typeStr === 'number') return 'number' as JsonSchemaType;
|
|
176
|
+
if (typeStr === 'string') return 'string' as JsonSchemaType;
|
|
177
|
+
if (typeStr === 'boolean') return 'boolean' as JsonSchemaType;
|
|
178
|
+
if (typeStr === 'array') return 'array' as JsonSchemaType;
|
|
179
|
+
if (typeStr === 'object') return 'object' as JsonSchemaType;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return 'string' as JsonSchemaType;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const resolveColumnOptions = (options: ColumnSchemaOptions): Required<ColumnSchemaOptions> => ({
|
|
186
|
+
includeDescriptions: options.includeDescriptions ?? false,
|
|
187
|
+
includeEnums: options.includeEnums ?? true,
|
|
188
|
+
includeExamples: options.includeExamples ?? false,
|
|
189
|
+
includeDefaults: options.includeDefaults ?? true,
|
|
190
|
+
includeNullable: options.includeNullable ?? true
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Maps relation type to array or single object
|
|
195
|
+
*/
|
|
196
|
+
export const mapRelationType = (
|
|
197
|
+
relationType: string
|
|
198
|
+
): { type: 'object' | 'array'; isNullable: boolean } => {
|
|
199
|
+
switch (relationType) {
|
|
200
|
+
case 'HAS_MANY':
|
|
201
|
+
case 'BELONGS_TO_MANY':
|
|
202
|
+
return { type: 'array', isNullable: false };
|
|
203
|
+
case 'HAS_ONE':
|
|
204
|
+
case 'BELONGS_TO':
|
|
205
|
+
return { type: 'object', isNullable: true };
|
|
206
|
+
default:
|
|
207
|
+
return { type: 'object', isNullable: true };
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Gets the OpenAPI format for temporal columns
|
|
213
|
+
*/
|
|
214
|
+
export const getTemporalFormat = (sqlType: string): JsonSchemaFormat | undefined => {
|
|
215
|
+
const type = normalizeType(sqlType);
|
|
216
|
+
|
|
217
|
+
switch (type) {
|
|
218
|
+
case 'date':
|
|
219
|
+
return 'date' as JsonSchemaFormat;
|
|
220
|
+
case 'datetime':
|
|
221
|
+
case 'timestamp':
|
|
222
|
+
case 'timestamptz':
|
|
223
|
+
return 'date-time' as JsonSchemaFormat;
|
|
224
|
+
default:
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
};
|
package/src/orm/unit-of-work.ts
CHANGED
|
@@ -198,12 +198,13 @@ export class UnitOfWork {
|
|
|
198
198
|
|
|
199
199
|
const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
|
|
200
200
|
let builder = new InsertQueryBuilder(tracked.table).values(payload as Record<string, ValueOperandInput>);
|
|
201
|
-
if (this.dialect.supportsReturning()) {
|
|
202
|
-
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
203
|
-
}
|
|
204
|
-
const compiled = builder.compile(this.dialect);
|
|
205
|
-
const results = await this.executeCompiled(compiled);
|
|
206
|
-
this.applyReturningResults(tracked, results);
|
|
201
|
+
if (this.dialect.supportsReturning()) {
|
|
202
|
+
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
203
|
+
}
|
|
204
|
+
const compiled = builder.compile(this.dialect);
|
|
205
|
+
const results = await this.executeCompiled(compiled);
|
|
206
|
+
this.applyReturningResults(tracked, results);
|
|
207
|
+
this.applyInsertId(tracked, results);
|
|
207
208
|
|
|
208
209
|
tracked.status = EntityStatus.Managed;
|
|
209
210
|
tracked.original = this.createSnapshot(tracked.table, tracked.entity as Record<string, unknown>);
|
|
@@ -344,18 +345,29 @@ export class UnitOfWork {
|
|
|
344
345
|
* @param tracked - The tracked entity
|
|
345
346
|
* @param results - Query results
|
|
346
347
|
*/
|
|
347
|
-
private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
|
|
348
|
-
if (!this.dialect.supportsReturning()) return;
|
|
349
|
-
const first = results[0];
|
|
350
|
-
if (!first || first.values.length === 0) return;
|
|
348
|
+
private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
|
|
349
|
+
if (!this.dialect.supportsReturning()) return;
|
|
350
|
+
const first = results[0];
|
|
351
|
+
if (!first || first.values.length === 0) return;
|
|
351
352
|
|
|
352
353
|
const row = first.values[0];
|
|
353
354
|
for (let i = 0; i < first.columns.length; i++) {
|
|
354
355
|
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
355
356
|
if (!(columnName in tracked.table.columns)) continue;
|
|
356
|
-
(tracked.entity as Record<string, unknown>)[columnName] = row[i];
|
|
357
|
-
}
|
|
358
|
-
}
|
|
357
|
+
(tracked.entity as Record<string, unknown>)[columnName] = row[i];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private applyInsertId(tracked: TrackedEntity, results: QueryResult[]): void {
|
|
362
|
+
if (this.dialect.supportsReturning()) return;
|
|
363
|
+
if (tracked.pk != null) return;
|
|
364
|
+
const pkName = findPrimaryKey(tracked.table);
|
|
365
|
+
const pkColumn = tracked.table.columns[pkName];
|
|
366
|
+
if (!pkColumn?.autoIncrement) return;
|
|
367
|
+
const insertId = results.find(result => typeof result.insertId === 'number')?.insertId;
|
|
368
|
+
if (insertId == null) return;
|
|
369
|
+
(tracked.entity as Record<string, unknown>)[pkName] = insertId;
|
|
370
|
+
}
|
|
359
371
|
|
|
360
372
|
/**
|
|
361
373
|
* Normalizes a column name by removing quotes and table prefixes.
|
|
@@ -66,7 +66,8 @@ import { SelectProjectionFacet } from './select/projection-facet.js';
|
|
|
66
66
|
import { SelectPredicateFacet } from './select/predicate-facet.js';
|
|
67
67
|
import { SelectCTEFacet } from './select/cte-facet.js';
|
|
68
68
|
import { SelectSetOpFacet } from './select/setop-facet.js';
|
|
69
|
-
import { SelectRelationFacet } from './select/relation-facet.js';
|
|
69
|
+
import { SelectRelationFacet } from './select/relation-facet.js';
|
|
70
|
+
import { buildFilterParameters, extractSchema, SchemaOptions, OpenApiSchemaBundle } from '../openapi/index.js';
|
|
70
71
|
|
|
71
72
|
type ColumnSelectionValue =
|
|
72
73
|
| ColumnDef
|
|
@@ -1108,16 +1109,39 @@ export class SelectQueryBuilder<T = EntityInstance<TableDef>, TTable extends Tab
|
|
|
1108
1109
|
return this.compile(dialect).sql;
|
|
1109
1110
|
}
|
|
1110
1111
|
|
|
1111
|
-
/**
|
|
1112
|
-
* Gets
|
|
1113
|
-
* @returns Hydration plan or undefined if none exists
|
|
1114
|
-
* @example
|
|
1115
|
-
* const plan = qb.include('posts').getHydrationPlan();
|
|
1116
|
-
* console.log(plan?.relations); // Information about included relations
|
|
1117
|
-
*/
|
|
1118
|
-
getHydrationPlan(): HydrationPlan | undefined {
|
|
1119
|
-
return this.context.hydration.getPlan();
|
|
1120
|
-
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Gets hydration plan for query
|
|
1114
|
+
* @returns Hydration plan or undefined if none exists
|
|
1115
|
+
* @example
|
|
1116
|
+
* const plan = qb.include('posts').getHydrationPlan();
|
|
1117
|
+
* console.log(plan?.relations); // Information about included relations
|
|
1118
|
+
*/
|
|
1119
|
+
getHydrationPlan(): HydrationPlan | undefined {
|
|
1120
|
+
return this.context.hydration.getPlan();
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Gets OpenAPI 3.1 JSON Schemas for query output and optional input payloads
|
|
1125
|
+
* @param options - Schema generation options
|
|
1126
|
+
* @returns OpenAPI 3.1 JSON Schemas for query output and input payloads
|
|
1127
|
+
* @example
|
|
1128
|
+
* const { output } = qb.select('id', 'title', 'author').getSchema();
|
|
1129
|
+
* console.log(JSON.stringify(output, null, 2));
|
|
1130
|
+
*/
|
|
1131
|
+
getSchema(options?: SchemaOptions): OpenApiSchemaBundle {
|
|
1132
|
+
const plan = this.context.hydration.getPlan();
|
|
1133
|
+
const bundle = extractSchema(this.env.table, plan, this.context.state.ast.columns, options);
|
|
1134
|
+
const parameters = buildFilterParameters(
|
|
1135
|
+
this.env.table,
|
|
1136
|
+
this.context.state.ast.where,
|
|
1137
|
+
this.context.state.ast.from,
|
|
1138
|
+
options ?? {}
|
|
1139
|
+
);
|
|
1140
|
+
if (parameters.length) {
|
|
1141
|
+
return { ...bundle, parameters };
|
|
1142
|
+
}
|
|
1143
|
+
return bundle;
|
|
1144
|
+
}
|
|
1121
1145
|
|
|
1122
1146
|
/**
|
|
1123
1147
|
* Gets the Abstract Syntax Tree (AST) representation of the query
|