Skip to content

Nested and sibling oneOf's #1302

@max-kahnt-keylight

Description

@max-kahnt-keylight

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch docusaurus-theme-openapi-docs@4.7.1 for the project I'm working on.

The current implementation does not handle some of my configurations gracefully,
in particular this abstract setting

 allOf:
   - $ref: CommonProps.yaml
   - oneOf:
       - TypeA
       - TypeB
       - TypeC
      discriminator:
        propertyName: type
      mapping:
        type-a: TypeA
        type-b: TypeB

title: TypeA
allOf:
  - $ref: CommonTypeProps.yaml 
  - oneOf:
    - Mode1
    - Mode2
    discriminator
      propertyName: mode
    mapping:
      mode-1: Mode1
      mode-2: Mode2

title: TypeB
allOf:
  - $ref: CommonTypeProps.yaml 
  - oneOf:
    - Mode3
    - Mode4
    discriminator
      propertyName: mode
    mapping:
      mode-3: Mode3
      mode-4: Mode4
  
title: TypeC
oneOf: 
  - Mode1
  - Mode2
discriminator
  propertyName: mode
mapping:
  mode-1: Mode1
  mode-2: Mode2

(I am willing to update this abstract example to something more concrete if there is interest in it.)

For the outer allOf, the discriminator is disregarded by the current implementation since it is nested.
For the inner oneOf, the propertyName produces an error since the property definition is nested.

The respective resolution of oneOf use-cases appears to be very inconsistent to me.
I don't understand why discriminators nested in allOf constructs are handled any differently than plain ones? Merging the schemas unconditionally beforehand fixed the issue for my specific use-case.
Why do discriminated and non-discriminated oneOf's have signifiantly different render paths? Shouldn't they only differ in how the "rendered discriminator" either uses the explicit discriminator (hint) or some defaults extracted from the oneOf contributions instead while coinciding for all subsequent properties within the tabs? I was happy to see the recent changes regarding nested allOf/oneOf constructs and they seem to apply to my setting without discriminators. Can't both cases (oneOf's with and w/o discriminator) be treated in corresponding fashion?

My adjustments almost certainly are not ready for merge, in particular the empty object omission was necessary for cases where a oneOf consists of nothing but the discriminating value itself. However, I expect issues with the discriminator resolution changes as well, probably even affecting my current document - just that I haven't found the regressions yet.

Here is the diff that solved my problem:

diff --git a/node_modules/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx b/node_modules/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx
index 631c464..bca76bc 100644
--- a/node_modules/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx
+++ b/node_modules/docusaurus-theme-openapi-docs/src/theme/Schema/index.tsx
@@ -314,14 +314,15 @@ const Properties: React.FC<SchemaProps> = ({
   }
   if (Object.keys(schema.properties as {}).length === 0) {
     return (
-      <SchemaItem
-        collapsible={false}
-        name=""
-        required={false}
-        schemaName="object"
-        qualifierMessage={undefined}
-        schema={{}}
-      />
+      <></>
+      // <SchemaItem
+      //   collapsible={false}
+      //   name=""
+      //   required={false}
+      //   schemaName="object"
+      //   qualifierMessage={undefined}
+      //   schema={{}}
+      // />
     );
   }
 
@@ -434,6 +435,13 @@ interface DiscriminatorNodeProps {
   schemaType: "request" | "response";
 }
 
+function findProperty(schema: SchemaObject, propertyName: string) {
+  return schema.properties?.[propertyName] 
+    ?? schema.oneOf?.find(subschema => findProperty(subschema, propertyName))
+    ?? schema.allOf?.find(subschema => findProperty(subschema, propertyName))
+    ?? {};
+}
+
 const DiscriminatorNode: React.FC<DiscriminatorNodeProps> = ({
   discriminator,
   schema,
@@ -443,9 +451,7 @@ const DiscriminatorNode: React.FC<DiscriminatorNodeProps> = ({
   let inferredMapping: any = {};
 
   // default to empty object if no parent-level properties exist
-  const discriminatorProperty = schema.properties
-    ? schema.properties![discriminator.propertyName]
-    : {};
+  const discriminatorProperty = findProperty(schema, discriminator.propertyName);
 
   if (schema.allOf) {
     const mergedSchemas = mergeAllOf(schema) as SchemaObject;
@@ -997,6 +1003,8 @@ const SchemaNode: React.FC<SchemaProps> = ({
     return null;
   }
 
+  schema = schema.allOf ? mergeAllOf(schema) as SchemaObject : schema;
+
   if (schema.discriminator) {
     const { discriminator } = schema;
     return (

I would be happy to push this topic towards an eventual PR merge, obtaining and providing feedback in the process, if everybody's time allows :)

This issue body was partially generated by patch-package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions