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

additionalProperties and OneOf at the same time

Open B-Lorentz opened this issue 3 years ago • 1 comments

The following spec:

openapi: 3.0.1

info:
  title: OpenAPI-CodeGen Test
  description: 'This is a test OpenAPI Spec'
  version: 1.0.0

servers:
  - url: https://test.oapi-codegen.com/v2
  - url: http://test.oapi-codegen.com/v2

paths:
  /cat:
    get:
      tags:
        - cat
      summary: Get cat status
      operationId: getCatStatus
      responses:
        200:
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Cat'
                

components:
  schemas:
    Cat:
      additionalProperties: true
      discriminator:
        mapping:
          alive: '#/components/schemas/CatAlive'
          dead: '#/components/schemas/CatDead'
        propertyName: state
      oneOf:
       - $ref: '#/components/schemas/CatAlive'
       - $ref: '#/components/schemas/CatDead'
    
    CatAlive:
      properties:
        state:
          type: string
          enum: [alive]
        name:
          type: string
        alive_since:
          type: string
          format: date-time
      required:
       - state

    CatDead:
      properties:
        state:
          type: string
          enum: [dead]
        name:
          type: string
        dead_since:
          type: string
          format: date-time
        cause:
          type: string
          enum: [car, dog, oldage, quantum-experiment]
      required:
       - state

Generates invalid code, because both the additionalProperties and the OneOf logic both generates a separate implementation of MarshallJSON and UnmarshallJSON, eg:

// Override default JSON handling for Cat to handle AdditionalProperties
func (a *Cat) UnmarshalJSON(b []byte) error {
	object := make(map[string]json.RawMessage)
	err := json.Unmarshal(b, &object)
	if err != nil {
		return err
	}

	if len(object) != 0 {
		a.AdditionalProperties = make(map[string]interface{})
		for fieldName, fieldBuf := range object {
			var fieldVal interface{}
			err := json.Unmarshal(fieldBuf, &fieldVal)
			if err != nil {
				return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
			}
			a.AdditionalProperties[fieldName] = fieldVal
		}
	}
	return nil
}

vs

func (t *Cat) UnmarshalJSON(b []byte) error {
	err := t.union.UnmarshalJSON(b)
	return err
}

Is this case supposed to be supported? I didn't find a clear statement of open-api spec saying that yes, this case is positively supported, but https://editor.swagger.io seems to understand it: image However, one can work around this by setting additionalProperties on the individual oneOf elements, so maybe it isn't that high-priority overall.

B-Lorentz avatar Sep 26 '22 14:09 B-Lorentz

I've prepared a potential fix: https://github.com/deepmap/oapi-codegen/pull/765

B-Lorentz avatar Sep 30 '22 11:09 B-Lorentz