Skip to content

Commit 2aa19bd

Browse files
committed
Fix schema
1 parent 7401378 commit 2aa19bd

File tree

3 files changed

+120
-13
lines changed

3 files changed

+120
-13
lines changed

examples/vite/openapi.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,20 @@
175175
},
176176
"components": {
177177
"schemas": {
178+
"Post": {
179+
"type": "object",
180+
"properties": {
181+
"id": {
182+
"type": "integer"
183+
},
184+
"createdAt": {
185+
"type": "string",
186+
"format": "date-time",
187+
"default": "2021-01-01T00:00:00Z"
188+
}
189+
},
190+
"required": ["id"]
191+
},
178192
"User": {
179193
"type": "object",
180194
"properties": {

packages/core/src/options.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
export interface DevupApiTypeGeneratorOptions {
22
/**
33
* Case conversion type for API endpoint names and parameters
4-
* @default 'camel'
4+
* @default {'camel'}
55
*/
66
convertCase?: 'snake' | 'camel' | 'pascal' | 'maintain'
77
/**
88
* Whether to make all properties non-nullable by default
9-
* @default false
9+
* @default {false}
1010
*/
11-
defaultNonNullable?: boolean
11+
requestDefaultNonNullable?: boolean
12+
/**
13+
* Whether to make all request properties non-nullable by default
14+
* @default {true}
15+
*/
16+
responseDefaultNonNullable?: boolean
1217
}
1318

1419
export interface DevupApiOptions extends DevupApiTypeGeneratorOptions {
1520
/**
1621
* Temporary directory for storing generated files
17-
* @default 'df'
22+
* @default {'df'}
1823
*/
1924
tempDir?: string
2025

2126
/**
2227
* OpenAPI file path
23-
* @default 'openapi.json'
28+
* @default {'openapi.json'}
2429
*/
2530
openapiFile?: string
2631
}

packages/generator/src/generate-interface.ts

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,16 @@ export interface EndpointDefinition {
2222
params?: Record<string, ParameterDefinition>
2323
body?: unknown
2424
query?: Record<string, ParameterDefinition>
25+
response?: unknown
2526
}
2627

28+
// Helper function to extract schema names from $ref
29+
function extractSchemaNameFromRef(ref: string): string | null {
30+
if (ref.startsWith('#/components/schemas/')) {
31+
return ref.replace('#/components/schemas/', '')
32+
}
33+
return null
34+
}
2735
export function generateInterface(
2836
schema: OpenAPIV3_1.Document,
2937
options?: DevupApiTypeGeneratorOptions,
@@ -40,14 +48,6 @@ export function generateInterface(
4048
} as const
4149
const convertCaseType = options?.convertCase ?? 'camel'
4250

43-
// Helper function to extract schema names from $ref
44-
const extractSchemaNameFromRef = (ref: string): string | null => {
45-
if (ref.startsWith('#/components/schemas/')) {
46-
return ref.replace('#/components/schemas/', '')
47-
}
48-
return null
49-
}
50-
5151
// Helper function to collect schema names from a schema object
5252
const collectSchemaNames = (
5353
schemaObj: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject,
@@ -248,6 +248,94 @@ export function generateInterface(
248248
endpoint.body = requestBodyType
249249
}
250250

251+
// Extract response
252+
// Check if response uses a component schema
253+
let responseType: unknown
254+
if (operation.responses) {
255+
// Prefer 200 response, fallback to first available response
256+
const successResponse =
257+
operation.responses['200'] ||
258+
operation.responses['201'] ||
259+
Object.values(operation.responses)[0]
260+
261+
if (successResponse) {
262+
if ('$ref' in successResponse) {
263+
// ResponseObject reference - skip for now
264+
// Could resolve if needed
265+
} else if ('content' in successResponse) {
266+
const content = successResponse.content
267+
const jsonContent = content?.['application/json']
268+
if (
269+
jsonContent &&
270+
'schema' in jsonContent &&
271+
jsonContent.schema
272+
) {
273+
// Check if schema is a direct reference to components.schemas
274+
if ('$ref' in jsonContent.schema) {
275+
const schemaName = extractSchemaNameFromRef(
276+
jsonContent.schema.$ref,
277+
)
278+
// Check if schema exists in components.schemas and is used in response
279+
if (
280+
schemaName &&
281+
schema.components?.schemas?.[schemaName] &&
282+
responseSchemaNames.has(schemaName)
283+
) {
284+
// Use component reference
285+
responseType = `DevupResponseComponentStruct['${schemaName}']`
286+
} else {
287+
// Extract schema type
288+
const { type: schemaType } = getTypeFromSchema(
289+
jsonContent.schema,
290+
schema,
291+
)
292+
responseType = schemaType
293+
}
294+
} else {
295+
// Check if it's an array with items referencing a component schema
296+
const schemaObj =
297+
jsonContent.schema as OpenAPIV3_1.SchemaObject
298+
if (
299+
schemaObj.type === 'array' &&
300+
schemaObj.items &&
301+
'$ref' in schemaObj.items
302+
) {
303+
const schemaName = extractSchemaNameFromRef(
304+
schemaObj.items.$ref,
305+
)
306+
// Check if schema exists in components.schemas and is used in response
307+
if (
308+
schemaName &&
309+
schema.components?.schemas?.[schemaName] &&
310+
responseSchemaNames.has(schemaName)
311+
) {
312+
// Use component reference for array items
313+
responseType = `Array<DevupResponseComponentStruct['${schemaName}']>`
314+
} else {
315+
// Extract schema type
316+
const { type: schemaType } = getTypeFromSchema(
317+
jsonContent.schema,
318+
schema,
319+
)
320+
responseType = schemaType
321+
}
322+
} else {
323+
// Extract schema type
324+
const { type: schemaType } = getTypeFromSchema(
325+
jsonContent.schema,
326+
schema,
327+
)
328+
responseType = schemaType
329+
}
330+
}
331+
}
332+
}
333+
}
334+
}
335+
if (responseType !== undefined) {
336+
endpoint.response = responseType
337+
}
338+
251339
// Generate path key (normalize path by replacing {param} with converted param and removing slashes)
252340
const normalizedPath = path.replace(/\{([^}]+)\}/g, (_, param) => {
253341
// Convert param name based on case type

0 commit comments

Comments
 (0)