pythonasyncioapiasyncfastapiframeworkjsonjson-schemaopenapiopenapi3pydanticpython-typespython3redocreststarletteswaggerswagger-uiuvicornweb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
594 lines
19 KiB
594 lines
19 KiB
from fastapi import FastAPI
|
|
from fastapi.openapi.models import AdditionalResponse
|
|
from pydantic import BaseModel
|
|
from starlette.responses import JSONResponse
|
|
from starlette.testclient import TestClient
|
|
|
|
app = FastAPI()
|
|
|
|
|
|
class Item(BaseModel):
|
|
name: str
|
|
price: float = None
|
|
|
|
|
|
class Response400(BaseModel):
|
|
'''HTTP 4xx Response Schema'''
|
|
title: str
|
|
detail: str
|
|
error_code: int # functional error ref
|
|
|
|
|
|
response_403 = AdditionalResponse(
|
|
status_code = 403,
|
|
description = 'Forbidden',
|
|
models = [
|
|
Response400,
|
|
],
|
|
)
|
|
|
|
additional_responses = [
|
|
response_403,
|
|
]
|
|
|
|
@app.api_route(
|
|
"/items/{item_id}",
|
|
methods=["GET"],
|
|
additional_responses=additional_responses,
|
|
)
|
|
def get_items(item_id: str):
|
|
return {"item_id": item_id}
|
|
|
|
|
|
def get_not_decorated(item_id: str):
|
|
return {"item_id": item_id}
|
|
|
|
|
|
app.add_api_route(
|
|
"/items-not-decorated/{item_id}",
|
|
get_not_decorated,
|
|
additional_responses=additional_responses,
|
|
)
|
|
|
|
|
|
@app.delete(
|
|
"/items/{item_id}",
|
|
additional_responses=additional_responses,
|
|
)
|
|
def delete_item(item_id: str, item: Item):
|
|
return {"item_id": item_id, "item": item}
|
|
|
|
|
|
@app.head(
|
|
"/items/{item_id}",
|
|
additional_responses=additional_responses,
|
|
)
|
|
def head_item(item_id: str):
|
|
return JSONResponse(headers={"x-fastapi-item-id": item_id})
|
|
|
|
|
|
@app.options(
|
|
"/items/{item_id}",
|
|
additional_responses=additional_responses,
|
|
)
|
|
def options_item(item_id: str):
|
|
return JSONResponse(headers={"x-fastapi-item-id": item_id})
|
|
|
|
|
|
@app.patch(
|
|
"/items/{item_id}",
|
|
additional_responses=additional_responses,
|
|
)
|
|
def patch_item(item_id: str, item: Item):
|
|
return {"item_id": item_id, "item": item}
|
|
|
|
|
|
@app.trace(
|
|
"/items/{item_id}",
|
|
additional_responses=additional_responses,
|
|
)
|
|
def trace_item(item_id: str):
|
|
return JSONResponse(media_type="message/http")
|
|
|
|
|
|
client = TestClient(app)
|
|
|
|
openapi_schema = {
|
|
"openapi": "3.0.2",
|
|
"info": {
|
|
"title": "Fast API",
|
|
"version": "0.1.0"
|
|
},
|
|
"paths": {
|
|
"/items/{item_id}": {
|
|
"get": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Get Items Get",
|
|
"operationId":
|
|
"get_items_items__item_id__get",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
},
|
|
"delete": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Delete Item Delete",
|
|
"operationId":
|
|
"delete_item_items__item_id__delete",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Item"
|
|
}
|
|
}
|
|
},
|
|
"required": True,
|
|
},
|
|
},
|
|
"options": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Options Item Options",
|
|
"operationId":
|
|
"options_item_items__item_id__options",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
},
|
|
"head": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Head Item Head",
|
|
"operationId":
|
|
"head_item_items__item_id__head",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
},
|
|
"patch": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Patch Item Patch",
|
|
"operationId":
|
|
"patch_item_items__item_id__patch",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
"requestBody": {
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Item"
|
|
}
|
|
}
|
|
},
|
|
"required": True,
|
|
},
|
|
},
|
|
"trace": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Trace Item Trace",
|
|
"operationId":
|
|
"trace_item_items__item_id__trace",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
},
|
|
},
|
|
"/items-not-decorated/{item_id}": {
|
|
"get": {
|
|
"responses": {
|
|
"200": {
|
|
"description": "Successful Response",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {}
|
|
}
|
|
},
|
|
},
|
|
"403": {
|
|
"description": "Forbidden",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref": "#/components/schemas/Response400"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
"422": {
|
|
"description": "Validation Error",
|
|
"content": {
|
|
"application/json": {
|
|
"schema": {
|
|
"$ref":
|
|
"#/components/schemas/HTTPValidationError"
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
"summary":
|
|
"Get Not Decorated Get",
|
|
"operationId":
|
|
"get_not_decorated_items-not-decorated__item_id__get",
|
|
"parameters": [{
|
|
"required": True,
|
|
"schema": {
|
|
"title": "Item_Id",
|
|
"type": "string"
|
|
},
|
|
"name": "item_id",
|
|
"in": "path",
|
|
}],
|
|
}
|
|
},
|
|
},
|
|
"components": {
|
|
"schemas": {
|
|
"Item": {
|
|
"title": "Item",
|
|
"required": ["name"],
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {
|
|
"title": "Name",
|
|
"type": "string"
|
|
},
|
|
"price": {
|
|
"title": "Price",
|
|
"type": "number"
|
|
},
|
|
},
|
|
},
|
|
"Response400": {
|
|
"title": "Response400",
|
|
"description": "HTTP 4xx Response Schema",
|
|
"required": ["title", "detail", "error_code"],
|
|
"type": "object",
|
|
"properties": {
|
|
"title": {
|
|
"title": "Title",
|
|
"type": "string"
|
|
},
|
|
"detail": {
|
|
"title": "Detail",
|
|
"type": "string"
|
|
},
|
|
"error_code": {
|
|
"title": "Error_Code",
|
|
"type": "integer"
|
|
},
|
|
},
|
|
},
|
|
"ValidationError": {
|
|
"title": "ValidationError",
|
|
"required": ["loc", "msg", "type"],
|
|
"type": "object",
|
|
"properties": {
|
|
"loc": {
|
|
"title": "Location",
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
},
|
|
"msg": {
|
|
"title": "Message",
|
|
"type": "string"
|
|
},
|
|
"type": {
|
|
"title": "Error Type",
|
|
"type": "string"
|
|
},
|
|
},
|
|
},
|
|
"HTTPValidationError": {
|
|
"title": "HTTPValidationError",
|
|
"type": "object",
|
|
"properties": {
|
|
"detail": {
|
|
"title": "Detail",
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/components/schemas/ValidationError"
|
|
},
|
|
}
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
|
|
|
|
def test_openapi_schema():
|
|
response = client.get("/openapi.json")
|
|
assert response.status_code == 200
|
|
assert response.json() == openapi_schema
|
|
|
|
|
|
def test_get_api_route():
|
|
response = client.get("/items/foo")
|
|
assert response.status_code == 200
|
|
assert response.json() == {"item_id": "foo"}
|
|
|
|
|
|
def test_get_api_route_not_decorated():
|
|
response = client.get("/items-not-decorated/foo")
|
|
assert response.status_code == 200
|
|
assert response.json() == {"item_id": "foo"}
|
|
|
|
|
|
def test_delete():
|
|
response = client.delete("/items/foo", json={"name": "Foo"})
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"item_id": "foo",
|
|
"item": {
|
|
"name": "Foo",
|
|
"price": None
|
|
}
|
|
}
|
|
|
|
|
|
def test_head():
|
|
response = client.head("/items/foo")
|
|
assert response.status_code == 200
|
|
assert response.headers["x-fastapi-item-id"] == "foo"
|
|
|
|
|
|
def test_options():
|
|
response = client.options("/items/foo")
|
|
assert response.status_code == 200
|
|
assert response.headers["x-fastapi-item-id"] == "foo"
|
|
|
|
|
|
def test_patch():
|
|
response = client.patch("/items/foo", json={"name": "Foo"})
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"item_id": "foo",
|
|
"item": {
|
|
"name": "Foo",
|
|
"price": None
|
|
}
|
|
}
|
|
|
|
|
|
def test_trace():
|
|
response = client.request("trace", "/items/foo")
|
|
assert response.status_code == 200
|
|
assert response.headers["content-type"] == "message/http"
|
|
|