Browse Source

📝 Document overriding operationId for all path operations using their function names (#642)

pull/734/head
Steven Kalt 5 years ago
committed by Sebastián Ramírez
parent
commit
bac2f587b7
  1. 18
      docs/src/path_operation_advanced_configuration/tutorial002.py
  2. 28
      docs/src/path_operation_advanced_configuration/tutorial003.py
  3. 30
      docs/src/path_operation_advanced_configuration/tutorial004.py
  4. 22
      docs/tutorial/path-operation-advanced-configuration.md
  5. 15
      tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial002.py
  6. 97
      tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial003.py
  7. 112
      tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py

18
docs/src/path_operation_advanced_configuration/tutorial002.py

@ -1,8 +1,24 @@
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.routing import APIRoute
app = FastAPI() app = FastAPI()
@app.get("/items/", include_in_schema=False) @app.get("/items/")
async def read_items(): async def read_items():
return [{"item_id": "Foo"}] return [{"item_id": "Foo"}]
def use_route_names_as_operation_ids(app: FastAPI) -> None:
"""
Simplify operation IDs so that generated API clients have simpler function
names.
Should be called only after all routes have been added.
"""
for route in app.routes:
if isinstance(route, APIRoute):
route.operation_id = route.name # in this case, 'read_items'
use_route_names_as_operation_ids(app)

28
docs/src/path_operation_advanced_configuration/tutorial003.py

@ -1,30 +1,8 @@
from typing import Set
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI() app = FastAPI()
class Item(BaseModel): @app.get("/items/", include_in_schema=False)
name: str async def read_items():
description: str = None return [{"item_id": "Foo"}]
price: float
tax: float = None
tags: Set[str] = []
@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(*, item: Item):
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
\f
:param item: User input.
"""
return item

30
docs/src/path_operation_advanced_configuration/tutorial004.py

@ -0,0 +1,30 @@
from typing import Set
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
tags: Set[str] = []
@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(*, item: Item):
"""
Create an item with all the information:
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
\f
:param item: User input.
"""
return item

22
docs/tutorial/path-operation-advanced-configuration.md

@ -11,12 +11,30 @@ You would have to make sure that it is unique for each operation.
{!./src/path_operation_advanced_configuration/tutorial001.py!} {!./src/path_operation_advanced_configuration/tutorial001.py!}
``` ```
### Using the *path operation function* name as the operationId
If you want to use your APIs' function names as `operationId`s, you can iterate over all of them and override each *path operation's* `operation_id` using their `APIRoute.name`.
You should do it after adding all your *path operations*.
```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24"
{!./src/path_operation_advanced_configuration/tutorial002.py!}
```
!!! tip
If you manually call `app.openapi()`, you should update the `operationId`s before that.
!!! warning
If you do this, you have to make sure each one of your *path operation functions* has a unique name.
Even if they are in different modules (Python files).
## Exclude from OpenAPI ## 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" ```Python hl_lines="6"
{!./src/path_operation_advanced_configuration/tutorial002.py!} {!./src/path_operation_advanced_configuration/tutorial003.py!}
``` ```
## Advanced description from docstring ## Advanced description from docstring
@ -28,5 +46,5 @@ Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate
It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest. It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest.
```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29" ```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29"
{!./src/path_operation_advanced_configuration/tutorial003.py!} {!./src/path_operation_advanced_configuration/tutorial004.py!}
``` ```

15
tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial002.py

@ -7,7 +7,20 @@ client = TestClient(app)
openapi_schema = { openapi_schema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "Fast API", "version": "0.1.0"}, "info": {"title": "Fast API", "version": "0.1.0"},
"paths": {}, "paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {"application/json": {"schema": {}}},
}
},
"summary": "Read Items",
"operationId": "read_items",
}
}
},
} }

97
tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial003.py

@ -7,90 +7,7 @@ client = TestClient(app)
openapi_schema = { openapi_schema = {
"openapi": "3.0.2", "openapi": "3.0.2",
"info": {"title": "Fast API", "version": "0.1.0"}, "info": {"title": "Fast API", "version": "0.1.0"},
"paths": { "paths": {},
"/items/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Create an item",
"description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item\n",
"operationId": "create_item_items__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
"description": {"title": "Description", "type": "string"},
"tax": {"title": "Tax", "type": "number"},
"tags": {
"title": "Tags",
"uniqueItems": True,
"type": "array",
"items": {"type": "string"},
"default": [],
},
},
},
"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"},
}
},
},
}
},
} }
@ -100,13 +17,7 @@ def test_openapi_schema():
assert response.json() == openapi_schema assert response.json() == openapi_schema
def test_query_params_str_validations(): def test_get():
response = client.post("/items/", json={"name": "Foo", "price": 42}) response = client.get("/items/")
assert response.status_code == 200 assert response.status_code == 200
assert response.json() == { assert response.json() == [{"item_id": "Foo"}]
"name": "Foo",
"price": 42,
"description": None,
"tax": None,
"tags": [],
}

112
tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py

@ -0,0 +1,112 @@
from starlette.testclient import TestClient
from path_operation_advanced_configuration.tutorial004 import app
client = TestClient(app)
openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "Fast API", "version": "0.1.0"},
"paths": {
"/items/": {
"post": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
},
},
},
"summary": "Create an item",
"description": "Create an item with all the information:\n\n- **name**: each item must have a name\n- **description**: a long description\n- **price**: required\n- **tax**: if the item doesn't have tax, you can omit this\n- **tags**: a set of unique tag strings for this item\n",
"operationId": "create_item_items__post",
"requestBody": {
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Item"}
}
},
"required": True,
},
}
}
},
"components": {
"schemas": {
"Item": {
"title": "Item",
"required": ["name", "price"],
"type": "object",
"properties": {
"name": {"title": "Name", "type": "string"},
"price": {"title": "Price", "type": "number"},
"description": {"title": "Description", "type": "string"},
"tax": {"title": "Tax", "type": "number"},
"tags": {
"title": "Tags",
"uniqueItems": True,
"type": "array",
"items": {"type": "string"},
"default": [],
},
},
},
"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_query_params_str_validations():
response = client.post("/items/", json={"name": "Foo", "price": 42})
assert response.status_code == 200
assert response.json() == {
"name": "Foo",
"price": 42,
"description": None,
"tax": None,
"tags": [],
}
Loading…
Cancel
Save