kin-openapi
kin-openapi copied to clipboard
Incorect validation of nullable in allOf
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.
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
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.
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
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]