Add support for shared/top-level parameters (dependencies, tags, etc) (#2434)

*  Add Default and DefaultPlaceholder data structures

to handle defaults and overrides

*  Add utils to get values by priority handling DefaultPlaceholders

*  Add support for top-level parameters in FastAPI, APIRouter, include_router

including: prefix, tags, dependencies, deprecated, include_in_schema, responses, default_response_class, callbacks

* ♻️ Update openapi utils to handle DefaultPlaceholder for response_class

* 📝 Update bigger-application example code to use top-level params

and showcase them in APIRouter, FastAPI, include_router

* 📝 Update docs for Bigger Applications, include diagrams, top-level params

* 🔥 Simplify code and docs for callbacks as default_response_class is no longer required

* 📝 Add docs for top-level dependencies, in FastAPI()

* 📝 Add docs reference to top-level dependencies in docs for decorator

*  Update/increase tests for Bigger Applications including shared parameters

*  Add tests for top-level dependencies in FastAPI()

*  Add tests for internal DefaultPlaceholder

*  Update/increase tests for callbacks with top-level parameters

*  Add LOTS of tests covering branches and cases for shared parameters

in top-level FastAPI, path operations, include_router, APIRouter, its path operations, nested include_router, nested APIRouter, and its path operations

* 🎨 Format/reorder parameters for consistency in FastAPI, APIRouter, include_router
This commit is contained in:
Sebastián Ramírez
2020-11-29 18:32:18 +01:00
committed by GitHub
parent d550738fa2
commit 313bbe802f
26 changed files with 7807 additions and 308 deletions

View File

@@ -0,0 +1,11 @@
from fastapi import Header, HTTPException
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def get_query_token(token: str):
if token != "jessica":
raise HTTPException(status_code=400, detail="No Jessica token provided")

View File

View File

@@ -0,0 +1,8 @@
from fastapi import APIRouter
router = APIRouter()
@router.post("/")
async def update_admin():
return {"message": "Admin getting schwifty"}

View File

@@ -1,20 +1,23 @@
from fastapi import Depends, FastAPI, Header, HTTPException
from fastapi import Depends, FastAPI
from .dependencies import get_query_token, get_token_header
from .internal import admin
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app = FastAPI(dependencies=[Depends(get_query_token)])
app.include_router(users.router)
app.include_router(items.router)
app.include_router(
items.router,
prefix="/items",
tags=["items"],
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
responses={418: {"description": "I'm a teapot"}},
)
@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

View File

@@ -1,16 +1,28 @@
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter, Depends, HTTPException
router = APIRouter()
from ..dependencies import get_token_header
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}
@router.get("/")
async def read_items():
return [{"name": "Item Foo"}, {"name": "item Bar"}]
return fake_items_db
@router.get("/{item_id}")
async def read_item(item_id: str):
return {"name": "Fake Specific Item", "item_id": item_id}
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}
@router.put(
@@ -19,6 +31,8 @@ async def read_item(item_id: str):
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "foo":
raise HTTPException(status_code=403, detail="You can only update the item: foo")
return {"item_id": item_id, "name": "The Fighters"}
if item_id != "plumbus":
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}

View File

@@ -5,7 +5,7 @@ router = APIRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Foo"}, {"username": "Bar"}]
return [{"username": "Rick"}, {"username": "Morty"}]
@router.get("/users/me", tags=["users"])

View File

@@ -0,0 +1,25 @@
from fastapi import Depends, FastAPI, Header, HTTPException
async def verify_token(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
async def verify_key(x_key: str = Header(...)):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])
@app.get("/items/")
async def read_items():
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]
@app.get("/users/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]

View File

@@ -1,7 +1,6 @@
from typing import Optional
from fastapi import APIRouter, FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel, HttpUrl
app = FastAPI()
@@ -23,7 +22,7 @@ class InvoiceEventReceived(BaseModel):
ok: bool
invoices_callback_router = APIRouter(default_response_class=JSONResponse)
invoices_callback_router = APIRouter()
@invoices_callback_router.post(