Browse Source

Add support for tags with Enums (#4468)

pull/4470/head
Sebastián Ramírez 3 years ago
committed by GitHub
parent
commit
569afb4378
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      docs/en/docs/tutorial/path-operation-configuration.md
  2. 20
      docs_src/path_operation_configuration/tutorial002b.py
  3. 23
      fastapi/applications.py
  4. 32
      fastapi/routing.py
  5. 56
      tests/test_tutorial/test_path_operation_configurations/test_tutorial002b.py

12
docs/en/docs/tutorial/path-operation-configuration.md

@ -64,6 +64,18 @@ They will be added to the OpenAPI schema and used by the automatic documentation
<img src="/img/tutorial/path-operation-configuration/image01.png">
### Tags with Enums
If you have a big application, you might end up accumulating **several tags**, and you would want to make sure you always use the **same tag** for related *path operations*.
In these cases, it could make sense to store the tags in an `Enum`.
**FastAPI** supports that the same way as with plain strings:
```Python hl_lines="1 8-10 13 18"
{!../../../docs_src/path_operation_configuration/tutorial002b.py!}
```
## Summary and description
You can add a `summary` and `description`:

20
docs_src/path_operation_configuration/tutorial002b.py

@ -0,0 +1,20 @@
from enum import Enum
from fastapi import FastAPI
app = FastAPI()
class Tags(Enum):
items = "items"
users = "users"
@app.get("/items/", tags=[Tags.items])
async def get_items():
return ["Portal gun", "Plumbus"]
@app.get("/users/", tags=[Tags.users])
async def read_users():
return ["Rick", "Morty"]

23
fastapi/applications.py

@ -1,3 +1,4 @@
from enum import Enum
from typing import Any, Callable, Coroutine, Dict, List, Optional, Sequence, Type, Union
from fastapi import routing
@ -219,7 +220,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -273,7 +274,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -342,7 +343,7 @@ class FastAPI(Starlette):
router: routing.APIRouter,
*,
prefix: str = "",
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
@ -368,7 +369,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -419,7 +420,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -470,7 +471,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -521,7 +522,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -572,7 +573,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -623,7 +624,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -674,7 +675,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -725,7 +726,7 @@ class FastAPI(Starlette):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,

32
fastapi/routing.py

@ -1,9 +1,9 @@
import asyncio
import dataclasses
import email.message
import enum
import inspect
import json
from enum import Enum, IntEnum
from typing import (
Any,
Callable,
@ -305,7 +305,7 @@ class APIRoute(routing.Route):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -330,7 +330,7 @@ class APIRoute(routing.Route):
openapi_extra: Optional[Dict[str, Any]] = None,
) -> None:
# normalise enums e.g. http.HTTPStatus
if isinstance(status_code, enum.IntEnum):
if isinstance(status_code, IntEnum):
status_code = int(status_code)
self.path = path
self.endpoint = endpoint
@ -438,7 +438,7 @@ class APIRouter(routing.Router):
self,
*,
prefix: str = "",
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
default_response_class: Type[Response] = Default(JSONResponse),
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
@ -466,7 +466,7 @@ class APIRouter(routing.Router):
"/"
), "A path prefix must not end with '/', as the routes will start with '/'"
self.prefix = prefix
self.tags: List[str] = tags or []
self.tags: List[Union[str, Enum]] = tags or []
self.dependencies = list(dependencies or []) or []
self.deprecated = deprecated
self.include_in_schema = include_in_schema
@ -483,7 +483,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -557,7 +557,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -634,7 +634,7 @@ class APIRouter(routing.Router):
router: "APIRouter",
*,
prefix: str = "",
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
default_response_class: Type[Response] = Default(JSONResponse),
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
@ -738,7 +738,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -790,7 +790,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -842,7 +842,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -894,7 +894,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -946,7 +946,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -998,7 +998,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -1050,7 +1050,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
@ -1102,7 +1102,7 @@ class APIRouter(routing.Router):
*,
response_model: Optional[Type[Any]] = None,
status_code: Optional[int] = None,
tags: Optional[List[str]] = None,
tags: Optional[List[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,

56
tests/test_tutorial/test_path_operation_configurations/test_tutorial002b.py

@ -0,0 +1,56 @@
from fastapi.testclient import TestClient
from docs_src.path_operation_configuration.tutorial002b import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
"/items/": {
"get": {
"tags": ["items"],
"summary": "Get Items",
"operationId": "get_items_items__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
}
},
"/users/": {
"get": {
"tags": ["users"],
"summary": "Read Users",
"operationId": "read_users_users__get",
"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_get_items():
response = client.get("/items/")
assert response.status_code == 200, response.text
assert response.json() == ["Portal gun", "Plumbus"]
def test_get_users():
response = client.get("/users/")
assert response.status_code == 200, response.text
assert response.json() == ["Rick", "Morty"]
Loading…
Cancel
Save