@@ -563,6 +563,75 @@ export function OpenAPISchemaPresentation(props: {
563563 ) ;
564564}
565565
566+ /**
567+ * Process properties from a schema object into property entries.
568+ */
569+ function processSchemaProperties (
570+ schema : OpenAPIV3 . SchemaObject ,
571+ discriminator ?: OpenAPIV3 . DiscriminatorObject | undefined ,
572+ discriminatorValue ?: string | undefined ,
573+ allRequired ?: Set < string >
574+ ) : OpenAPISchemaPropertyEntry [ ] {
575+ const result : OpenAPISchemaPropertyEntry [ ] = [ ] ;
576+
577+ if ( schema . properties ) {
578+ Object . entries ( schema . properties ) . forEach ( ( [ propertyName , propertySchema ] ) => {
579+ const isDiscriminator = discriminator ?. propertyName === propertyName ;
580+ if ( checkIsReference ( propertySchema ) ) {
581+ if ( ! isDiscriminator || ! discriminatorValue ) {
582+ return ;
583+ }
584+ }
585+
586+ let finalSchema = propertySchema ;
587+ if ( isDiscriminator && discriminatorValue ) {
588+ finalSchema = {
589+ ...propertySchema ,
590+ const : discriminatorValue ,
591+ enum : [ discriminatorValue ] ,
592+ } ;
593+ }
594+
595+ result . push ( {
596+ propertyName,
597+ required : Array . isArray ( schema . required )
598+ ? schema . required . includes ( propertyName )
599+ : allRequired ?. has ( propertyName )
600+ ? true
601+ : undefined ,
602+ isDiscriminatorProperty : isDiscriminator ,
603+ schema : finalSchema ,
604+ } ) ;
605+ } ) ;
606+ }
607+
608+ if ( schema . additionalProperties && ! checkIsReference ( schema . additionalProperties ) ) {
609+ result . push ( {
610+ propertyName : 'Other properties' ,
611+ schema : schema . additionalProperties === true ? { } : schema . additionalProperties ,
612+ } ) ;
613+ }
614+
615+ return result ;
616+ }
617+
618+ /**
619+ * Merge properties into a result array, with later properties overriding earlier ones.
620+ */
621+ function mergeProperties (
622+ result : OpenAPISchemaPropertyEntry [ ] ,
623+ newProperties : OpenAPISchemaPropertyEntry [ ]
624+ ) : void {
625+ for ( const prop of newProperties ) {
626+ const existingIndex = result . findIndex ( ( p ) => p . propertyName === prop . propertyName ) ;
627+ if ( existingIndex >= 0 ) {
628+ result [ existingIndex ] = prop ;
629+ } else {
630+ result . push ( prop ) ;
631+ }
632+ }
633+ }
634+
566635/**
567636 * Get the sub-properties of a schema.
568637 */
@@ -596,46 +665,55 @@ function getSchemaProperties(
596665 return [ { propertyName : 'items' , schema : items } ] ;
597666 }
598667
599- if ( schema . type === 'object' || schema . properties ) {
600- const result : OpenAPISchemaPropertyEntry [ ] = [ ] ;
601-
602- if ( schema . properties ) {
603- Object . entries ( schema . properties ) . forEach ( ( [ propertyName , propertySchema ] ) => {
604- const isDiscriminator = discriminator ?. propertyName === propertyName ;
605- if ( checkIsReference ( propertySchema ) ) {
606- if ( ! isDiscriminator || ! discriminatorValue ) {
607- return ;
608- }
609- }
668+ // Handle allOf by collecting properties from all schemas in the allOf
669+ if ( schema . allOf && Array . isArray ( schema . allOf ) ) {
670+ const allOfSchemas = schema . allOf . filter (
671+ ( s ) : s is OpenAPIV3 . SchemaObject => ! checkIsReference ( s )
672+ ) ;
610673
611- let finalSchema = propertySchema ;
612- if ( isDiscriminator && discriminatorValue ) {
613- finalSchema = {
614- ...propertySchema ,
615- const : discriminatorValue ,
616- enum : [ discriminatorValue ] ,
617- } ;
674+ if ( allOfSchemas . length > 0 ) {
675+ const result : OpenAPISchemaPropertyEntry [ ] = [ ] ;
676+ const allRequired = new Set < string > ( ) ;
677+
678+ // Collect required fields and properties from all allOf schemas
679+ for ( const allOfSchema of allOfSchemas ) {
680+ if ( Array . isArray ( allOfSchema . required ) ) {
681+ allOfSchema . required . forEach ( ( req ) => allRequired . add ( req ) ) ;
682+ }
683+ const allOfProperties = getSchemaProperties (
684+ allOfSchema ,
685+ discriminator ,
686+ discriminatorValue
687+ ) ;
688+ if ( allOfProperties ) {
689+ mergeProperties ( result , allOfProperties ) ;
618690 }
691+ }
619692
620- result . push ( {
621- propertyName,
622- required : Array . isArray ( schema . required )
623- ? schema . required . includes ( propertyName )
624- : undefined ,
625- isDiscriminatorProperty : isDiscriminator ,
626- schema : finalSchema ,
627- } ) ;
628- } ) ;
629- }
693+ // Collect required fields from the schema itself
694+ if ( Array . isArray ( schema . required ) ) {
695+ schema . required . forEach ( ( req ) => allRequired . add ( req ) ) ;
696+ }
630697
631- if ( schema . additionalProperties && ! checkIsReference ( schema . additionalProperties ) ) {
632- result . push ( {
633- propertyName : 'Other properties' ,
634- schema : schema . additionalProperties === true ? { } : schema . additionalProperties ,
698+ // Include direct properties from the schema itself
699+ mergeProperties (
700+ result ,
701+ processSchemaProperties ( schema , discriminator , discriminatorValue , allRequired )
702+ ) ;
703+
704+ // Update required status for all properties
705+ result . forEach ( ( prop ) => {
706+ if ( prop . propertyName && allRequired . has ( prop . propertyName ) ) {
707+ prop . required = true ;
708+ }
635709 } ) ;
710+
711+ return result . length > 0 ? result : null ;
636712 }
713+ }
637714
638- return result ;
715+ if ( schema . type === 'object' || schema . properties ) {
716+ return processSchemaProperties ( schema , discriminator , discriminatorValue ) ;
639717 }
640718
641719 return null ;
0 commit comments