apple / swift-openapi-generator

Generate Swift client and server code from an OpenAPI document.
https://swiftpackageindex.com/apple/swift-openapi-generator/documentation
Apache License 2.0
1.43k stars 116 forks source link

Assigning type by matching "pattern" #624

Closed niklassaers closed 1 month ago

niklassaers commented 1 month ago

Question

Hi, an API I'm using uses inheritance on objects and has many objects that are so similar that it's hard for swift-openapi-generator to get the correct result back. In the example below, it can easily confuse LoginPage and SearchPage.

However, each object contains a property "type" that matches a pattern (well, it's a constant pattern) so that the type HomePage has type == "HomePage", the type SearchPage has type == "SearchPage" and the type LoginPage has type == "LoginPage". When the JSON is being parsed into types, though, this pattern doesn't seem to be taken into account. Is that right? Is there anything I can do to make it take this "pattern" into account?

I've done my best to recreate the API as a better readable example:

openapi: 3.1.0
info:
  title: Page API
  description: API for getting pages
  version: 1.0.0
paths:
  /api/v1/content/pages:
    get:
      tags:
        - Content
      summary: Page content; loaded inside frame
      operationId: getPages
      parameters:
        - name: path
          in: query
          description: The slug to identify the content for a page
          required: true
          schema:
            type: string
          examples:
            default:
              value: "/"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/HomePage"
                  - $ref: "#/components/schemas/SearchPage"
                  - $ref: "#/components/schemas/LoginPage"
        "404":
          description: Not Found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotFoundPage"

components:
  schemas:
    HomePage:
      type: object
      allOf:
        - $ref: "#/components/schemas/ModulePageBase"
        - type: object
          properties:
            type:
              pattern: HomePage
              type: string
            id:
              type: string
              examples:
                - "691d30b2-aba0-463b-932f-393ec3c1d248"
            hideFromRobots:
              type: boolean
            description:
              type: string
            referenceTitle:
              type: string
            title:
              type: string
            referenceImage:
              $ref: "#/components/schemas/Image"
            referenceDescription:
              type: string
            robotsTxt:
              type: string
            url:
              type: string
      required:
        - type
        - id
        - modules

    SearchPage:
      type: object
      allOf:
        - $ref: "#/components/schemas/PageBase"
        - type: object
          properties:
            noResultText:
              type: string
            type:
              pattern: SearchPage
              type: string
      required:
        - type

    LoginPage:
      type: object
      allOf:
        - $ref: "#/components/schemas/PageBase"
        - type: object
          properties:
            type:
              pattern: LoginPage
              type: string
      required:
        - type

    ModulePageBase:
      type: object
      allOf:
        - $ref: "#/components/schemas/PageBase"
        - type: object
          properties:
            type:
              type: string
            modules:
              type: array
              minItems: 1
              items:
                anyOf:
                  - $ref: "#/components/schemas/HeroModule"
                  - $ref: "#/components/schemas/ImageModule"
                  - $ref: "#/components/schemas/VideoModule"
                  - $ref: "#/components/schemas/ImageAndTextModule"
                  - $ref: "#/components/schemas/RichTextModule"
      required:
        - type
        - modules

    PageBase:
      type: object
      properties:
        type:
          type: string
      required:
        - type

    # Module definitions
    HeroModule:
      type: object
      required:
        - type
        - image
      properties:
        callToAction:
          $ref: "#/components/schemas/Link"
        headline:
          type: string
        subHeadline:
          type: string
        image:
          $ref: "#/components/schemas/Image"
        type:
          pattern: HeroModule
          type: string

    ImageModule:
      type: object
      required:
        - type
        - image
      properties:
        caption:
          description: "Text shown beside the image on the site to ie a label"
          type: string
        image:
          $ref: "#/components/schemas/Image"
        type:
          pattern: ImageModule
          type: string

    VideoModule:
      type: object
      required:
        - type
        - videoUrl
        - thumbnail
      properties:
        videoUrl:
          type: string
        thumbnail:
          $ref: "#/components/schemas/Image"
        type:
          pattern: VideoModule
          type: string

    ImageAndTextModule:
      type: object
      required:
        - type
        - image
      properties:
        callToAction:
          $ref: "#/components/schemas/Link"
        headline:
          type: string
        richText:
          type: string
        image:
          $ref: "#/components/schemas/Image"
        type:
          pattern: ImageAndTextModule
          type: string

    RichTextModule:
      type: object
      required:
        - richText
        - type
      properties:
        headline:
          type: string
        richText:
          type: string
        secondaryColumn:
          type: string
        type:
          pattern: RichTextModule
          type: string

    # Additional schemas
    Image:
      type: object
      required:
        - type
        - src
        - width
        - height
      properties:
        type:
          type: string
        src:
          type: string
        width:
          type: integer
        height:
          type: integer
        alt:
          type: string

    Link:
      type: object
      required:
        - url
        - title
      properties:
        url:
          type: string
        title:
          type: string
        target:
          type: string

    NotFoundPage:
      type: object
      allOf:
        - $ref: "#/components/schemas/PageBase"
        - type: object
          properties:
            type:
              pattern: NotFoundPage
              type: string
czechboy0 commented 1 month ago

Yes, check out the discriminator feature of OpenAPI: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#discriminator-object

niklassaers commented 1 month ago

Thank you so much @czechboy0 , discriminator did the trick :-)