swagger-api / swagger-parser

Swagger Spec to Java POJOs
http://swagger.io
Apache License 2.0
778 stars 525 forks source link

Inline schemas get empty model name when title is empty #2118

Open ludovicianul opened 1 month ago

ludovicianul commented 1 month ago
Description

When an inline schema has empty title, the resulted model has an empty name causing schema to not properly be resolved.

swagger-parser version

Version 2.1.22

OpenAPI declaration file content or url
openapi: "3.0.0"
info:
  version: 1.0.0
  title: Swagger Petstore
  description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification
  termsOfService: http://swagger.io/terms/
  contact:
    name: Swagger API Team
    email: apiteam@swagger.io
    url: http://swagger.io
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
  - url: http://petstore.swagger.io/api
paths:
  /pet-types:
    post:
      description: Creates a new pet in the store.  Duplicates are allowed
      operationId: addPet
      requestBody:
        description: Pet to add to the store
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MegaPet'
      responses:
        '200':
          description: pet response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MegaPet'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  responses:
    BadRequest:
      description: Request rejected due to a structural or business validation
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string
    MegaPet:
      properties:
        id:
          type: integer
          format: int64
        code:
          type: string
          format: byte
          example: MjIyMjIyMg==
        additionalCode:
          type: string
          format: binary
          example: MjIyMjIyMg==
        dateOfBirth:
          type: string
          format: date
          example: "2000-12-12"
        timeOfVaccination:
          type: string
          format: date-time
          example: 2012-01-24T15:54:14.876Z
        age:
          type: string
          pattern: "[0-9]+"
        owner:
          type: object
        previousOwner:
          type: object
          example: "prev owner"
        metadata:
          type: object
          maxProperties: 10
          additionalProperties:
            type: string
            maxLength: 256
        creator:
          title: ''
          description: ''
          type: object
          properties:
            id:
              description: ''
              type: string
              example: 63d415e0dd0d828c3a878548
              readOnly: true
            email:
              description: ''
              type: string
              example: fett@intergalactic.com
              readOnly: true
            displayName:
              description: ''
              type: string
              example: Boba Fett
              readOnly: true
Generation Details

Use the following code:

        OpenAPIParser openAPIV3Parser = new OpenAPIParser();
        ParseOptions options = new ParseOptions();
        options.setResolve(true);
        options.setFlatten(true);

        OpenAPI openAPI = openAPIV3Parser.readContents(Files.readString(Paths.get("openapi.yaml")), null, options).getOpenAPI();
        PathItem pathItem = openAPI.getPaths().get("/pet-types");

You can check the openAPI.getComponents.getSchemas() that it contains an empty model with an empty title and those 3 properties.

Screenshot 2024-08-12 at 22 12 02 Screenshot 2024-08-12 at 22 12 18
Steps to reproduce

See above

Suggest a fix

The issue seems to be this code from the io.swagger.v3.parser.util.InlineModelResolver class:

 private String resolveModelName(String title, String key) {
        if (title == null) {
            return uniqueName(key);
        } else {
            return uniqueName(title);
        }
    }

The fix is to test for empty strings also:

 private String resolveModelName(String title, String key) {
        if (StringUtils.isBlank(title)) {
            return uniqueName(key);
        } else {
            return uniqueName(title);
        }
    }
ponelat commented 3 weeks ago

This looks like expected behaviour. An empty string, is still a string. So it should prefer that. The workaround would be to not provide a title: '' at all. Is there more context here, @ludovicianul ?

@frantuma what happens when we serialize this back out, does that work as expected?

ludovicianul commented 3 weeks ago

It's still a string, but if all objects will have empty title, that will be a conflict. They will just override each other's schema. I suspect there might be frameworks out there that generate the OpenAPI file with empty title instead of null and setting empty title is not deliberate. My view is that null and empty should be treated the same in this particular case.

ponelat commented 2 weeks ago

@ludovicianul the uniqueName should ensure that multiple empty titles won't cause conflicts, even if they're not pretty. This is one of those messy things, empty titles aren't great and should likely never be produced from the start.

However, the goal of creating the names of schemas is to be helpful, not pure. And adding an empty string test can help things. Will try get the PR merged when we get a chance, thank you :)