oapi-codegen icon indicating copy to clipboard operation
oapi-codegen copied to clipboard

Merging schema's with undefined properties doesn't work

Open thumbnail opened this issue 1 year ago • 2 comments

oapi-codegen isn't able to merge schema's which have incompatible properties, even if the properties aren't defined (!).

If a property needs to be overwritten, or should be defined at a later moment depending on context, it's common to use allOf + readOnly: true. Example:

allOf:
 - $ref '#/components/myField'
readOnly: true

Reproduce

In this example, I defined identityId, which is shared across payloads, but should /sometimes/ be set to readOnly.

config.yaml

package: identities
generate:
  client: true
  echo-server: true
  strict-server: true
  embedded-spec: false
  models: true
output: ./identities/identities.gen.go

identities.yaml

openapi: 3.0.0
info:
  title: test
  version: 0.0.0
paths:
  /identities/{identityId}:
    get:
      operationId: getIdentity
      parameters:
        - $ref: '#/components/parameters/PathIdentityId'
      responses:
        '200':
          description: get identity
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Identity'
    post:
      operationId: updateIdentity
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: '#/components/schemas/Identity'
                - type: object
                  properties:
                    identity_id: # setting identityId to readOnly, so it can't be changed/upserted.
                      readOnly: true
      parameters:
        - $ref: '#/components/parameters/PathIdentityId'
      responses:
        '200':
          description: get identity
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Identity'
components:
  parameters:
    PathIdentityId:
      in: path
      name: identityId
      schema:
        $ref: '#/components/schemas/IdentityId'
      required: true
  schemas:
    IdentityId:
      description: unique id of an identity
      type: string
      pattern: ^[ 0-9a-zA-Z- ]+$
      minLength: 1
      maxLength: 64
      example: 89e0ec57-4a7c-41a0-9433-970ba7709733
    Identity:
      type: object
      required:
        - identity_id
        - name
      properties:
        identity_id:
          $ref: '#/components/schemas/IdentityId'
        name:
          type: string

cmd

go run github.com/oapi-codegen/oapi-codegen/v2/cmd/[email protected] --config=./config/rest-gen/identities.yml ./internal/rest/identities/identities.yaml

throws the following error:

error generating code: error generating type definitions: error generating Go types for component schemas: error converting Schema User to Go type: error generating Go schema for property 'identity_id': error merging schemas: error merging schemas for AllOf: merging two schemas with different ReadOnly
exit status 1

Looking at the code, it seems that the reader is defaulting to false, and has no way to differentiate later if the property was set to false, or defaulted to false. https://github.com/oapi-codegen/oapi-codegen/blob/main/pkg/codegen/merge_schemas.go#L176-L178

thumbnail avatar Jan 15 '25 08:01 thumbnail

Hi

I have created "temporary fixes" that should work for some cases (but I don't think should ever be merged, as they break the openapi specs - I guess implementations such as the python ones do the same thing but that doesn't mean oapi-codegen should follow the same bad practice).

https://github.com/lukacat10/kin-openapi/tree/nullable-fields https://github.com/lukacat10/oapi-codegen/tree/nullable-fields

lukacat10 avatar Feb 15 '25 12:02 lukacat10

I ran into this as well, here's a minimal repo case:

openapi: 3.0.3
info:
  title: Minimal API
  version: "1.0.0"
paths:
  /get:
    get:
      summary: Get a value
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetResponse'
components:
  schemas:
    GetBaseResponse:
      type: object
      properties:
        value:
          type: string
      readOnly: true
    GetResponse:
      allOf:
        - $ref: '#/components/schemas/GetBaseResponse'
        - description: A description
          # oapi-codegen will fail if the following readOnly isn't set on this otherwise-empty schema
          readOnly: true  

emoses avatar Apr 23 '25 16:04 emoses