Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AllOf references not resolved by parser, OneOf and AnyOf resolved correctly #1157

Closed
Leo11221 opened this issue Jul 19, 2019 · 4 comments
Closed

Comments

@Leo11221
Copy link

Leo11221 commented Jul 19, 2019

Hi, it seems that AllOf references are not being resolved, it seems to work for AnyOf and OneOf but not AllOf unless I'm not understanding it correctly, test case:

package com.workday.maya.swagger.generator

import com.workday.maya.logging.Slogger
import com.workday.maya.model.{Expression, ExpressionsMappingSource, Mapping, Path}
import com.workday.maya.test.UnitSpec
import io.swagger.v3.parser.OpenAPIV3Parser
import io.swagger.v3.parser.core.models.ParseOptions
import play.api.libs.json.Json

class AllOfTest extends UnitSpec with Slogger {

  val swaggerJsonGenerator = new SwaggerJsonGenerator

  behavior of "AllOf"

  val anyOfSchema = """openapi: 3.0.0
                      |paths:
                      |  /pets:
                      |      get:
                      |        description: "Test"
                      |        responses:
                      |          200:
                      |            description: Parameters missing or invalid
                      |            content:
                      |              application/json:
                      |                schema:
                      |                  $ref: '#/components/schemas/Pet'
                      |components:
                      |  schemas:
                      |    Dog:
                      |      type: object
                      |      properties:
                      |        bark:
                      |          type: boolean
                      |        breed:
                      |          type: string
                      |    Cat:
                      |      type: object
                      |      properties:
                      |        hunts:
                      |          type: boolean
                      |        age:
                      |          type: integer
                      |    Pet:
                      |      anyOf:
                      |        - $ref: '#/components/schemas/Cat'
                      |        - $ref: '#/components/schemas/Dog'
                      |""".stripMargin

  val oneOfSchema = """openapi: 3.0.0
                      |paths:
                      |  /pets:
                      |    get:
                      |      description: "Test"
                      |      responses:
                      |        200:
                      |          description: Parameters missing or invalid
                      |          content:
                      |            application/json:
                      |              schema:
                      |                $ref: '#/components/schemas/Pet'
                      |components:
                      |  schemas:
                      |    Dog:
                      |      type: object
                      |      properties:
                      |        bark:
                      |          type: boolean
                      |        breed:
                      |          type: string
                      |    Cat:
                      |      type: object
                      |      properties:
                      |        hunts:
                      |          type: boolean
                      |        age:
                      |          type: integer
                      |    Pet:
                      |      oneOf:
                      |        - $ref: '#/components/schemas/Cat'
                      |        - $ref: '#/components/schemas/Dog'""".stripMargin

  val allOfSchema = """openapi: 3.0.0
                      |paths:
                      |  /pets:
                      |    get:
                      |      description: "Test"
                      |      responses:
                      |        200:
                      |          description: Parameters missing or invalid
                      |          content:
                      |            application/json:
                      |              schema:
                      |                $ref: '#/components/schemas/Pet'
                      |components:
                      |  schemas:
                      |    Dog:
                      |      type: object
                      |      properties:
                      |        bark:
                      |          type: boolean
                      |        breed:
                      |          type: string
                      |    Cat:
                      |      type: object
                      |      properties:
                      |        hunts:
                      |          type: boolean
                      |        age:
                      |          type: integer
                      |    Pet:
                      |      allOf:
                      |        - $ref: '#/components/schemas/Cat'
                      |        - $ref: '#/components/schemas/Dog'""".stripMargin

  private val swaggerParserOptions = new ParseOptions
  swaggerParserOptions.setResolve(true)
  swaggerParserOptions.setResolveFully(true)


  it should "resolve references for allOf" in {
    val openApiAllOf = new OpenAPIV3Parser().readContents(allOfSchema, null, swaggerParserOptions)
    println("All of schema is" + openApiAllOf.getOpenAPI.getComponents.getSchemas.get("Pet"))
  }

  it should "resolve references for anyOf" in {
    val openApiAllOf = new OpenAPIV3Parser().readContents(anyOfSchema, null, swaggerParserOptions)
    println("Any of schema is" + openApiAllOf.getOpenAPI.getComponents.getSchemas.get("Pet"))
  }

  it should "resolve references for oneOf" in {
    val openApiAllOf = new OpenAPIV3Parser().readContents(oneOfSchema, null, swaggerParserOptions)
    println("One of schema is" + openApiAllOf.getOpenAPI.getComponents.getSchemas.get("Pet"))
  }
}

Output:

Activating test InMemoryLog4jAppender
All of schema isclass ComposedSchema {
    class Schema {
        type: null
        format: null
        $ref: null
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }
    allOf: [class Schema {
        type: null
        format: null
        $ref: #/components/schemas/Cat
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }, class Schema {
        type: null
        format: null
        $ref: #/components/schemas/Dog
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }]
    anyOf: null
    oneOf: null
}
Any of schema isclass ComposedSchema {
    class Schema {
        type: null
        format: null
        $ref: null
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }
    allOf: null
    anyOf: [class ObjectSchema {
        class Schema {
            type: object
            format: null
            $ref: null
            description: null
            title: null
            multipleOf: null
            maximum: null
            exclusiveMaximum: null
            minimum: null
            exclusiveMinimum: null
            maxLength: null
            minLength: null
            pattern: null
            maxItems: null
            minItems: null
            uniqueItems: null
            maxProperties: null
            minProperties: null
            required: null
            not: null
            properties: {hunts=class BooleanSchema {
                class Schema {
                    type: boolean
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }, age=class IntegerSchema {
                class Schema {
                    type: integer
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }}
            additionalProperties: null
            nullable: null
            readOnly: null
            writeOnly: null
            example: null
            externalDocs: null
            deprecated: null
            discriminator: null
            xml: null
        }
    }, class ObjectSchema {
        class Schema {
            type: object
            format: null
            $ref: null
            description: null
            title: null
            multipleOf: null
            maximum: null
            exclusiveMaximum: null
            minimum: null
            exclusiveMinimum: null
            maxLength: null
            minLength: null
            pattern: null
            maxItems: null
            minItems: null
            uniqueItems: null
            maxProperties: null
            minProperties: null
            required: null
            not: null
            properties: {bark=class BooleanSchema {
                class Schema {
                    type: boolean
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }, breed=class StringSchema {
                class Schema {
                    type: string
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }}
            additionalProperties: null
            nullable: null
            readOnly: null
            writeOnly: null
            example: null
            externalDocs: null
            deprecated: null
            discriminator: null
            xml: null
        }
    }]
    oneOf: null
}
One of schema isclass ComposedSchema {
    class Schema {
        type: null
        format: null
        $ref: null
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }
    allOf: null
    anyOf: null
    oneOf: [class ObjectSchema {
        class Schema {
            type: object
            format: null
            $ref: null
            description: null
            title: null
            multipleOf: null
            maximum: null
            exclusiveMaximum: null
            minimum: null
            exclusiveMinimum: null
            maxLength: null
            minLength: null
            pattern: null
            maxItems: null
            minItems: null
            uniqueItems: null
            maxProperties: null
            minProperties: null
            required: null
            not: null
            properties: {hunts=class BooleanSchema {
                class Schema {
                    type: boolean
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }, age=class IntegerSchema {
                class Schema {
                    type: integer
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }}
            additionalProperties: null
            nullable: null
            readOnly: null
            writeOnly: null
            example: null
            externalDocs: null
            deprecated: null
            discriminator: null
            xml: null
        }
    }, class ObjectSchema {
        class Schema {
            type: object
            format: null
            $ref: null
            description: null
            title: null
            multipleOf: null
            maximum: null
            exclusiveMaximum: null
            minimum: null
            exclusiveMinimum: null
            maxLength: null
            minLength: null
            pattern: null
            maxItems: null
            minItems: null
            uniqueItems: null
            maxProperties: null
            minProperties: null
            required: null
            not: null
            properties: {bark=class BooleanSchema {
                class Schema {
                    type: boolean
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }, breed=class StringSchema {
                class Schema {
                    type: string
                    format: null
                    $ref: null
                    description: null
                    title: null
                    multipleOf: null
                    maximum: null
                    exclusiveMaximum: null
                    minimum: null
                    exclusiveMinimum: null
                    maxLength: null
                    minLength: null
                    pattern: null
                    maxItems: null
                    minItems: null
                    uniqueItems: null
                    maxProperties: null
                    minProperties: null
                    required: null
                    not: null
                    properties: null
                    additionalProperties: null
                    nullable: null
                    readOnly: null
                    writeOnly: null
                    example: null
                    externalDocs: null
                    deprecated: null
                    discriminator: null
                    xml: null
                }
            }}
            additionalProperties: null
            nullable: null
            readOnly: null
            writeOnly: null
            example: null
            externalDocs: null
            deprecated: null
            discriminator: null
            xml: null
        }
    }]
}

Process finished with exit code 0
@scottr-ad
Copy link
Contributor

scottr-ad commented Aug 2, 2019

@Leo11221
I think the problem is that ResolverFully by default aggregates allOf into a new Schema object, which then gets replaced in the path response, but is not also replaced in the component schemas list.

You would also see the same problem if you had anyOf AND oneOf in the same Schema object, and they were by default combined into a new Schema.

I believe a suitable solution to this would be to modify the $ref resolution in ResolverFully.resolveSchema to put any resolved Schema back into the schemas map (replacing the original), as so:

ResolverFully.java

    public Schema resolveSchema(Schema schema) {
        if(schema.get$ref() != null) {
            String ref= schema.get$ref();
            ref = ref.substring(ref.lastIndexOf("/") + 1);
            Schema resolved = schemas.get(ref);

            if (resolved != null) {

                if (this.resolvedModels.containsKey(ref)) {
                    LOGGER.debug("avoiding infinite loop");
                    return resolvedModels.get(ref);
                }
                resolvedModels.put(ref, schema);
                Schema model = resolveSchema(resolved);

                // if we make it without a resolution loop, we can update the reference
                resolvedModels.put(ref, model);
                schemas.put(ref, model);   // ** Replace original schema with resolved **

                return model;

            }else {
                return schema;
            }
        }

scottr-ad pushed a commit to scottr-ad/swagger-parser that referenced this issue Aug 13, 2019
… with $ref with new aggregated Schema in schemas Map
@scottr-ad
Copy link
Contributor

Pull Request #1168 raised to implement solution suggested above. Could one of the regular contributors please review?

gracekarina added a commit that referenced this issue Aug 14, 2019
Issue #1157 - AllOf references not resolved by parser, OneOf and AnyOf resolved correctly
@gracekarina
Copy link
Contributor

Hi @Leo11221 for reporting this and @scottr-ad for raising the PR. please let us know if this issue is still happening after the merge.

@hiren-intellectdesign
Copy link

@gracekarina I am facing exactly the same issue in v1 (unable to resolve $ref's if allOf used)... can you point me to which version 1.x.x has this fix available? OR if not already, any plan to fix it soon?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants