Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforcedefaults aiohttp #1163

Merged
merged 3 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion connexion/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ def __init__(self,

@property
def json(self):
return self.json_getter()
if not hasattr(self, '_json'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change is unrelated to your example, why did you add it?

Copy link
Contributor Author

@p4l1ly p4l1ly Feb 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is related. Actually, it fixes the issue #1162. As opposed to FlaskApp, this code says that the body is recomputed each time we access ConnexionRequest.json. So if a validator changes the json, it is not reflected in the body argument because here it is recomputed again. This change avoids the recomputation and uses the same reference (which has been modified by the validator) again.

Copy link
Contributor Author

@p4l1ly p4l1ly Feb 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really, please try the example without this change and see that it behaves differently (the foo attribute is not added with the default value).

self._json = self.json_getter()
return self._json


class ConnexionResponse:
Expand Down
16 changes: 16 additions & 0 deletions examples/openapi3/enforcedefaults_aiohttp/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
========================
Custom Validator Example
========================

In this example we fill-in non-provided properties with their defaults.
Validator code is based on example from `python-jsonschema docs`_.

Running:

.. code-block:: bash

$ ./enforcedefaults.py

Now open your browser and go to http://localhost:8080/v1/ui/ to see the Swagger
UI. If you send a ``POST`` request with empty body ``{}``, you should receive
echo with defaults filled-in.
39 changes: 39 additions & 0 deletions examples/openapi3/enforcedefaults_aiohttp/enforcedefaults-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
openapi: '3.0.0'
info:
version: '1'
title: Custom Validator Example
servers:
- url: http://localhost:8080/{basePath}
variables:
basePath:
default: api
paths:
/echo:
post:
description: Echo passed data
operationId: enforcedefaults.echo
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Data'
responses:
'200':
description: Data with defaults filled in by validator
default:
description: Unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
components:
schemas:
Data:
type: object
properties:
foo:
type: string
default: foo
Error:
type: string
56 changes: 56 additions & 0 deletions examples/openapi3/enforcedefaults_aiohttp/enforcedefaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3

import connexion
import jsonschema
import six
from connexion.decorators.validation import RequestBodyValidator
from connexion.json_schema import Draft4RequestValidator


async def echo(body):
return body


# via https://python-jsonschema.readthedocs.io/
def extend_with_set_default(validator_class):
validate_properties = validator_class.VALIDATORS['properties']

def set_defaults(validator, properties, instance, schema):
for property, subschema in six.iteritems(properties):
if 'default' in subschema:
instance.setdefault(property, subschema['default'])

for error in validate_properties(
validator, properties, instance, schema):
yield error

return jsonschema.validators.extend(
validator_class, {'properties': set_defaults})

DefaultsEnforcingDraft4Validator = extend_with_set_default(Draft4RequestValidator)


class DefaultsEnforcingRequestBodyValidator(RequestBodyValidator):
def __init__(self, *args, **kwargs):
super(DefaultsEnforcingRequestBodyValidator, self).__init__(
*args, validator=DefaultsEnforcingDraft4Validator, **kwargs)


validator_map = {
'body': DefaultsEnforcingRequestBodyValidator
}


if __name__ == '__main__':
app = connexion.AioHttpApp(
__name__,
port=8080,
specification_dir='.',
options={'swagger_ui': True}
)
app.add_api(
'enforcedefaults-api.yaml',
arguments={'title': 'Hello World Example'},
validator_map=validator_map,
)
app.run()