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

Additional Responses #97

Merged
merged 10 commits into from
Apr 5, 2019
Merged

Additional Responses #97

merged 10 commits into from
Apr 5, 2019

Conversation

barsi
Copy link
Contributor

@barsi barsi commented Mar 22, 2019

Additional Responses Support

Summary

allows additional responses to be defined beside the default one

Example

# -*- coding: utf-8 -*-
import uuid, datetime as dt

import fastapi
from starlette import responses
import pydantic as dantic

from fastapi.openapi import models, constants

app = fastapi.FastAPI(debug=True)


class Response400(dantic.BaseModel):
    '''HTTP 4xx responses schema'''
    summary: str
    description: str
    ref_code: str # functional error ref


class Point(dantic.BaseModel):
    id: dantic.UUID1
    latitude: float
    longitude: float
    pinned: bool
    pinned_at: dt.datetime


response_403 = models.AdditionalResponse(
    status_code=403,
    description='Forbidden',
    models=[
        Response400,
    ],
)

response_500 = models.AdditionalResponse(
    status_code=500,
    description='Server Error',
    content_type='text/plain',
)


@app.post(
    '/pin-my-location',
    summary='Pin My Location',
    description='Pin my location ...',
    response_model=Point,
    additional_responses=[
        response_403,
        response_500,
    ])
async def pin_my_location():
    if dt.datetime.utcnow().strftime('%Y-%m') != '2019-03':
        return responses.UJSONResponse({
            'summary': 'Access Denied!',
            'detail': 'No Access allowed after Mar 2019.',
            'ref_code': 'TNQ00723',
        },
        status_code=403,)
    return Point(
        id=uuid.uuid1(7, 3),
        latitude=0.0,
        longitude=0.0,
        pinned=True,
        pinned_at=dt.datetime.utcnow(),
    )

Result

Screen Shot 2019-03-22 at 10 44 42 PM

you can also define additional responses on app.include_router so it can save you from repeating the definition of common response types (e.g. HTTP 500)

Limitations

This pull request only implements the injection of
additional responses definitions into OpenAPI schema
to be appeared in swagger or other OpenAPI 3 compatible
clients (e.g. postman), here are some of the limitations:

  • No validation on additional responses if they were returned.
  • No support for multiple media types in response content definition.
  • Extra fields on response not implemented yet (e.g. headers & examples).

@codecov
Copy link

codecov bot commented Mar 22, 2019

Codecov Report

Merging #97 into master will decrease coverage by 0.79%.
The diff coverage is 83.57%.

Impacted file tree graph

@@           Coverage Diff            @@
##           master     #97     +/-   ##
========================================
- Coverage     100%   99.2%   -0.8%     
========================================
  Files         109     110      +1     
  Lines        2740    2877    +137     
========================================
+ Hits         2740    2854    +114     
- Misses          0      23     +23
Impacted Files Coverage Δ
fastapi/openapi/utils.py 100% <100%> (ø) ⬆️
tests/test_additional_responses.py 100% <100%> (ø)
fastapi/openapi/models.py 100% <100%> (ø) ⬆️
fastapi/applications.py 100% <100%> (ø) ⬆️
fastapi/utils.py 100% <100%> (ø) ⬆️
fastapi/routing.py 86.85% <50%> (-13.15%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9778542...95679ca. Read the comment docs.

@codecov
Copy link

codecov bot commented Mar 22, 2019

Codecov Report

Merging #97 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff           @@
##           master    #97    +/-   ##
======================================
  Coverage     100%   100%            
======================================
  Files         109    146    +37     
  Lines        2740   3590   +850     
======================================
+ Hits         2740   3590   +850
Impacted Files Coverage Δ
fastapi/openapi/utils.py 100% <100%> (ø) ⬆️
fastapi/routing.py 100% <100%> (ø) ⬆️
...rial/test_additional_responses/test_tutorial004.py 100% <100%> (ø)
docs/src/bigger_applications/app/routers/items.py 100% <100%> (ø) ⬆️
docs/src/additional_responses/tutorial002.py 100% <100%> (ø)
...est_tutorial/test_bigger_applications/test_main.py 100% <100%> (ø) ⬆️
docs/src/additional_responses/tutorial003.py 100% <100%> (ø)
...rial/test_additional_responses/test_tutorial003.py 100% <100%> (ø)
...rial/test_additional_responses/test_tutorial001.py 100% <100%> (ø)
...rial/test_additional_responses/test_tutorial002.py 100% <100%> (ø)
... and 52 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9778542...2bd7759. Read the comment docs.

@tiangolo
Copy link
Member

Thanks! I'll review it soon.

@tiangolo tiangolo merged commit ad47130 into fastapi:master Apr 5, 2019
@tiangolo
Copy link
Member

tiangolo commented Apr 5, 2019

Thanks @barsi, I see you put quite some effort into this, thanks for that! 👏 🍰


I took it further and updated/refactored it all to make it simpler for developers to use while extending the capabilities.

For example, it's possible to add more info to an existing response_model, like example. And they get the data from the additional response and the model combined.

I also included docs, tests for the code in the docs, etc.

With this you can now add a responses parameter with a dict, e.g.:

from fastapi import FastAPI
from pydantic import BaseModel
from starlette.responses import JSONResponse


class Item(BaseModel):
    id: str
    value: str


class Message(BaseModel):
    message: str


app = FastAPI()


@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
    if item_id == "foo":
        return {"id": "foo", "value": "there goes my hero"}
    else:
        return JSONResponse(status_code=404, content={"message": "Item not found"})

The new docs are here: https://fastapi.tiangolo.com/tutorial/additional-responses/

@barsi
Copy link
Contributor Author

barsi commented Apr 5, 2019

@tiangolo Thanks for the brilliant & carefully crafted framework 🥇 , glad to be a contributor 👍

@tiangolo
Copy link
Member

tiangolo commented Apr 5, 2019

Thanks! 😊 🌮 🍰

lmignon pushed a commit to acsone/fastapi that referenced this pull request Sep 19, 2024
Signed-off-by lmignon
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants