From 836bb97a2d343937205a01a9829b975c48cdfcad Mon Sep 17 00:00:00 2001 From: Edouard Lavery-Plante Date: Thu, 29 Jul 2021 16:01:13 -0400 Subject: [PATCH] =?utf8?q?=E2=9C=A8=20Add=20support=20for=20extensions=20a?= =?utf8?q?nd=20updates=20to=20the=20OpenAPI=20schema=20in=20path=20operati?= =?utf8?q?ons=20(#1922)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez --- .../path-operation-advanced-configuration.md | 120 +++++++++++++++++- .../image01.png | Bin 0 -> 68080 bytes .../tutorial005.py | 8 ++ .../tutorial006.py | 41 ++++++ .../tutorial007.py | 34 +++++ fastapi/applications.py | 20 +++ fastapi/openapi/models.py | 3 + fastapi/openapi/utils.py | 2 + fastapi/routing.py | 23 ++++ tests/test_openapi_route_extensions.py | 45 +++++++ .../test_tutorial005.py | 36 ++++++ .../test_tutorial006.py | 59 +++++++++ .../test_tutorial007.py | 97 ++++++++++++++ 13 files changed, 487 insertions(+), 1 deletion(-) create mode 100644 docs/en/docs/img/tutorial/path-operation-advanced-configuration/image01.png create mode 100644 docs_src/path_operation_advanced_configuration/tutorial005.py create mode 100644 docs_src/path_operation_advanced_configuration/tutorial006.py create mode 100644 docs_src/path_operation_advanced_configuration/tutorial007.py create mode 100644 tests/test_openapi_route_extensions.py create mode 100644 tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial005.py create mode 100644 tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial006.py create mode 100644 tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007.py diff --git a/docs/en/docs/advanced/path-operation-advanced-configuration.md b/docs/en/docs/advanced/path-operation-advanced-configuration.md index 8d085d7549..352fe0764e 100644 --- a/docs/en/docs/advanced/path-operation-advanced-configuration.md +++ b/docs/en/docs/advanced/path-operation-advanced-configuration.md @@ -33,7 +33,7 @@ You should do it after adding all your *path operations*. ## Exclude from OpenAPI -To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`; +To exclude a *path operation* from the generated OpenAPI schema (and thus, from the automatic documentation systems), use the parameter `include_in_schema` and set it to `False`: ```Python hl_lines="6" {!../../../docs_src/path_operation_advanced_configuration/tutorial003.py!} @@ -50,3 +50,121 @@ It won't show up in the documentation, but other tools (such as Sphinx) will be ```Python hl_lines="19-29" {!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!} ``` + +## Additional Responses + +You probably have seen how to declare the `response_model` and `status_code` for a *path operation*. + +That defines the metadata about the main response of a *path operation*. + +You can also declare additional responses with their models, status codes, etc. + +There's a whole chapter here in the documentation about it, you can read it at [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}. + +## OpenAPI Extra + +When you declare a *path operation* in your application, **FastAPI** automatically generates the relevant metadata about that *path operation* to be included in the OpenAPI schema. + +!!! note "Technical details" + In the OpenAPI specification it is called the Operation Object. + +It has all the information about the *path operation* and is used to generate the automatic documentation. + +It includes the `tags`, `parameters`, `requestBody`, `responses`, etc. + +This *path operation*-specific OpenAPI schema is normally generated automatically by **FastAPI**, but you can also extend it. + +!!! tip + This is a low level extension point. + + If you only need to declare additonal responses, a more convenient way to do it is with [Additional Responses in OpenAPI](./additional-responses.md){.internal-link target=_blank}. + +You can extend the OpenAPI schema for a *path operation* using the parameter `openapi_extra`. + +### OpenAPI Extensions + +This `openapi_extra` can be helpful, for example, to declare [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions): + +```Python hl_lines="6" +{!../../../docs_src/path_operation_advanced_configuration/tutorial005.py!} +``` + +If you open the automatic API docs, your extension will show up at the bottom of the specific *path operation*. + + + +And if you see the resulting OpenAPI (at `/openapi.json` in your API), you will see your extension as part of the specific *path operation* too: + +```JSON hl_lines="22" +{ + "openapi": "3.0.2", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/items/": { + "get": { + "summary": "Read Items", + "operationId": "read_items_items__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "x-aperture-labs-portal": "blue" + } + } + } +} +``` + +### Custom OpenAPI *path operation* schema + +The dictionary in `openapi_extra` will be deeply merged with the automatically generated OpenAPI schema for the *path operation*. + +So, you could add additional data to the automatically generated schema. + +For example, you could decide to read and validate the request with your own code, without using the automatic features of FastAPI with Pydantic, but you could still want to define the request in the OpenAPI schema. + +You could do that with `openapi_extra`: + +```Python hl_lines="20-37 39-40" +{!../../../docs_src/path_operation_advanced_configuration/tutorial006.py!} +``` + +In this example, we didn't declare any Pydantic model. In fact, the request body is not even parsed as JSON, it is read directly as `bytes`, and the function `magic_data_reader()` would be in charge of parsing it in some way. + +Nevertheless, we can declare the expected schema for the request body. + +### Custom OpenAPI content type + +Using this same trick, you could use a Pydantic model to define the JSON Schema that is then included in the custom OpenAPI schema section for the *path operation*. + +And you could do this even if the data type in the request is not JSON. + +For example, in this application we don't use FastAPI's integrated functionality to extract the JSON Schema from Pydantic models nor the automatic validation for JSON. In fact, we are declaring the request content type as YAML, not JSON: + +```Python hl_lines="17-22 24" +{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!} +``` + +Nevertheless, although we are not using the default integrated functionality, we are still using a Pydantic model to manually generate the JSON Schema for the data that we want to receive in YAML. + +Then we use the request directly, and extract the body as `bytes`. This means that FastAPI won't even try to parse the request payload as JSON. + +And then in our code, we parse that YAML content directly, and then we are again using the same Pydantic model to validate the YAML content: + +```Python hl_lines="26-33" +{!../../../docs_src/path_operation_advanced_configuration/tutorial007.py!} +``` + +!!! tip + Here we re-use the same Pydantic model. + + But the same way, we could have validated it in some other way. diff --git a/docs/en/docs/img/tutorial/path-operation-advanced-configuration/image01.png b/docs/en/docs/img/tutorial/path-operation-advanced-configuration/image01.png new file mode 100644 index 0000000000000000000000000000000000000000..554e7c45664e4a8d12b2ac28712553f99f6ece49 GIT binary patch literal 68080 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

9A3+C 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ǭoH0$607btP@jFWRq z>bGnl5iF0$!H00FX0-7zkN@RSB>6e%lJqx^@h%A�himnKQFQ{DI`3c21SE4qwst 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 OD=s144x%l`tMaq0R1 literal 0 Hc-jL100001 diff --git a/docs_src/path_operation_advanced_configuration/tutorial005.py b/docs_src/path_operation_advanced_configuration/tutorial005.py new file mode 100644 index 0000000000..5837ad8351 --- /dev/null +++ b/docs_src/path_operation_advanced_configuration/tutorial005.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/items/", openapi_extra={"x-aperture-labs-portal": "blue"}) +async def read_items(): + return [{"item_id": "portal-gun"}] diff --git a/docs_src/path_operation_advanced_configuration/tutorial006.py b/docs_src/path_operation_advanced_configuration/tutorial006.py new file mode 100644 index 0000000000..403c3ee3fb --- /dev/null +++ b/docs_src/path_operation_advanced_configuration/tutorial006.py @@ -0,0 +1,41 @@ +from fastapi import FastAPI, Request + +app = FastAPI() + + +def magic_data_reader(raw_body: bytes): + return { + "size": len(raw_body), + "content": { + "name": "Maaaagic", + "price": 42, + "description": "Just kiddin', no magic here. ✨", + }, + } + + +@app.post( + "/items/", + openapi_extra={ + "requestBody": { + "content": { + "application/json": { + "schema": { + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"type": "string"}, + "price": {"type": "number"}, + "description": {"type": "string"}, + }, + } + } + }, + "required": True, + }, + }, +) +async def create_item(request: Request): + raw_body = await request.body() + data = magic_data_reader(raw_body) + return data diff --git a/docs_src/path_operation_advanced_configuration/tutorial007.py b/docs_src/path_operation_advanced_configuration/tutorial007.py new file mode 100644 index 0000000000..d51752bb87 --- /dev/null +++ b/docs_src/path_operation_advanced_configuration/tutorial007.py @@ -0,0 +1,34 @@ +from typing import List + +import yaml +from fastapi import FastAPI, HTTPException, Request +from pydantic import BaseModel, ValidationError + +app = FastAPI() + + +class Item(BaseModel): + name: str + tags: List[str] + + +@app.post( + "/items/", + openapi_extra={ + "requestBody": { + "content": {"application/x-yaml": {"schema": Item.schema()}}, + "required": True, + }, + }, +) +async def create_item(request: Request): + raw_body = await request.body() + try: + data = yaml.safe_load(raw_body) + except yaml.YAMLError: + raise HTTPException(status_code=422, detail="Invalid YAML") + try: + item = Item.parse_obj(data) + except ValidationError as e: + raise HTTPException(status_code=422, detail=e.errors()) + return item diff --git a/fastapi/applications.py b/fastapi/applications.py index b013e7b46a..0c25026e2b 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -236,6 +236,7 @@ class FastAPI(Starlette): JSONResponse ), name: Optional[str] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> None: self.router.add_api_route( path, @@ -260,6 +261,7 @@ class FastAPI(Starlette): include_in_schema=include_in_schema, response_class=response_class, name=name, + openapi_extra=openapi_extra, ) def api_route( @@ -286,6 +288,7 @@ class FastAPI(Starlette): include_in_schema: bool = True, response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.router.add_api_route( @@ -311,6 +314,7 @@ class FastAPI(Starlette): include_in_schema=include_in_schema, response_class=response_class, name=name, + openapi_extra=openapi_extra, ) return func @@ -379,6 +383,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.get( path, @@ -402,6 +407,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def put( @@ -428,6 +434,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.put( path, @@ -451,6 +458,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def post( @@ -477,6 +485,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.post( path, @@ -500,6 +509,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def delete( @@ -526,6 +536,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.delete( path, @@ -549,6 +560,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def options( @@ -575,6 +587,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.options( path, @@ -598,6 +611,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def head( @@ -624,6 +638,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.head( path, @@ -647,6 +662,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def patch( @@ -673,6 +689,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.patch( path, @@ -696,6 +713,7 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def trace( @@ -722,6 +740,7 @@ class FastAPI(Starlette): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.router.trace( path, @@ -745,4 +764,5 @@ class FastAPI(Starlette): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py index efbd88ad74..4f55aa001c 100644 --- a/fastapi/openapi/models.py +++ b/fastapi/openapi/models.py @@ -227,6 +227,9 @@ class Operation(BaseModel): security: Optional[List[Dict[str, List[str]]]] = None servers: Optional[List[Server]] = None + class Config: + extra = "allow" + class PathItem(BaseModel): ref: Optional[str] = Field(None, alias="$ref") diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 604ba5b006..0e73e21bf8 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -317,6 +317,8 @@ def get_openapi_path( "HTTPValidationError": validation_error_response_definition, } ) + if route.openapi_extra: + deep_dict_update(operation, route.openapi_extra) path[method.lower()] = operation return path, security_schemes, definitions diff --git a/fastapi/routing.py b/fastapi/routing.py index f5fe0c0bde..0ad082341f 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -320,6 +320,7 @@ class APIRoute(routing.Route): ), dependency_overrides_provider: Optional[Any] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> None: # normalise enums e.g. http.HTTPStatus if isinstance(status_code, enum.IntEnum): @@ -406,6 +407,7 @@ class APIRoute(routing.Route): self.dependency_overrides_provider = dependency_overrides_provider self.callbacks = callbacks self.app = request_response(self.get_route_handler()) + self.openapi_extra = openapi_extra def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]: return get_request_handler( @@ -496,6 +498,7 @@ class APIRouter(routing.Router): name: Optional[str] = None, route_class_override: Optional[Type[APIRoute]] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> None: route_class = route_class_override or self.route_class responses = responses or {} @@ -537,6 +540,7 @@ class APIRouter(routing.Router): name=name, dependency_overrides_provider=self.dependency_overrides_provider, callbacks=current_callbacks, + openapi_extra=openapi_extra, ) self.routes.append(route) @@ -565,6 +569,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: def decorator(func: DecoratedCallable) -> DecoratedCallable: self.add_api_route( @@ -591,6 +596,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) return func @@ -695,6 +701,7 @@ class APIRouter(routing.Router): name=route.name, route_class_override=type(route), callbacks=current_callbacks, + openapi_extra=route.openapi_extra, ) elif isinstance(route, routing.Route): methods = list(route.methods or []) # type: ignore # in Starlette @@ -742,6 +749,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -766,6 +774,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def put( @@ -792,6 +801,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -816,6 +826,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def post( @@ -842,6 +853,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -866,6 +878,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def delete( @@ -892,6 +905,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -916,6 +930,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def options( @@ -942,6 +957,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -966,6 +982,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def head( @@ -992,6 +1009,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -1016,6 +1034,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def patch( @@ -1042,6 +1061,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( path=path, @@ -1066,6 +1086,7 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) def trace( @@ -1092,6 +1113,7 @@ class APIRouter(routing.Router): response_class: Type[Response] = Default(JSONResponse), name: Optional[str] = None, callbacks: Optional[List[BaseRoute]] = None, + openapi_extra: Optional[Dict[str, Any]] = None, ) -> Callable[[DecoratedCallable], DecoratedCallable]: return self.api_route( @@ -1117,4 +1139,5 @@ class APIRouter(routing.Router): response_class=response_class, name=name, callbacks=callbacks, + openapi_extra=openapi_extra, ) diff --git a/tests/test_openapi_route_extensions.py b/tests/test_openapi_route_extensions.py new file mode 100644 index 0000000000..8a1080d697 --- /dev/null +++ b/tests/test_openapi_route_extensions.py @@ -0,0 +1,45 @@ +from fastapi import FastAPI +from fastapi.testclient import TestClient + +app = FastAPI() + + +@app.get("/", openapi_extra={"x-custom-extension": "value"}) +def route_with_extras(): + return {} + + +client = TestClient(app) + + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + }, + "summary": "Route With Extras", + "operationId": "route_with_extras__get", + "x-custom-extension": "value", + } + }, + }, +} + + +def test_openapi(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +def test_get_route(): + response = client.get("/") + assert response.status_code == 200, response.text + assert response.json() == {} diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial005.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial005.py new file mode 100644 index 0000000000..5042d18375 --- /dev/null +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial005.py @@ -0,0 +1,36 @@ +from fastapi.testclient import TestClient + +from docs_src.path_operation_advanced_configuration.tutorial005 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + } + }, + "summary": "Read Items", + "operationId": "read_items_items__get", + "x-aperture-labs-portal": "blue", + } + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +def test_get(): + response = client.get("/items/") + assert response.status_code == 200, response.text diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial006.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial006.py new file mode 100644 index 0000000000..5533b29571 --- /dev/null +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial006.py @@ -0,0 +1,59 @@ +from fastapi.testclient import TestClient + +from docs_src.path_operation_advanced_configuration.tutorial006 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/": { + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"type": "string"}, + "price": {"type": "number"}, + "description": {"type": "string"}, + }, + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + } + }, + } + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +def test_post(): + response = client.post("/items/", data=b"this is actually not validated") + assert response.status_code == 200, response.text + assert response.json() == { + "size": 30, + "content": { + "name": "Maaaagic", + "price": 42, + "description": "Just kiddin', no magic here. ✨", + }, + } diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007.py new file mode 100644 index 0000000000..cb5dbc8eb0 --- /dev/null +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007.py @@ -0,0 +1,97 @@ +from fastapi.testclient import TestClient + +from docs_src.path_operation_advanced_configuration.tutorial007 import app + +client = TestClient(app) + +openapi_schema = { + "openapi": "3.0.2", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/": { + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/x-yaml": { + "schema": { + "title": "Item", + "required": ["name", "tags"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "tags": { + "title": "Tags", + "type": "array", + "items": {"type": "string"}, + }, + }, + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + } + }, + } + } + }, +} + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == openapi_schema + + +def test_post(): + yaml_data = """ + name: Deadpoolio + tags: + - x-force + - x-men + - x-avengers + """ + response = client.post("/items/", data=yaml_data) + assert response.status_code == 200, response.text + assert response.json() == { + "name": "Deadpoolio", + "tags": ["x-force", "x-men", "x-avengers"], + } + + +def test_post_broken_yaml(): + yaml_data = """ + name: Deadpoolio + tags: + x - x-force + x - x-men + x - x-avengers + """ + response = client.post("/items/", data=yaml_data) + assert response.status_code == 422, response.text + assert response.json() == {"detail": "Invalid YAML"} + + +def test_post_invalid(): + yaml_data = """ + name: Deadpoolio + tags: + - x-force + - x-men + - x-avengers + - sneaky: object + """ + response = client.post("/items/", data=yaml_data) + assert response.status_code == 422, response.text + assert response.json() == { + "detail": [ + {"loc": ["tags", 3], "msg": "str type expected", "type": "type_error.str"} + ] + } -- 2.47.3