Skip to content

Commit 5b62b95

Browse files
committed
fix(theme): handle nested discriminators in allOf structures
Add support for discriminators nested within allOf schemas. Previously, discriminators inside allOf items were not properly detected, and the discriminator property lookup only checked top-level schema properties. Changes: - Add findProperty() helper to recursively search for properties in nested oneOf/anyOf/allOf structures - Update DiscriminatorNode to use findProperty() for discriminator property lookup - Update SchemaNode to check for discriminators in allOf items and merge schemas before processing - Return null instead of empty "object" SchemaItem when properties are empty after discriminator processing - Add test case for nested discriminator in allOf This fixes scenarios like: ```yaml allOf: - $ref: CommonProps - oneOf: [VariantA, VariantB] discriminator: propertyName: type ``` Fixes #1302
1 parent 6198c9b commit 5b62b95

2 files changed

Lines changed: 360 additions & 17 deletions

File tree

demo/examples/tests/discriminator.yaml

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,73 @@ paths:
350350
schema:
351351
$ref: "#/components/schemas/BaseEmptySubschema"
352352

353+
/discriminator-nested-allof:
354+
post:
355+
tags:
356+
- discriminator
357+
summary: Nested Discriminator in allOf
358+
description: |
359+
Tests discriminators nested within allOf structures, where the discriminator
360+
and its property definition are inside an allOf item rather than at the top level.
361+
This is a common pattern when combining shared properties with discriminated unions.
362+
363+
Schema:
364+
```yaml
365+
allOf:
366+
- $ref: '#/components/schemas/CommonProps'
367+
- oneOf:
368+
- $ref: '#/components/schemas/NestedVariantA'
369+
- $ref: '#/components/schemas/NestedVariantB'
370+
discriminator:
371+
propertyName: variantType
372+
mapping:
373+
variant-a: '#/components/schemas/NestedVariantA'
374+
variant-b: '#/components/schemas/NestedVariantB'
375+
```
376+
requestBody:
377+
required: true
378+
content:
379+
application/json:
380+
schema:
381+
$ref: "#/components/schemas/NestedDiscriminatorBase"
382+
responses:
383+
"200":
384+
description: Successful response
385+
content:
386+
application/json:
387+
schema:
388+
$ref: "#/components/schemas/NestedDiscriminatorBase"
389+
390+
/discriminator-doubly-nested:
391+
post:
392+
tags:
393+
- discriminator
394+
summary: Doubly Nested Discriminators (Issue #1302 Full Scenario)
395+
description: |
396+
Tests the full scenario from issue #1302 with doubly-nested discriminators:
397+
- Outer level: allOf with discriminator on "type" selecting TypeA/TypeB/TypeC
398+
- TypeA: has allOf with its own nested discriminator on "mode"
399+
- TypeB: has allOf with its own nested discriminator on "mode"
400+
- TypeC: has discriminator on "mode" at root level (NOT in allOf)
401+
402+
This tests:
403+
1. Discriminators nested in allOf are properly detected
404+
2. Inner discriminators work after selecting outer type
405+
3. TypeC pattern (discriminator at root) still works
406+
requestBody:
407+
required: true
408+
content:
409+
application/json:
410+
schema:
411+
$ref: "#/components/schemas/DoublyNestedBase"
412+
responses:
413+
"200":
414+
description: Successful response
415+
content:
416+
application/json:
417+
schema:
418+
$ref: "#/components/schemas/DoublyNestedBase"
419+
353420
components:
354421
schemas:
355422
BaseBasic:
@@ -569,3 +636,227 @@ components:
569636
type: object
570637
allOf:
571638
- $ref: "#/components/schemas/BaseEmptySubschema"
639+
640+
# Schemas for nested discriminator test
641+
CommonProps:
642+
type: object
643+
properties:
644+
id:
645+
type: string
646+
format: uuid
647+
description: Unique identifier
648+
createdAt:
649+
type: string
650+
format: date-time
651+
description: Creation timestamp
652+
required:
653+
- id
654+
655+
NestedDiscriminatorBase:
656+
allOf:
657+
- $ref: "#/components/schemas/CommonProps"
658+
- oneOf:
659+
- $ref: "#/components/schemas/NestedVariantA"
660+
- $ref: "#/components/schemas/NestedVariantB"
661+
discriminator:
662+
propertyName: variantType
663+
mapping:
664+
variant-a: "#/components/schemas/NestedVariantA"
665+
variant-b: "#/components/schemas/NestedVariantB"
666+
667+
NestedVariantA:
668+
type: object
669+
properties:
670+
variantType:
671+
type: string
672+
enum: ["variant-a"]
673+
configA:
674+
type: object
675+
properties:
676+
settingA1:
677+
type: string
678+
settingA2:
679+
type: integer
680+
required:
681+
- variantType
682+
683+
NestedVariantB:
684+
type: object
685+
properties:
686+
variantType:
687+
type: string
688+
enum: ["variant-b"]
689+
configB:
690+
type: object
691+
properties:
692+
settingB1:
693+
type: boolean
694+
settingB2:
695+
type: array
696+
items:
697+
type: string
698+
required:
699+
- variantType
700+
701+
# Test case for doubly-nested discriminators (issue #1302 full scenario)
702+
# Outer: allOf with discriminator on "type"
703+
# Inner: TypeA and TypeB each have their own discriminator on "mode"
704+
DoublyNestedBase:
705+
allOf:
706+
- $ref: "#/components/schemas/OuterCommonProps"
707+
- oneOf:
708+
- $ref: "#/components/schemas/OuterTypeA"
709+
- $ref: "#/components/schemas/OuterTypeB"
710+
- $ref: "#/components/schemas/OuterTypeC"
711+
discriminator:
712+
propertyName: type
713+
mapping:
714+
type-a: "#/components/schemas/OuterTypeA"
715+
type-b: "#/components/schemas/OuterTypeB"
716+
type-c: "#/components/schemas/OuterTypeC"
717+
718+
OuterCommonProps:
719+
type: object
720+
properties:
721+
id:
722+
type: string
723+
format: uuid
724+
name:
725+
type: string
726+
727+
# TypeA: has allOf with its own nested discriminator
728+
OuterTypeA:
729+
title: TypeA
730+
allOf:
731+
- $ref: "#/components/schemas/InnerCommonProps"
732+
- oneOf:
733+
- $ref: "#/components/schemas/ModeA1"
734+
- $ref: "#/components/schemas/ModeA2"
735+
discriminator:
736+
propertyName: mode
737+
mapping:
738+
mode-a1: "#/components/schemas/ModeA1"
739+
mode-a2: "#/components/schemas/ModeA2"
740+
properties:
741+
type:
742+
type: string
743+
enum: ["type-a"]
744+
required:
745+
- type
746+
747+
# TypeB: also has allOf with its own nested discriminator
748+
OuterTypeB:
749+
title: TypeB
750+
allOf:
751+
- $ref: "#/components/schemas/InnerCommonProps"
752+
- oneOf:
753+
- $ref: "#/components/schemas/ModeB1"
754+
- $ref: "#/components/schemas/ModeB2"
755+
discriminator:
756+
propertyName: mode
757+
mapping:
758+
mode-b1: "#/components/schemas/ModeB1"
759+
mode-b2: "#/components/schemas/ModeB2"
760+
properties:
761+
type:
762+
type: string
763+
enum: ["type-b"]
764+
required:
765+
- type
766+
767+
# TypeC: has discriminator at root (NOT in allOf)
768+
OuterTypeC:
769+
title: TypeC
770+
type: object
771+
oneOf:
772+
- $ref: "#/components/schemas/ModeC1"
773+
- $ref: "#/components/schemas/ModeC2"
774+
discriminator:
775+
propertyName: mode
776+
mapping:
777+
mode-c1: "#/components/schemas/ModeC1"
778+
mode-c2: "#/components/schemas/ModeC2"
779+
properties:
780+
type:
781+
type: string
782+
enum: ["type-c"]
783+
required:
784+
- type
785+
786+
InnerCommonProps:
787+
type: object
788+
properties:
789+
timestamp:
790+
type: string
791+
format: date-time
792+
793+
ModeA1:
794+
type: object
795+
properties:
796+
mode:
797+
type: string
798+
enum: ["mode-a1"]
799+
settingA1:
800+
type: string
801+
required:
802+
- mode
803+
804+
ModeA2:
805+
type: object
806+
properties:
807+
mode:
808+
type: string
809+
enum: ["mode-a2"]
810+
settingA2:
811+
type: number
812+
required:
813+
- mode
814+
815+
ModeB1:
816+
type: object
817+
properties:
818+
mode:
819+
type: string
820+
enum: ["mode-b1"]
821+
settingB1:
822+
type: boolean
823+
required:
824+
- mode
825+
826+
ModeB2:
827+
type: object
828+
properties:
829+
mode:
830+
type: string
831+
enum: ["mode-b2"]
832+
settingB2:
833+
type: array
834+
items:
835+
type: string
836+
required:
837+
- mode
838+
839+
ModeC1:
840+
type: object
841+
properties:
842+
mode:
843+
type: string
844+
enum: ["mode-c1"]
845+
settingC1:
846+
type: integer
847+
required:
848+
- mode
849+
850+
ModeC2:
851+
type: object
852+
properties:
853+
mode:
854+
type: string
855+
enum: ["mode-c2"]
856+
settingC2:
857+
type: object
858+
properties:
859+
nested:
860+
type: string
861+
required:
862+
- mode

0 commit comments

Comments
 (0)