Skip to content

Weird YAML tag in section /components/examples of auto-generated OpenAPI spec file #774

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

Closed
EugeneDrm opened this issue Jul 8, 2020 · 5 comments
Labels
bug Something isn't working

Comments

@EugeneDrm
Copy link

The issue appears when a combination of tools is used:

  • spring-boot-starter-web:2.3.1.RELEASE
  • springdoc-openapi-ui:1.4.3
  • jackson:2.11.0 (comes with SpringBoot)

Example of auto-generated OpenAPI file with unwanted tag

....
componets:
  ....
  examples:
    umbrellaExample:
      value:
        object: !<Type A>
          type: TYPE_A
          name: x
          description: "y"

!<Type A> doesn't seem to be a valid YAML tag.
Swagger Online Editor tool also complains about this tag and refuses to render such YAML file.

There's a working example available at https://github.com/EugeneDrm/open-api-tag-bug

Key notes

  • there's an abstract class (AbstractObject) with one or more concrete implementations
  • the abstract class (AbstractObject) has a type field
  • annotation @JsonTypeInfo is used on the abstract class (AbstractObject) to properly deserialize concrete implementation(s) into abstract reference
  • there's a model class (Umbrella) which references the abstract class (AbstractObject)
  • an example object for model class (Umbrella) is defined in OpenAPI components/examples section via OpenAPI bean

Umbrella

public class Umbrella {
  @Schema(description = "This reference to abstract class causes weird YAML tag to be added", anyOf = ConcreteObjectA.class)
  private final AbstractObject object;
  ...
}

AbstractObject

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXISTING_PROPERTY,
    property = "type",
    visible = true
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = ConcreteObjectA.class, name = "Type A")
})
public abstract class AbstractObject {
  private final ConcreteType type;
  private final String name;
  ...
}

Controller

@RestController
public class Controller {
  @Operation(summary = "Test Bug", responses = {
      @ApiResponse(responseCode = "200", description = "OK",
          content = @Content(
              schema = @Schema(implementation = Umbrella.class),
              examples = @ExampleObject(ref = "#/components/examples/umbrellaExample", name = "Example with weird YAML tag")
          )
      )
  })
  @GetMapping(value = "/bug", produces = MediaType.APPLICATION_JSON_VALUE)
  public Umbrella bug() {
    return new Umbrella(new ConcreteObjectA("a", "b"));
  }
}

Application

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Bean
  public OpenAPI openApi() {
    return new OpenAPI()
        .components(new Components()
            .addExamples("umbrellaExample", new Example().value(new Umbrella(new ConcreteObjectA("x", "y"))))
        );
  }
}

Expected behavior

  • No YAML tag is generated in the examples section
@bnasslahsen
Copy link
Collaborator

@EugeneDrm,

This is the generated OpenAPI description with your code: There is no wired tag. Additionnaly, springdoc internally doesn't generate any of these tags. It mainly relies on swagger-core and jackson for json/yaml generation.

openapi: 3.0.1
info:
  title: OpenAPI definition
  version: v0
servers:
  - url: 'http://localhost:8080'
    description: Generated server url
paths:
  /bug:
    get:
      tags:
        - controller
      summary: Test Bug
      operationId: bug
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Umbrella'
              examples:
                Example with weird YAML tag:
                  $ref: '#/components/examples/umbrellaExample'
components:
  schemas:
    AbstractObject:
      type: object
      properties:
        type:
          type: string
          enum:
            - TYPE_A
            - TYPE_B
        name:
          type: string
      description: This reference to abstract class causes weird YAML tag to be added
      discriminator:
        propertyName: type
      anyOf:
        - $ref: '#/components/schemas/ConcreteObjectA'
    ConcreteObjectA:
      type: object
      allOf:
        - $ref: '#/components/schemas/AbstractObject'
        - type: object
          properties:
            description:
              type: string
    Umbrella:
      type: object
      properties:
        object:
          $ref: '#/components/schemas/AbstractObject'
  examples:
    umbrellaExample:
      value:
        object:
          type: TYPE_A
          name: x
          description: 'y'

Try to apply the following java code, to your type, to check the wired tag is present as well:

	ResolvedSchema resolvedSchema = ModelConverters.getInstance()
			.resolveAsResolvedSchema(
					new AnnotatedType(MyClass.class).resolveAsRef(false));

@EugeneDrm
Copy link
Author

@bnasslahsen, thank you for such a quick response!

I just tried generating the OpenAPI spec again, and still get the unwanted tag. Not really sure why you don't have it in your output. Have you used my project w/o any changes?

For a clean experiment, I just did this:

  • purged local maven repo
  • git clone of my example project
  • mvn verify
    I still get object: !<Type A> in file ./target/api-docs.yaml.

I also don't know what I should look for in the ResolvedSchema resolvedSchema. It looks fine to me. Any hint would be very appreciated.

With all that said, are you suggesting I should open this issue in swagger-core project? I think the jackson library should not affect how OpenAPI spec is generated, because jackson knows nothing about OpenAPI, right? So it's either springdoc-openapi, or some other underlying library related to openapi (one of the swagger-*).

Thanks for your help!

@bnasslahsen
Copy link
Collaborator

bnasslahsen commented Jul 8, 2020

Hi @EugeneDrm,

My message to use ResolvedSchema, is to look for the cause in swagger library.
This is the sample code to confirm it's an issue on swagger-core code.

OpenAPI openAPI = new OpenAPI()
		.components(new Components()
				.addExamples("umbrellaExample", new Example().value(new Umbrella(new ConcreteObjectA("x", "y"))))
		);
String result = Yaml.mapper().writeValueAsString(openAPI);
Assert.isTrue(!result.contains("!<Type A>"), "should not contain wired charachter");

You can submit your ticket on the swagger-project instead:

I think we can fin workaround for it in springodc. There is the following property that should be set: Feature.USE_NATIVE_TYPE_ID on the YamlFactory. But it's not accessible from swagger-api.

@EugeneDrm
Copy link
Author

@bnasslahsen thanks, this is very helpful.
I was able to run the code snippet and see that the issue is coming from swagger library.
I'll submit a new issue in swagger-core project. Closing this one.

@bnasslahsen
Copy link
Collaborator

@EugeneDrm,

Thanks to your clear description, a workaround will be availble on the next release of springdoc-openapi.
This fix is already available on the latest snapshot.

@bnasslahsen bnasslahsen added the bug Something isn't working label Jan 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants