@@ -36,9 +36,19 @@ function OpenAPISchemaProperty(
3636 context : OpenAPIClientContext ;
3737 circularRefs : CircularRefsIds ;
3838 className ?: string ;
39+ discriminator ?: OpenAPIV3 . DiscriminatorObject ;
40+ discriminatorValue ?: string ;
3941 } & Omit < ComponentPropsWithoutRef < 'div' > , 'property' | 'context' | 'circularRefs' | 'className' >
4042) {
41- const { circularRefs : parentCircularRefs , context, className, property, ...rest } = props ;
43+ const {
44+ circularRefs : parentCircularRefs ,
45+ context,
46+ className,
47+ property,
48+ discriminator,
49+ discriminatorValue,
50+ ...rest
51+ } = props ;
4252
4353 const { schema } = property ;
4454
@@ -59,33 +69,47 @@ function OpenAPISchemaProperty(
5969 const circularRefs = new Map ( parentCircularRefs ) ;
6070 circularRefs . set ( schema , id ) ;
6171
62- const properties = getSchemaProperties ( schema ) ;
72+ const properties = getSchemaProperties ( schema , discriminator , discriminatorValue ) ;
6373
6474 const ancestors = new Set ( circularRefs . keys ( ) ) ;
6575 const alternatives = getSchemaAlternatives ( schema , ancestors ) ;
6676
6777 const header = < OpenAPISchemaPresentation id = { id } context = { context } property = { property } /> ;
6878 const content = ( ( ) => {
6979 if ( alternatives ?. schemas ) {
70- const { schemas, discriminator } = alternatives ;
80+ const { schemas, discriminator : alternativeDiscriminator , type } = alternatives ;
7181 return (
7282 < div className = "openapi-schema-alternatives" >
73- { schemas . map ( ( alternativeSchema , index ) => (
74- < div key = { index } className = "openapi-schema-alternative" >
75- < OpenAPISchemaAlternative
76- schema = { alternativeSchema }
77- discriminator = { discriminator }
78- circularRefs = { circularRefs }
79- context = { context }
80- />
81- { index < schemas . length - 1 ? (
82- < OpenAPISchemaAlternativeSeparator
83- schema = { schema }
83+ { schemas . map ( ( alternativeSchema , index ) => {
84+ // If the alternative has its own discriminator, use it.
85+ const effectiveDiscriminator =
86+ alternativeDiscriminator ||
87+ ( type === 'allOf' ? discriminator : undefined ) ;
88+
89+ // If we are inheriting and using parent discriminator, pass down the value.
90+ const effectiveDiscriminatorValue =
91+ ! alternativeDiscriminator && type === 'allOf'
92+ ? discriminatorValue
93+ : undefined ;
94+
95+ return (
96+ < div key = { index } className = "openapi-schema-alternative" >
97+ < OpenAPISchemaAlternative
98+ schema = { alternativeSchema }
99+ discriminator = { effectiveDiscriminator }
100+ discriminatorValue = { effectiveDiscriminatorValue }
101+ circularRefs = { circularRefs }
84102 context = { context }
85103 />
86- ) : null }
87- </ div >
88- ) ) }
104+ { index < schemas . length - 1 ? (
105+ < OpenAPISchemaAlternativeSeparator
106+ schema = { schema }
107+ context = { context }
108+ />
109+ ) : null }
110+ </ div >
111+ ) ;
112+ } ) }
89113 </ div >
90114 ) ;
91115 }
@@ -187,11 +211,45 @@ function OpenAPIRootSchema(props: {
187211 const id = useId ( ) ;
188212 const properties = getSchemaProperties ( schema ) ;
189213 const description = resolveDescription ( schema ) ;
214+ const ancestors = new Set ( parentCircularRefs . keys ( ) ) ;
215+ const alternatives = getSchemaAlternatives ( schema , ancestors ) ;
190216
191- if ( properties ?. length ) {
192- const circularRefs = new Map ( parentCircularRefs ) ;
193- circularRefs . set ( schema , id ) ;
217+ const circularRefs = new Map ( parentCircularRefs ) ;
218+ circularRefs . set ( schema , id ) ;
219+
220+ // Handle root-level oneOf/allOf/anyOf
221+ if ( alternatives ?. schemas ) {
222+ const { schemas, discriminator : alternativeDiscriminator } = alternatives ;
223+ return (
224+ < >
225+ { description ? (
226+ < Markdown source = { description } className = "openapi-schema-root-description" />
227+ ) : null }
228+ < div className = "openapi-schema-alternatives" >
229+ { schemas . map ( ( alternativeSchema , index ) => {
230+ return (
231+ < div key = { index } className = "openapi-schema-alternative" >
232+ < OpenAPISchemaAlternative
233+ schema = { alternativeSchema }
234+ discriminator = { alternativeDiscriminator }
235+ circularRefs = { circularRefs }
236+ context = { context }
237+ />
238+ { index < schemas . length - 1 ? (
239+ < OpenAPISchemaAlternativeSeparator
240+ schema = { schema }
241+ context = { context }
242+ />
243+ ) : null }
244+ </ div >
245+ ) ;
246+ } ) }
247+ </ div >
248+ </ >
249+ ) ;
250+ }
194251
252+ if ( properties ?. length ) {
195253 return (
196254 < >
197255 { description ? (
@@ -228,6 +286,56 @@ export function OpenAPIRootSchemaFromServer(props: {
228286 ) ;
229287}
230288
289+ /**
290+ * Get the discriminator value for a schema.
291+ */
292+ function getDiscriminatorValue (
293+ schema : OpenAPIV3 . SchemaObject ,
294+ discriminator : OpenAPIV3 . DiscriminatorObject | undefined
295+ ) : string | undefined {
296+ if ( ! discriminator ) {
297+ return undefined ;
298+ }
299+
300+ if ( discriminator . mapping ) {
301+ const mappingEntry = Object . entries ( discriminator . mapping ) . find ( ( [ key , ref ] ) => {
302+ if ( schema . title === ref || ( ! ! schema . title && ref . endsWith ( `/${ schema . title } ` ) ) ) {
303+ return true ;
304+ }
305+
306+ // Fallback: check if the title contains the key (normalized)
307+ if ( schema . title ?. toLowerCase ( ) . replace ( / \s / g, '' ) . includes ( key . toLowerCase ( ) ) ) {
308+ return true ;
309+ }
310+
311+ return false ;
312+ } ) ;
313+
314+ if ( mappingEntry ) {
315+ return mappingEntry [ 0 ] ;
316+ }
317+ }
318+
319+ if ( ! discriminator . propertyName || ! schema . properties ) {
320+ return undefined ;
321+ }
322+
323+ const property = schema . properties [ discriminator . propertyName ] ;
324+ if ( ! property || checkIsReference ( property ) ) {
325+ return undefined ;
326+ }
327+
328+ if ( property . const ) {
329+ return String ( property . const ) ;
330+ }
331+
332+ if ( property . enum ?. length === 1 ) {
333+ return String ( property . enum [ 0 ] ) ;
334+ }
335+
336+ return ;
337+ }
338+
231339/**
232340 * Render a tab for an alternative schema.
233341 * It renders directly the properties if relevant;
@@ -236,11 +344,14 @@ export function OpenAPIRootSchemaFromServer(props: {
236344function OpenAPISchemaAlternative ( props : {
237345 schema : OpenAPIV3 . SchemaObject ;
238346 discriminator : OpenAPIV3 . DiscriminatorObject | undefined ;
347+ discriminatorValue ?: string ;
239348 circularRefs : CircularRefsIds ;
240349 context : OpenAPIClientContext ;
241350} ) {
242351 const { schema, discriminator, circularRefs, context } = props ;
243- const properties = getSchemaProperties ( schema , discriminator ) ;
352+ const discriminatorValue =
353+ props . discriminatorValue || getDiscriminatorValue ( schema , discriminator ) ;
354+ const properties = getSchemaProperties ( schema , discriminator , discriminatorValue ) ;
244355
245356 return properties ?. length ? (
246357 < OpenAPIDisclosure
@@ -257,6 +368,8 @@ function OpenAPISchemaAlternative(props: {
257368 ) : (
258369 < OpenAPISchemaProperty
259370 property = { { schema } }
371+ discriminator = { discriminator }
372+ discriminatorValue = { discriminatorValue }
260373 circularRefs = { circularRefs }
261374 context = { context }
262375 />
@@ -435,12 +548,13 @@ export function OpenAPISchemaPresentation(props: {
435548 */
436549function getSchemaProperties (
437550 schema : OpenAPIV3 . SchemaObject ,
438- discriminator ?: OpenAPIV3 . DiscriminatorObject | undefined
551+ discriminator ?: OpenAPIV3 . DiscriminatorObject | undefined ,
552+ discriminatorValue ?: string | undefined
439553) : null | OpenAPISchemaPropertyEntry [ ] {
440554 // check array AND schema.items as this is sometimes null despite what the type indicates
441555 if ( schema . type === 'array' && schema . items && ! checkIsReference ( schema . items ) ) {
442556 const items = schema . items ;
443- const itemProperties = getSchemaProperties ( items ) ;
557+ const itemProperties = getSchemaProperties ( items , discriminator , discriminatorValue ) ;
444558 if ( itemProperties ) {
445559 return itemProperties . map ( ( prop ) => ( {
446560 ...prop ,
@@ -467,17 +581,29 @@ function getSchemaProperties(
467581
468582 if ( schema . properties ) {
469583 Object . entries ( schema . properties ) . forEach ( ( [ propertyName , propertySchema ] ) => {
584+ const isDiscriminator = discriminator ?. propertyName === propertyName ;
470585 if ( checkIsReference ( propertySchema ) ) {
471- return ;
586+ if ( ! isDiscriminator || ! discriminatorValue ) {
587+ return ;
588+ }
589+ }
590+
591+ let finalSchema = propertySchema ;
592+ if ( isDiscriminator && discriminatorValue ) {
593+ finalSchema = {
594+ ...propertySchema ,
595+ const : discriminatorValue ,
596+ enum : [ discriminatorValue ] ,
597+ } ;
472598 }
473599
474600 result . push ( {
475601 propertyName,
476602 required : Array . isArray ( schema . required )
477603 ? schema . required . includes ( propertyName )
478604 : undefined ,
479- isDiscriminatorProperty : discriminator ?. propertyName === propertyName ,
480- schema : propertySchema ,
605+ isDiscriminatorProperty : isDiscriminator ,
606+ schema : finalSchema ,
481607 } ) ;
482608 } ) ;
483609 }
0 commit comments