fork of hey-api/openapi-ts because I need some additional things
1import type { SymbolMeta } from '@hey-api/codegen-core';
2import { fromRef, ref } from '@hey-api/codegen-core';
3import type { IR, SchemaWithType } from '@hey-api/shared';
4import { deduplicateSchema, pathToJsonPointer } from '@hey-api/shared';
5
6// import { $ } from '../../../py-dsl';
7import type { Ast, IrSchemaToAstOptions } from '../shared/types';
8import type { PydanticPlugin } from '../types';
9import { createProcessor } from './processor';
10import { irSchemaWithTypeToAst } from './toAst';
11
12export function irSchemaToAst({
13 optional,
14 plugin,
15 schema,
16 schemaExtractor,
17 state,
18}: IrSchemaToAstOptions & {
19 optional?: boolean;
20 schema: IR.SchemaObject;
21}): Ast {
22 if (schemaExtractor && !schema.$ref) {
23 const extracted = schemaExtractor({
24 meta: {
25 resource: 'definition',
26 resourceId: pathToJsonPointer(fromRef(state.path)),
27 },
28 naming: plugin.config.definitions,
29 path: fromRef(state.path),
30 plugin,
31 schema,
32 });
33 if (extracted !== schema) schema = extracted;
34 }
35
36 if (schema.$ref) {
37 const query: SymbolMeta = {
38 category: 'schema',
39 resource: 'definition',
40 resourceId: schema.$ref,
41 tool: 'pydantic',
42 };
43 const refSymbol = plugin.referenceSymbol(query);
44 const refName = typeof refSymbol === 'string' ? refSymbol : refSymbol.name;
45
46 return {
47 // expression: $.expr(refName),
48 fieldConstraints: optional ? { default: null } : undefined,
49 hasLazyExpression: !plugin.isSymbolRegistered(query),
50 models: [],
51 // pipes: [],
52 typeAnnotation: refName,
53 };
54 }
55
56 if (schema.type) {
57 const typeAst = irSchemaWithTypeToAst({
58 plugin,
59 schema: schema as SchemaWithType,
60 state,
61 });
62
63 const constraints: Record<string, unknown> = {};
64 if (optional) {
65 constraints.default = null;
66 }
67 if (schema.default !== undefined) {
68 constraints.default = schema.default;
69 }
70 if (schema.description) {
71 constraints.description = schema.description;
72 }
73
74 return {
75 ...typeAst,
76 fieldConstraints: { ...typeAst.fieldConstraints, ...constraints },
77 // pipes: [],
78 };
79 }
80
81 if (schema.items) {
82 schema = deduplicateSchema({ schema });
83
84 if (schema.items) {
85 const itemsAnnotations: string[] = [];
86 const itemsConstraints: Record<string, unknown>[] = [];
87
88 for (const item of schema.items) {
89 const itemAst = irSchemaToAst({
90 plugin,
91 schema: item,
92 state: {
93 ...state,
94 path: ref([...fromRef(state.path), 'items']),
95 },
96 });
97 itemsAnnotations.push(itemAst.typeAnnotation);
98 if (itemAst.fieldConstraints) {
99 itemsConstraints.push(itemAst.fieldConstraints);
100 }
101 }
102
103 const unionType = itemsAnnotations.join(' | ');
104 return {
105 // expression: $.expr(`list[${unionType}]`),
106 fieldConstraints: itemsConstraints.length > 0 ? itemsConstraints[0] : undefined,
107 hasLazyExpression: false,
108 models: [],
109 // pipes: [],
110 typeAnnotation: `list[${unionType}]`,
111 };
112 }
113 }
114
115 return {
116 // expression: $.expr('Any'),
117 hasLazyExpression: false,
118 models: [],
119 // pipes: [],
120 typeAnnotation: 'Any',
121 };
122}
123
124export const handlerV2: PydanticPlugin['Handler'] = ({ plugin }) => {
125 plugin.symbol('Any', {
126 external: 'typing',
127 importKind: 'named',
128 meta: {
129 category: 'external',
130 resource: 'typing.Any',
131 },
132 });
133 plugin.symbol('BaseModel', {
134 external: 'pydantic',
135 importKind: 'named',
136 meta: {
137 category: 'external',
138 resource: 'pydantic.BaseModel',
139 },
140 });
141 plugin.symbol('ConfigDict', {
142 external: 'pydantic',
143 importKind: 'named',
144 meta: {
145 category: 'external',
146 resource: 'pydantic.ConfigDict',
147 },
148 });
149 plugin.symbol('Field', {
150 external: 'pydantic',
151 importKind: 'named',
152 meta: {
153 category: 'external',
154 resource: 'pydantic.Field',
155 },
156 });
157 plugin.symbol('Literal', {
158 external: 'typing',
159 importKind: 'named',
160 meta: {
161 category: 'external',
162 resource: 'typing.Literal',
163 },
164 });
165 plugin.symbol('Optional', {
166 external: 'typing',
167 importKind: 'named',
168 meta: {
169 category: 'external',
170 resource: 'typing.Optional',
171 },
172 });
173
174 const processor = createProcessor(plugin);
175
176 plugin.forEach('operation', 'parameter', 'requestBody', 'schema', 'webhook', (event) => {
177 switch (event.type) {
178 case 'parameter':
179 processor.process({
180 meta: {
181 resource: 'definition',
182 resourceId: pathToJsonPointer(event._path),
183 },
184 naming: plugin.config.definitions,
185 path: event._path,
186 plugin,
187 schema: event.parameter.schema,
188 tags: event.tags,
189 });
190 break;
191 case 'requestBody':
192 processor.process({
193 meta: {
194 resource: 'definition',
195 resourceId: pathToJsonPointer(event._path),
196 },
197 naming: plugin.config.definitions,
198 path: event._path,
199 plugin,
200 schema: event.requestBody.schema,
201 tags: event.tags,
202 });
203 break;
204 case 'schema':
205 processor.process({
206 meta: {
207 resource: 'definition',
208 resourceId: pathToJsonPointer(event._path),
209 },
210 naming: plugin.config.definitions,
211 path: event._path,
212 plugin,
213 schema: event.schema,
214 tags: event.tags,
215 });
216 break;
217 }
218 });
219};