Description
Description
When an API object has a field that is required but has readOnly: true, a request containing an object without that field fails to validate.
OpenAPI spec says:
You can use the
readOnly
andwriteOnly
keywords to mark specific properties as read-only or write-only. This is useful, for example, when GET returns more properties than used in POST – you can use the same schema in both GET and POST and mark the extra properties asreadOnly
.readOnly
properties are included in responses but not in requests, [...]
If a
readOnly
orwriteOnly
property is included in the required list, required affects just the relevant scope – responses only or requests only. That is, read-only required properties apply to responses only, [...]
This is a regression from Connexion 2.x.
Expected behaviour
Object validates successfully.
Actual behaviour
Object fails to validate, because it is missing a read-only field:
ERROR:connexion.validators.json:Validation error: 'objectId' is a required property
ERROR:connexion.middleware.exceptions:BadRequestProblem(status_code=400, detail="'objectId' is a required property")
Presumably Connexion 3.x fails to use jsonschema validation properly here.
Steps to reproduce
- Use the openapi.yaml and testcontroller.py from below
- Start with
connexion run openapi.yaml --stub
- Attempt to do the following request: PUT .../objects/45d2847a-ed55-440a-bfa7-5e759d0d671f, with body
{
"name": "abc"
}
Minimal example:
openapi.yaml
openapi: 3.0.3
info:
title: test
version: '1.0'
paths:
'/objects/{objectId}':
parameters:
- schema:
type: string
format: uuid
name: objectId
in: path
required: true
put:
summary: Create or update object
operationId: object_put
x-openapi-router-controller: testcontroller
responses:
'200':
description: OK - Object updated
content:
application/json:
schema:
$ref: '#/components/schemas/Object'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Object'
components:
schemas:
Object:
title: BaseObject
type: object
properties:
objectId:
type: string
format: uuid
readOnly: true
name:
type: string
required:
- objectId
- name
testcontroller.py
from connexion import FlaskApp, request
app = FlaskApp(__name__)
def object_put(objectId, body):
return {"objectId": objectId, **body}
app.add_api("openapi.yaml")
Additional info:
Output of the commands:
python --version
Python 3.11.3
pip show connexion | grep "^Version\:"
Version: 3.0.6