@@ -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,34 +69,23 @@ 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 ;
7180 return (
72- < 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 }
84- context = { context }
85- />
86- ) : null }
87- </ div >
88- ) ) }
89- </ div >
81+ < OpenAPISchemaAlternatives
82+ alternatives = { alternatives }
83+ schema = { schema }
84+ circularRefs = { circularRefs }
85+ context = { context }
86+ parentDiscriminator = { discriminator }
87+ parentDiscriminatorValue = { discriminatorValue }
88+ />
9089 ) ;
9190 }
9291
@@ -187,11 +186,30 @@ function OpenAPIRootSchema(props: {
187186 const id = useId ( ) ;
188187 const properties = getSchemaProperties ( schema ) ;
189188 const description = resolveDescription ( schema ) ;
189+ const ancestors = new Set ( parentCircularRefs . keys ( ) ) ;
190+ const alternatives = getSchemaAlternatives ( schema , ancestors ) ;
190191
191- if ( properties ?. length ) {
192- const circularRefs = new Map ( parentCircularRefs ) ;
193- circularRefs . set ( schema , id ) ;
192+ const circularRefs = new Map ( parentCircularRefs ) ;
193+ circularRefs . set ( schema , id ) ;
194+
195+ // Handle root-level oneOf/allOf/anyOf
196+ if ( alternatives ?. schemas ) {
197+ return (
198+ < >
199+ { description ? (
200+ < Markdown source = { description } className = "openapi-schema-root-description" />
201+ ) : null }
202+ < OpenAPISchemaAlternatives
203+ alternatives = { alternatives }
204+ schema = { schema }
205+ circularRefs = { circularRefs }
206+ context = { context }
207+ />
208+ </ >
209+ ) ;
210+ }
194211
212+ if ( properties ?. length ) {
195213 return (
196214 < >
197215 { description ? (
@@ -228,6 +246,116 @@ export function OpenAPIRootSchemaFromServer(props: {
228246 ) ;
229247}
230248
249+ /**
250+ * Get the discriminator value for a schema.
251+ */
252+ function getDiscriminatorValue (
253+ schema : OpenAPIV3 . SchemaObject ,
254+ discriminator : OpenAPIV3 . DiscriminatorObject | undefined
255+ ) : string | undefined {
256+ if ( ! discriminator ) {
257+ return undefined ;
258+ }
259+
260+ if ( discriminator . mapping ) {
261+ const mappingEntry = Object . entries ( discriminator . mapping ) . find ( ( [ key , ref ] ) => {
262+ if ( schema . title === ref || ( ! ! schema . title && ref . endsWith ( `/${ schema . title } ` ) ) ) {
263+ return true ;
264+ }
265+
266+ // Fallback: check if the title contains the key (normalized)
267+ if ( schema . title ?. toLowerCase ( ) . replace ( / \s / g, '' ) . includes ( key . toLowerCase ( ) ) ) {
268+ return true ;
269+ }
270+
271+ return false ;
272+ } ) ;
273+
274+ if ( mappingEntry ) {
275+ return mappingEntry [ 0 ] ;
276+ }
277+ }
278+
279+ if ( ! discriminator . propertyName || ! schema . properties ) {
280+ return undefined ;
281+ }
282+
283+ const property = schema . properties [ discriminator . propertyName ] ;
284+ if ( ! property || checkIsReference ( property ) ) {
285+ return undefined ;
286+ }
287+
288+ if ( property . const ) {
289+ return String ( property . const ) ;
290+ }
291+
292+ if ( property . enum ?. length === 1 ) {
293+ return String ( property . enum [ 0 ] ) ;
294+ }
295+
296+ return ;
297+ }
298+
299+ /**
300+ * Render alternatives (oneOf/allOf/anyOf) for a schema.
301+ */
302+ function OpenAPISchemaAlternatives ( props : {
303+ alternatives : SchemaAlternatives ;
304+ schema : OpenAPIV3 . SchemaObject ;
305+ circularRefs : CircularRefsIds ;
306+ context : OpenAPIClientContext ;
307+ parentDiscriminator ?: OpenAPIV3 . DiscriminatorObject ;
308+ parentDiscriminatorValue ?: string ;
309+ } ) {
310+ const {
311+ alternatives,
312+ schema,
313+ circularRefs,
314+ context,
315+ parentDiscriminator,
316+ parentDiscriminatorValue,
317+ } = props ;
318+
319+ if ( ! alternatives ?. schemas ) {
320+ return null ;
321+ }
322+
323+ const { schemas, discriminator : alternativeDiscriminator , type } = alternatives ;
324+
325+ return (
326+ < div className = "openapi-schema-alternatives" >
327+ { schemas . map ( ( alternativeSchema , index ) => {
328+ // If the alternative has its own discriminator, use it.
329+ // Otherwise, for allOf, inherit from parent discriminator.
330+ const effectiveDiscriminator =
331+ alternativeDiscriminator ||
332+ ( type === 'allOf' ? parentDiscriminator : undefined ) ;
333+
334+ // If we are inheriting and using parent discriminator, pass down the value.
335+ const effectiveDiscriminatorValue =
336+ ! alternativeDiscriminator && type === 'allOf'
337+ ? parentDiscriminatorValue
338+ : undefined ;
339+
340+ return (
341+ < div key = { index } className = "openapi-schema-alternative" >
342+ < OpenAPISchemaAlternative
343+ schema = { alternativeSchema }
344+ discriminator = { effectiveDiscriminator }
345+ discriminatorValue = { effectiveDiscriminatorValue }
346+ circularRefs = { circularRefs }
347+ context = { context }
348+ />
349+ { index < schemas . length - 1 ? (
350+ < OpenAPISchemaAlternativeSeparator schema = { schema } context = { context } />
351+ ) : null }
352+ </ div >
353+ ) ;
354+ } ) }
355+ </ div >
356+ ) ;
357+ }
358+
231359/**
232360 * Render a tab for an alternative schema.
233361 * It renders directly the properties if relevant;
@@ -236,11 +364,14 @@ export function OpenAPIRootSchemaFromServer(props: {
236364function OpenAPISchemaAlternative ( props : {
237365 schema : OpenAPIV3 . SchemaObject ;
238366 discriminator : OpenAPIV3 . DiscriminatorObject | undefined ;
367+ discriminatorValue ?: string ;
239368 circularRefs : CircularRefsIds ;
240369 context : OpenAPIClientContext ;
241370} ) {
242371 const { schema, discriminator, circularRefs, context } = props ;
243- const properties = getSchemaProperties ( schema , discriminator ) ;
372+ const discriminatorValue =
373+ props . discriminatorValue || getDiscriminatorValue ( schema , discriminator ) ;
374+ const properties = getSchemaProperties ( schema , discriminator , discriminatorValue ) ;
244375
245376 return properties ?. length ? (
246377 < OpenAPIDisclosure
@@ -257,6 +388,8 @@ function OpenAPISchemaAlternative(props: {
257388 ) : (
258389 < OpenAPISchemaProperty
259390 property = { { schema } }
391+ discriminator = { discriminator }
392+ discriminatorValue = { discriminatorValue }
260393 circularRefs = { circularRefs }
261394 context = { context }
262395 />
@@ -435,12 +568,13 @@ export function OpenAPISchemaPresentation(props: {
435568 */
436569function getSchemaProperties (
437570 schema : OpenAPIV3 . SchemaObject ,
438- discriminator ?: OpenAPIV3 . DiscriminatorObject | undefined
571+ discriminator ?: OpenAPIV3 . DiscriminatorObject | undefined ,
572+ discriminatorValue ?: string | undefined
439573) : null | OpenAPISchemaPropertyEntry [ ] {
440574 // check array AND schema.items as this is sometimes null despite what the type indicates
441575 if ( schema . type === 'array' && schema . items && ! checkIsReference ( schema . items ) ) {
442576 const items = schema . items ;
443- const itemProperties = getSchemaProperties ( items ) ;
577+ const itemProperties = getSchemaProperties ( items , discriminator , discriminatorValue ) ;
444578 if ( itemProperties ) {
445579 return itemProperties . map ( ( prop ) => ( {
446580 ...prop ,
@@ -467,17 +601,29 @@ function getSchemaProperties(
467601
468602 if ( schema . properties ) {
469603 Object . entries ( schema . properties ) . forEach ( ( [ propertyName , propertySchema ] ) => {
604+ const isDiscriminator = discriminator ?. propertyName === propertyName ;
470605 if ( checkIsReference ( propertySchema ) ) {
471- return ;
606+ if ( ! isDiscriminator || ! discriminatorValue ) {
607+ return ;
608+ }
609+ }
610+
611+ let finalSchema = propertySchema ;
612+ if ( isDiscriminator && discriminatorValue ) {
613+ finalSchema = {
614+ ...propertySchema ,
615+ const : discriminatorValue ,
616+ enum : [ discriminatorValue ] ,
617+ } ;
472618 }
473619
474620 result . push ( {
475621 propertyName,
476622 required : Array . isArray ( schema . required )
477623 ? schema . required . includes ( propertyName )
478624 : undefined ,
479- isDiscriminatorProperty : discriminator ?. propertyName === propertyName ,
480- schema : propertySchema ,
625+ isDiscriminatorProperty : isDiscriminator ,
626+ schema : finalSchema ,
481627 } ) ;
482628 } ) ;
483629 }
0 commit comments