connexion
connexion copied to clipboard
Allow customization of validator / Apply defaults in request body
Description
I need Connexion to use default value if a property is missing in request body. Although it is done with URL query, in body 'default' is just for documentation purposes (that is nature of JSON Schema). In order to enforce defaults (as described here https://python-jsonschema.readthedocs.io/en/latest/faq/#why-doesn-t-my-schema-that-has-a-default-property-actually-set-the-default-on-my-instance) I need to overwrite validator used in connexion/decorators/validation.py
. It would be even better if Connexion offered defaults enforcement on its own.
Expected bahaviour
- Access to body validation class in similar way as we have it with Resolver.
OR
- A flag to set 'defaults enforcement'. When 'defaults' is defined for a non-required property and the property is not passed, default value should be used.
Actual behaviour
We don't have tools for that. When non-required property is not passed, it is not included in data provided for controlled function, even if there are defaults.
Example
...
post:
operationId: "controllers.post_foo"
parameters:
- in: "body"
name: "foo"
required: true
schema:
$ref: "#/definitions/Foo"
responses:
200:
description: "OK"
definitions:
Foo:
type: "object"
properties:
name:
type: "string"
default: "BOB"
If -d '{}'
was sent, controller should receive {'name': 'BOB'}
.
Another example
It should be enough to provide defaults in bottom-most layer.
definitions:
Foo:
type: "object"
default: {}
properties:
name:
type: "object"
properties:
first:
type: "string"
default: "BOB"
last:
type: "string"
default: "DOE"
If -d '{}'
was sent, controller should receive {'name': {'first': 'BOB', 'last': 'DOE'}}
.
I think it could be enough to keep validation_class
as Request/ResponseBodyValidator
's attribute, RequestBodyValidator
as a Operation
's attribute and Operation
as an Api
's attribute (as we do with Resolver
). I will take a look at that and hopefully post a draft of needed changes.
@Ruwann you can use a custom validator since #352. I don't think we should add a flag to add defaults to Connexion. I would close this one if you agree.
Yes, indeed. And there is also an example about how to use default values. However, I was considering adding a flag to enable this behaviour because for query parameters, we do provide the default values:
https://github.com/zalando/connexion/blob/803cf638785223b3a9321c4ad5bbe371a61e38b6/connexion/operations/swagger2.py#L257-L259
https://github.com/zalando/connexion/blob/803cf638785223b3a9321c4ad5bbe371a61e38b6/connexion/operations/openapi.py#L352-L362
When in the code myself, it was this that appears to start with the defaults and update the dict with what the request has.
I'd have to get in there with the debugger/logging to figure out why that doesn't cause the desired behavior... unless you already know.
I think this only takes the default at the top level of the body and doesn't take into account any nested defaults, https://github.com/spec-first/connexion/blob/87a0fed4dcd164eb779cd11afd838845910b1e32/connexion/operations/openapi.py#L297
FWIW, I just stumbled on a flaw with the approach suggested in some of the other issue related to this (https://github.com/spec-first/connexion/blob/main/examples/swagger2/enforcedefaults/enforcedefaults.py)
If you have a schema with oneOf, the defaults for every type contained within the oneOf will be set. This is because the jsonschema validator invokes the validators for each instance of oneOf, without regard for the provided discriminator value.
I considered seeing if there was a way to strip out the validators which didn't match the provided discrinator, but it appears the the necessary context (mapping the type to the validator) is missing at the point where the validation occurs.
In particular, the schema has nothing like class_name
for well-typed objects. With that, I think I could make this work for oneOf polymorphism.
Fixed since https://github.com/spec-first/connexion/pull/1616