@@ -23,6 +23,7 @@ import type {
2323 IRModelObject ,
2424 IRModelString ,
2525 IROperation ,
26+ IROperationParams ,
2627 IRParameter ,
2728 IRParameterBase ,
2829 IRParameterCookie ,
@@ -31,6 +32,7 @@ import type {
3132 IRParameterQuery ,
3233 IRPreprocess ,
3334 IRRef ,
35+ IRRequestBody ,
3436 IRResponse ,
3537 IRServer ,
3638 IRServerVariable ,
@@ -59,7 +61,7 @@ export class Input {
5961 constructor (
6062 readonly loader : OpenapiLoader ,
6163 readonly config : InputConfig ,
62- private readonly schemaNormalizer = new SchemaNormalizer ( config ) ,
64+ readonly schemaNormalizer = new SchemaNormalizer ( config ) ,
6365 private readonly parameterNormalizer = new ParameterNormalizer (
6466 schemaNormalizer ,
6567 ) ,
@@ -89,8 +91,10 @@ export class Input {
8991
9092 return Object . fromEntries (
9193 Object . entries ( schemas ) . map ( ( [ name , maybeSchema ] ) => {
92- // TODO: double normalization?
93- return [ name , this . schema ( this . schemaNormalizer . normalize ( maybeSchema ) ) ]
94+ const schema = this . schemaNormalizer . normalize (
95+ this . loader . schema ( maybeSchema ) ,
96+ )
97+ return [ name , schema ]
9498 } ) ,
9599 )
96100 }
@@ -119,8 +123,6 @@ export class Input {
119123 ) ) {
120124 paths = this . loader . paths ( paths )
121125
122- const params = this . normalizeParameters ( paths . parameters )
123-
124126 const additionalAttributes = Object . fromEntries (
125127 Object . entries ( paths ) . filter (
126128 ( [ key ] ) => key !== "parameters" && ! isHttpMethod ( key ) ,
@@ -154,16 +156,18 @@ export class Input {
154156 throw new Error ( "callbacks are not supported" )
155157 }
156158
159+ const parameters = ( paths . parameters ?? [ ] ) . concat (
160+ definition . parameters ?? [ ] ,
161+ )
162+
157163 result . push ( {
158164 ...additionalAttributes ,
159165 route,
160166 method,
161167 servers : this . normalizeServers (
162168 coalesce ( definition . servers , paths . servers , [ ] ) ,
163169 ) ,
164- parameters : params . concat (
165- this . normalizeParameters ( definition . parameters ) ,
166- ) ,
170+ params : this . normalizeParameters ( operationId , parameters ) ,
167171 operationId,
168172 tags : definition . tags ?? [ ] ,
169173 requestBody : this . normalizeRequestBodyObject (
@@ -235,9 +239,13 @@ export class Input {
235239 ) . map ( ( [ name , operations ] ) => ( { name, operations} ) )
236240 }
237241
238- schema ( maybeRef : Reference | Schema ) : IRModel {
239- const schema = this . loader . schema ( maybeRef )
240- return this . schemaNormalizer . normalize ( schema )
242+ schema ( maybeRef : MaybeIRModel ) : IRModel {
243+ if ( isRef ( maybeRef ) ) {
244+ const schema = this . loader . schema ( maybeRef )
245+ return this . schemaNormalizer . normalize ( schema )
246+ }
247+
248+ return maybeRef
241249 }
242250
243251 preprocess ( maybePreprocess : Reference | xInternalPreproccess ) : IRPreprocess {
@@ -288,7 +296,7 @@ export class Input {
288296 private normalizeRequestBodyObject (
289297 operationId : string ,
290298 requestBody ?: RequestBody | Reference ,
291- ) {
299+ ) : IRRequestBody | undefined {
292300 if ( ! requestBody ) {
293301 return undefined
294302 }
@@ -340,11 +348,73 @@ export class Input {
340348 }
341349
342350 private normalizeParameters (
351+ operationId : string ,
343352 parameters : ( Parameter | Reference ) [ ] = [ ] ,
344- ) : IRParameter [ ] {
345- return parameters
346- . map ( ( it ) => this . loader . parameter ( it ) )
347- . map ( ( it ) => this . parameterNormalizer . normalizeParameter ( it ) )
353+ ) : IROperationParams {
354+ const allParameters = parameters . map ( ( it ) => this . loader . parameter ( it ) )
355+
356+ const pathParameters = allParameters . filter ( ( it ) => it . in === "path" )
357+ const queryParameters = allParameters . filter ( ( it ) => it . in === "query" )
358+ const headerParameters = allParameters . filter ( ( it ) => it . in === "header" )
359+
360+ const normalizedParameters = allParameters . map ( ( it ) =>
361+ this . parameterNormalizer . normalizeParameter ( it ) ,
362+ )
363+
364+ return {
365+ all : normalizedParameters ,
366+ path : {
367+ name : `${ operationId } ParamSchema` ,
368+ list : normalizedParameters . filter ( ( it ) => it . in === "path" ) ,
369+ $ref : this . loader . addVirtualType (
370+ operationId ,
371+ upperFirst ( `${ operationId } ParamSchema` ) ,
372+ this . reduceParametersToOpenApiSchema ( pathParameters ) ,
373+ ) ,
374+ } ,
375+ query : {
376+ name : `${ operationId } QuerySchema` ,
377+ list : normalizedParameters . filter ( ( it ) => it . in === "query" ) ,
378+ $ref : this . loader . addVirtualType (
379+ operationId ,
380+ upperFirst ( `${ operationId } QuerySchema` ) ,
381+ this . reduceParametersToOpenApiSchema ( queryParameters ) ,
382+ ) ,
383+ } ,
384+ header : {
385+ name : `${ operationId } RequestHeaderSchema` ,
386+ list : normalizedParameters . filter ( ( it ) => it . in === "header" ) ,
387+ $ref : this . loader . addVirtualType (
388+ operationId ,
389+ upperFirst ( `${ operationId } RequestHeaderSchema` ) ,
390+ this . reduceParametersToOpenApiSchema ( headerParameters ) ,
391+ ) ,
392+ } ,
393+ }
394+ }
395+
396+ private reduceParametersToOpenApiSchema (
397+ parameters : Parameter [ ] ,
398+ ) : SchemaObject {
399+ const properties : Record < string , Schema | Reference > = { }
400+ const required : string [ ] = [ ]
401+
402+ for ( const parameter of parameters ) {
403+ properties [ parameter . name ] = parameter . schema
404+
405+ if ( parameter . required ) {
406+ required . push ( parameter . name )
407+ }
408+ }
409+
410+ return {
411+ isIRModel : false ,
412+ type : "object" ,
413+ properties,
414+ required,
415+ additionalProperties : false ,
416+ nullable : false ,
417+ }
348418 }
349419
350420 private normalizeOperationId (
@@ -374,28 +444,28 @@ export class Input {
374444
375445 return Object . fromEntries (
376446 filtered . map ( ( [ contentType , mediaType ] ) => {
377- return [
378- contentType ,
379- {
380- schema : this . normalizeMediaTypeSchema (
381- operationId ,
382- contentType ,
383- mediaType . schema ,
384- suffix ,
385- hasMultipleMediaTypes ,
386- ) ,
387- encoding : mediaType . encoding ,
388- } ,
389- ]
390- } ) ,
447+ return [
448+ contentType ,
449+ {
450+ schema : this . normalizeMediaTypeSchema (
451+ operationId ,
452+ contentType ,
453+ mediaType . schema ,
454+ suffix ,
455+ hasMultipleMediaTypes ,
456+ ) ,
457+ encoding : mediaType . encoding ,
458+ } ,
459+ ]
460+ } ) ,
391461 )
392462 }
393463
394464 private normalizeMediaTypeSchema (
395465 operationId : string ,
396466 mediaType : string ,
397467 schema : Schema | Reference ,
398- suffix : string ,
468+ suffix : "RequestBody" | `${ string } Response` ,
399469 hasMultipleMediaTypes : boolean ,
400470 ) : MaybeIRModel {
401471 const syntheticName = `${ upperFirst ( operationId ) } ${
@@ -405,15 +475,16 @@ export class Input {
405475 const result = this . schemaNormalizer . normalize ( schema )
406476
407477 const shouldCreateVirtualType =
408- this . config . extractInlineSchemas &&
478+ ( this . config . extractInlineSchemas || suffix === "RequestBody" ) &&
409479 ! isRef ( result ) &&
480+ ! isRef ( schema ) &&
410481 ( result . type === "object" ||
411482 ( result . type === "array" &&
412483 ! isRef ( result . items ) &&
413484 result . items . type === "object" ) )
414485
415486 return shouldCreateVirtualType
416- ? this . loader . addVirtualType ( operationId , syntheticName , result )
487+ ? this . loader . addVirtualType ( operationId , syntheticName , schema )
417488 : result
418489 }
419490}
@@ -461,6 +532,17 @@ export class ParameterNormalizer {
461532 throwUnsupportedStyle ( style )
462533 }
463534
535+ // todo: add if dereferenced(base.schema).type === "array
536+ /*
537+
538+ "x-internal-preprocess": {
539+ deserialize: {
540+ fn: "(it: unknown) => Array.isArray(it) || it === undefined ? it : [it]",
541+ },
542+ },
543+
544+ */
545+
464546 return {
465547 ...base ,
466548 in : "query" ,
@@ -571,25 +653,30 @@ export class SchemaNormalizer {
571653 return schemaObject satisfies IRRef
572654 }
573655
656+ if ( Reflect . get ( schemaObject , "isIRModel" ) ) {
657+ throw new Error ( "double normalization!" )
658+ }
659+
574660 // TODO: HACK: translates a type array into a a oneOf - unsure if this makes sense,
575661 // or is the cleanest way to do it. I'm fairly sure this will work fine
576662 // for most things though.
577663 if ( Array . isArray ( schemaObject . type ) ) {
578664 const nullable = Boolean ( schemaObject . type . find ( ( it ) => it === "null" ) )
579665 return self . normalize ( {
666+ isIRModel : false ,
667+ type : "object" ,
580668 oneOf : schemaObject . type
581669 . filter ( ( it ) => it !== "null" )
582- . map ( ( it ) =>
583- self . normalize ( {
584- ...schemaObject ,
585- type : it ,
586- nullable,
587- } ) ,
588- ) ,
670+ . map ( ( it ) => ( {
671+ ...schemaObject ,
672+ type : it ,
673+ nullable,
674+ } ) ) ,
589675 } )
590676 }
591677
592678 const base : IRModelBase = {
679+ isIRModel : true ,
593680 nullable : schemaObject . nullable || false ,
594681 readOnly : schemaObject . readOnly || false ,
595682 default : schemaObject . default ,
@@ -795,10 +882,11 @@ export class SchemaNormalizer {
795882 type : "never" ,
796883 }
797884 }
798- default :
885+ default : {
799886 throw new Error (
800887 `unsupported type '${ schemaObject . type satisfies never } '` ,
801888 )
889+ }
802890 }
803891
804892 function normalizeProperties (
0 commit comments