@@ -11,7 +11,7 @@ export interface OperationInfo {
11
11
responses : OpenAPIV3 . ResponsesObject ;
12
12
}
13
13
14
- function generateAxiosMethod ( operation : OperationInfo ) : string {
14
+ function generateAxiosMethod ( operation : OperationInfo , spec : OpenAPIV3 . Document ) : string {
15
15
const { method, path, operationId, summary, description, parameters, requestBody, responses } = operation ;
16
16
17
17
// Generate JSDoc
@@ -50,59 +50,67 @@ function generateAxiosMethod(operation: OperationInfo): string {
50
50
51
51
jsDocLines . push ( ' */' ) ;
52
52
53
- // Generate method parameters
54
53
const urlParams = parameters ?. filter ( p => p . in === 'path' ) || [ ] ;
55
54
const queryParams = parameters ?. filter ( p => p . in === 'query' ) || [ ] ;
56
55
56
+ const isFormData = requestBody &&
57
+ 'content' in requestBody &&
58
+ requestBody . content ?. [ 'multipart/form-data' ] ;
59
+
60
+ const formDataSchema = isFormData && requestBody . content [ 'multipart/form-data' ] . schema ? (
61
+ '$ref' in requestBody . content [ 'multipart/form-data' ] . schema
62
+ ? spec . components ?. schemas ?. [ requestBody . content [ 'multipart/form-data' ] . schema . $ref . split ( '/' ) . pop ( ) ! ]
63
+ : requestBody . content [ 'multipart/form-data' ] . schema
64
+ ) as OpenAPIV3 . SchemaObject : undefined ;
65
+
57
66
// Build data type parts
58
- const typeComponents : string [ ] = [ ] ;
67
+ const dataProps : string [ ] = [ ] ;
59
68
60
- // Add request body type if it exists
61
- if ( requestBody ) {
62
- typeComponents . push ( operationId + 'Request' ) ;
63
- }
64
-
65
- // Add path and query parameters if any
66
- const additionalProps : string [ ] = [ ] ;
69
+ // Add path and query parameters
67
70
urlParams . forEach ( p => {
68
- additionalProps . push ( `${ p . name } : ${ getTypeFromParam ( p ) } ` ) ;
71
+ dataProps . push ( `${ p . name } : ${ getTypeFromParam ( p ) } ` ) ;
69
72
} ) ;
70
73
queryParams . forEach ( p => {
71
- additionalProps . push ( `${ p . name } ${ p . required ? '' : '?' } : ${ getTypeFromParam ( p ) } ` ) ;
74
+ dataProps . push ( `${ p . name } ${ p . required ? '' : '?' } : ${ getTypeFromParam ( p ) } ` ) ;
72
75
} ) ;
73
76
74
- if ( additionalProps . length > 0 ) {
75
- typeComponents . push ( `{ ${ additionalProps . join ( '; ' ) } }` ) ;
76
- }
77
-
78
- const hasData = typeComponents . length > 0 ;
79
- const dataType = typeComponents . length > 1
80
- ? typeComponents . join ( ' & ' )
81
- : typeComponents [ 0 ] || 'undefined' ;
77
+ // Add request body type if it exists
78
+ const hasData = ( parameters && parameters . length > 0 ) || operation . requestBody ;
79
+ const dataType = hasData
80
+ ? requestBody
81
+ ? `${ operationId } Request & { ${ dataProps . join ( '; ' ) } }`
82
+ : `{ ${ dataProps . join ( '; ' ) } }`
83
+ : 'undefined' ;
82
84
83
85
// Get response type from 2xx response
84
86
const successResponse = Object . entries ( responses ) . find ( ( [ code ] ) => code . startsWith ( '2' ) ) ;
85
87
const responseType = successResponse ? `${ operationId } Response${ successResponse [ 0 ] } ` : 'any' ;
86
88
87
- // Generate method
88
89
const urlWithParams = urlParams . length > 0
89
90
? path . replace ( / { ( \w + ) } / g, '${data.$1}' )
90
91
: path ;
91
92
92
93
const methodBody = [
93
94
`const url = \`${ urlWithParams } \`;` ,
94
- // Combine query and path params to filter from body
95
- ( queryParams . length > 0 || urlParams . length > 0 ) ?
96
- `const paramsToFilter = ${ JSON . stringify ( [ ...queryParams , ...urlParams ] . map ( p => p . name ) ) } ;` : '' ,
97
95
queryParams . length > 0 ?
98
96
`const queryData = Object.fromEntries(Object.entries(data).filter(([key]) => ${ JSON . stringify ( queryParams . map ( p => p . name ) ) } .includes(key)));` : '' ,
99
- ( requestBody && ( queryParams . length > 0 || urlParams . length > 0 ) ) ?
100
- `const bodyData = Object.fromEntries(Object.entries(data).filter(([key]) => !paramsToFilter.includes(key)));` : '' ,
101
- queryParams . length > 0 ?
102
- 'const queryString = `?${new URLSearchParams(queryData)}`;' : '' ,
103
- `return this.axios.${ method . toLowerCase ( ) } <${ responseType } >(url${ queryParams . length > 0 ? ' + queryString' : '' } , {
104
- ${ requestBody ? `data: ${ ( queryParams . length > 0 || urlParams . length > 0 ) ? 'bodyData' : 'data' } ,` : '' }
105
- headers
97
+ requestBody && queryParams . length > 0 ?
98
+ `const bodyData = Object.fromEntries(Object.entries(data).filter(([key]) => !${ JSON . stringify ( queryParams . map ( p => p . name ) ) } .includes(key)));` : '' ,
99
+ isFormData ?
100
+ `const formData = new FormData();
101
+ ${ Object . entries ( ( formDataSchema ?. properties || { } )
102
+ ) . map ( ( [ key , prop ] : [ string , any ] ) => {
103
+ const isBinary = prop . format === 'binary' ;
104
+ return formDataSchema ?. required ?. includes ( key )
105
+ ? `formData.append("${ key } ", ${ isBinary ? '' : 'String(' } ${ queryParams . length > 0 ? 'bodyData' : 'data' } .${ key } ${ isBinary ? '' : ')' } );`
106
+ : `if (${ queryParams . length > 0 ? 'bodyData' : 'data' } .${ key } != null) {
107
+ formData.append("${ key } ", ${ isBinary ? '' : 'String(' } ${ queryParams . length > 0 ? 'bodyData' : 'data' } .${ key } ${ isBinary ? '' : ')' } );
108
+ }`
109
+ } ) . join ( '\n ' ) } ` : '' ,
110
+ `return this.axios.${ method . toLowerCase ( ) } <${ responseType } >(url, {
111
+ ${ queryParams . length > 0 ? `params: queryData,` : '' }
112
+ ${ requestBody ? `data: ${ isFormData ? 'formData' : queryParams . length > 0 ? 'bodyData' : 'data' } ,` : '' }
113
+ ${ isFormData ? `headers: { 'Content-Type': 'multipart/form-data', ...headers },` : 'headers' }
106
114
});`
107
115
] . filter ( Boolean ) . join ( '\n ' ) ;
108
116
@@ -184,7 +192,7 @@ export class ApiClient {
184
192
}
185
193
});
186
194
}
187
- ${ operations . map ( op => generateAxiosMethod ( op ) ) . join ( '\n\n' ) }
195
+ ${ operations . map ( op => generateAxiosMethod ( op , spec ) ) . join ( '\n\n' ) }
188
196
}
189
197
` ;
190
198
}
0 commit comments