kin-openapi icon indicating copy to clipboard operation
kin-openapi copied to clipboard

Incorect validation of nullable in allOf

Open pali opened this issue 5 years ago • 2 comments

When allOf contains two structures and both of them have defined "nullable" : true then validator should accept null value (as null value is valid for both defined structures).

But kin-openapi validator currently reject null value. Below is schema and test case.

{
    "openapi": "3.0.0",
    "info": {
        "version": "1.0.0",
        "title": "test"
    },
    "paths": {
        "/": {
            "get": {
                "responses": {
                    "200": {
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "value": {
                                            "$ref": "#/components/schemas/NULL"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components" : {
        "schemas" : {
            "NULL" : {
                "allOf" : [
                    {
                        "nullable" : true,
                        "type" : "boolean",
                        "enum" : [ false ]
                    },
                    {
                        "nullable" : true,
                        "type" : "boolean",
                        "enum" : [ true ]
                    }
                ]
            }
        }
    }
}

Test input: { "value" : null }. Validator should accept this input, but currently it rejects it.

Looks like that PR https://github.com/getkin/kin-openapi/pull/169 did not fixed this case.

pali avatar Feb 04 '20 14:02 pali

Same problem here:

I have this object that is used as a response:

    MyObject:
      type: object
      description: MyObject description
      properties:
        nullableRef:
          allOf:
            - $ref: '#/components/schemas/NotNullableObject'
            - nullable: true
            - description: nullableRef description
            - example: 5432
          required:
            - nullableRef
          additionalProperties: false

And when validating I'm geting the error: response body doesn't match the schema: Error at "nullableRef":Value is not nullable

arnaubennassar avatar Sep 18 '20 17:09 arnaubennassar

The example by @pali conflicts with https://github.com/OAI/OpenAPI-Specification/blob/master/proposals/003_Clarify-Nullable.md

nullable: true doesn't add "null" to the values of the enum and thus is not allowed. The allOf needs to look like this:

                "allOf" : [
                    {
                        "type" : "boolean",
                        "enum" : [ false ]
                    },
                    {
                        "type" : "boolean",
                        "enum" : [ true ]
                    },
                    {
                        "enum" : [ null ]
                    }
                ]

or simply:

                    {
                        "type" : "boolean",
                        "nullable": true
                    }

Still, we also have issues with allOf and nullable.

m-mohr avatar Nov 23 '20 13:11 m-mohr

I'm having a similar issue with validation of nullable values not working as I expect it to.

I have a few types declared that mostly shouldn't be null, but in a few cases should. I've tried to solve it using allOf with an anonymous, nullable type. Other tools accept this solution, but kin-openapi doesn't seem to.

I've created a minimal example showing my problem:

nullable_api.yml:

openapi: 3.0.1
info:
  title: "test"
  version: 0.0.0
paths:
  /not_nullable:
    get:
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotNullable"
  /nullable:
    get:
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/NotNullable"
                  - type: object
                    nullable: true
components:
  schemas:
    NotNullable:
      type: object
      properties:
        key:
          type: integer
          example: 1

I'd like to reuse the type NotNullable for both the endpoints, but for /nullable it should be nullable.

Here's an example program showing what I'm doing:

package main

import (
	"bytes"
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"

	"github.com/getkin/kin-openapi/openapi3"
	"github.com/getkin/kin-openapi/openapi3filter"
	"github.com/getkin/kin-openapi/routers/gorillamux"
)

func main() {
	const openAPISpec = "openapi/nullable_api.yml"
	ctx := context.Background()
	mockResponse := []byte(`null`)
	// mockResponse := []byte(`{"key": 1}`) // NOTE: it works with this body

	loader := &openapi3.Loader{Context: ctx, IsExternalRefsAllowed: true}
	doc, err := loader.LoadFromFile(openAPISpec)
	if err != nil {
		log.Fatalf("failed to load openapi spec: %s", err)
	}

	err = doc.Validate(ctx)
	if err != nil {
		log.Fatalf("failed to validate openapi spec: %s", err)
	}

	router, err := gorillamux.NewRouter(doc)
	if err != nil {
		log.Fatalf("failed to create gorilla router: %s", err)
	}

	httpReq := httptest.NewRequest(http.MethodGet, "http://127.0.0.1/nullable", nil)

	// Find route
	route, pathParams, err := router.FindRoute(httpReq)
	if err != nil {
		log.Fatalf("failed to find route: %s", err)
	}

	// Validate request
	requestValidationInput := &openapi3filter.RequestValidationInput{
		Request:    httpReq,
		PathParams: pathParams,
		Route:      route,
		Options: &openapi3filter.Options{
			AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
		},
	}
	err = openapi3filter.ValidateRequest(ctx, requestValidationInput)
	if err != nil {
		log.Fatalf("request not valid: %s", err)
	}

	// Validate response
	responseValidationInput := &openapi3filter.ResponseValidationInput{
		RequestValidationInput: requestValidationInput,
		Status:                 http.StatusOK,
		Header:                 http.Header{"Content-Type": []string{"application/json"}},
		Body:                   ioutil.NopCloser(bytes.NewReader(mockResponse)),
	}

	err = openapi3filter.ValidateResponse(ctx, responseValidationInput)
	if err != nil {
		log.Fatalf("response not valid: %s", err)
	}

	log.Printf("success!")
}

The above program outputs the following when I use null as the response:

response not valid: response body doesn't match the schema: Value is not nullable
Schema:
  {
    "allOf": [
      {
        "$ref": "#/components/schemas/NotNullable"
      },
      {
        "nullable": true,
        "type": "object"
      }
    ]
  }

Value:
  null

micvbang avatar Oct 25 '22 12:10 micvbang

I'm curious about the case of a nullable ref without allOf

For example the following:

openapi: "3.0.0"
info:
  version: 1.0.0
  title: Swagger Petstore
paths:
  /pet:
    get:
      description: |
        Returns pet from the system that the user has access to
      responses:
        '200':
          description: pet response
          content:
            application/json:
              required:
              schema:
                $ref: '#/components/schemas/Pet'
                nullable: "true"
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Pet:
      type: object
      required:
        - name
        - house
      properties:
        name:
          type: string
        tag:
          type: string
        house:
          nullable: true
          $ref: '#/components/schemas/House'

    House:
      type: object
      required:
        - address
      properties:
        address:
          type: string

    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string

Results in:

2023/05/25 10:48:59 Validation error: invalid paths: invalid path /pet: invalid operation GET: extra sibling fields: [nullable]

Kellel avatar May 25 '23 17:05 Kellel