diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a512a019b8a5..c9723b25bbace 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v04 + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-pydantic-v2-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v05 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-tests.txt @@ -62,7 +62,7 @@ jobs: id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v04 + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ matrix.pydantic-version }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-test-v05 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -r requirements-tests.txt diff --git a/.gitignore b/.gitignore index d380d16b7d7b3..9be494cec0a92 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ archive.zip *~ .*.sw? .cache + +# macOS +.DS_Store diff --git a/README.md b/README.md index 50f80ded67f0b..2662134261ea0 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ The key features are: + @@ -56,6 +57,7 @@ The key features are: + diff --git a/docs/em/docs/advanced/extending-openapi.md b/docs/em/docs/advanced/extending-openapi.md deleted file mode 100644 index 496a8d9de34f4..0000000000000 --- a/docs/em/docs/advanced/extending-openapi.md +++ /dev/null @@ -1,314 +0,0 @@ -# โ†” ๐Ÿ—„ - -!!! warning - ๐Ÿ‘‰ ๐Ÿ‘ ๐Ÿง โš’. ๐Ÿ‘† ๐ŸŽฒ ๐Ÿ’ช ๐Ÿšถ โšซ๏ธ. - - ๐Ÿšฅ ๐Ÿ‘† ๐Ÿ“„ ๐Ÿ”ฐ - ๐Ÿ‘ฉโ€๐Ÿ’ป ๐Ÿฆฎ, ๐Ÿ‘† ๐Ÿ’ช ๐ŸŽฒ ๐Ÿšถ ๐Ÿ‘‰ ๐Ÿ“„. - - ๐Ÿšฅ ๐Ÿ‘† โช ๐Ÿ’ญ ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ”€ ๐Ÿ— ๐Ÿ—„ ๐Ÿ”—, ๐Ÿ˜ฃ ๐Ÿ‘‚. - -๐Ÿ“ค ๐Ÿ’ผ ๐ŸŒโ” ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ’ช ๐Ÿ”€ ๐Ÿ— ๐Ÿ—„ ๐Ÿ”—. - -๐Ÿ‘‰ ๐Ÿ“„ ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ โ”. - -## ๐Ÿ˜ ๐Ÿ› ๏ธ - -๐Ÿ˜ (๐Ÿ”ข) ๐Ÿ› ๏ธ, โฉ. - -`FastAPI` ๐Ÿˆธ (๐Ÿ‘) โœ”๏ธ `.openapi()` ๐Ÿ‘ฉโ€๐Ÿ”ฌ ๐Ÿ‘ˆ ๐Ÿ“ˆ ๐Ÿ“จ ๐Ÿ—„ ๐Ÿ”—. - -๐Ÿ• ๐Ÿˆธ ๐ŸŽš ๐Ÿ—, *โžก ๐Ÿ› ๏ธ* `/openapi.json` (โš–๏ธ โšซ๏ธโ” ๐Ÿ‘† โš’ ๐Ÿ‘† `openapi_url`) ยฎ. - -โšซ๏ธ ๐Ÿ“จ ๐ŸŽป ๐Ÿ“จ โฎ๏ธ ๐Ÿ ๐Ÿˆธ `.openapi()` ๐Ÿ‘ฉโ€๐Ÿ”ฌ. - -๐Ÿ”ข, โšซ๏ธโ” ๐Ÿ‘ฉโ€๐Ÿ”ฌ `.openapi()` ๐Ÿ”จ โœ… ๐Ÿ  `.openapi_schema` ๐Ÿ‘€ ๐Ÿšฅ โšซ๏ธ โœ”๏ธ ๐ŸŽš & ๐Ÿ“จ ๐Ÿ‘ซ. - -๐Ÿšฅ โšซ๏ธ ๐Ÿšซ, โšซ๏ธ ๐Ÿ— ๐Ÿ‘ซ โš™๏ธ ๐Ÿš™ ๐Ÿ”ข `fastapi.openapi.utils.get_openapi`. - -& ๐Ÿ‘ˆ ๐Ÿ”ข `get_openapi()` ๐Ÿ“จ ๐Ÿ”ข: - -* `title`: ๐Ÿ—„ ๐Ÿ“›, ๐ŸŽฆ ๐Ÿฉบ. -* `version`: โฌ ๐Ÿ‘† ๐Ÿ› ๏ธ, โœ… `2.5.0`. -* `openapi_version`: โฌ ๐Ÿ—„ ๐Ÿ”ง โš™๏ธ. ๐Ÿ”ข, โช: `3.0.2`. -* `description`: ๐Ÿ“› ๐Ÿ‘† ๐Ÿ› ๏ธ. -* `routes`: ๐Ÿ“‡ ๐Ÿ›ฃ, ๐Ÿ‘ซ ๐Ÿ”  ยฎ *โžก ๐Ÿ› ๏ธ*. ๐Ÿ‘ซ โœŠ โšช๏ธโžก๏ธ `app.routes`. - -## ๐Ÿ”‘ ๐Ÿ”ข - -โš™๏ธ โ„น ๐Ÿ”›, ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐ŸŽ ๐Ÿš™ ๐Ÿ”ข ๐Ÿ— ๐Ÿ—„ ๐Ÿ”— & ๐Ÿ” ๐Ÿ”  ๐Ÿ• ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ’ช. - -๐Ÿ–ผ, โžก๏ธ ๐Ÿšฎ ๐Ÿ“„ ๐Ÿ—„ โ†” ๐Ÿ”Œ ๐Ÿ›ƒ ๐Ÿ”ฑ. - -### ๐Ÿ˜ **FastAPI** - -๐Ÿฅ‡, โœ ๐ŸŒ ๐Ÿ‘† **FastAPI** ๐Ÿˆธ ๐Ÿ›Ž: - -```Python hl_lines="1 4 7-9" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### ๐Ÿ— ๐Ÿ—„ ๐Ÿ”— - -โคด๏ธ, โš™๏ธ ๐ŸŽ ๐Ÿš™ ๐Ÿ”ข ๐Ÿ— ๐Ÿ—„ ๐Ÿ”—, ๐Ÿ”˜ `custom_openapi()` ๐Ÿ”ข: - -```Python hl_lines="2 15-20" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### ๐Ÿ”€ ๐Ÿ—„ ๐Ÿ”— - -๐Ÿ”œ ๐Ÿ‘† ๐Ÿ’ช ๐Ÿšฎ ๐Ÿ“„ โ†”, โŽ ๐Ÿ›ƒ `x-logo` `info` "๐ŸŽš" ๐Ÿ—„ ๐Ÿ”—: - -```Python hl_lines="21-23" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### ๐Ÿ’พ ๐Ÿ—„ ๐Ÿ”— - -๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐Ÿ  `.openapi_schema` "๐Ÿ’พ", ๐Ÿช ๐Ÿ‘† ๐Ÿ— ๐Ÿ”—. - -๐Ÿ‘ˆ ๐ŸŒŒ, ๐Ÿ‘† ๐Ÿˆธ ๐Ÿ† ๐Ÿšซ โœ”๏ธ ๐Ÿ— ๐Ÿ”— ๐Ÿ”  ๐Ÿ•ฐ ๐Ÿ‘ฉโ€๐Ÿ’ป ๐Ÿ“‚ ๐Ÿ‘† ๐Ÿ› ๏ธ ๐Ÿฉบ. - -โšซ๏ธ ๐Ÿ”œ ๐Ÿ— ๐Ÿ•ด ๐Ÿ•, & โคด๏ธ ๐ŸŽ ๐Ÿ’พ ๐Ÿ”— ๐Ÿ”œ โš™๏ธ โญ ๐Ÿ“จ. - -```Python hl_lines="13-14 24-25" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### ๐Ÿ” ๐Ÿ‘ฉโ€๐Ÿ”ฌ - -๐Ÿ”œ ๐Ÿ‘† ๐Ÿ’ช โŽ `.openapi()` ๐Ÿ‘ฉโ€๐Ÿ”ฌ โฎ๏ธ ๐Ÿ‘† ๐Ÿ†• ๐Ÿ”ข. - -```Python hl_lines="28" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### โœ… โšซ๏ธ - -๐Ÿ• ๐Ÿ‘† ๐Ÿšถ http://127.0.0.1:8000/redoc ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐Ÿ‘ˆ ๐Ÿ‘† โš™๏ธ ๐Ÿ‘† ๐Ÿ›ƒ ๐Ÿ”ฑ (๐Ÿ‘‰ ๐Ÿ–ผ, **FastAPI**'โ“‚ ๐Ÿ”ฑ): - - - -## ๐Ÿ‘ค-๐Ÿ•ธ ๐Ÿ•ธ & ๐ŸŽš ๐Ÿฉบ - -๐Ÿ› ๏ธ ๐Ÿฉบ โš™๏ธ **๐Ÿฆ ๐ŸŽš** & **๐Ÿ“„**, & ๐Ÿ”  ๐Ÿ‘ˆ ๐Ÿ’ช ๐Ÿ•ธ & ๐ŸŽš ๐Ÿ“. - -๐Ÿ”ข, ๐Ÿ‘ˆ ๐Ÿ“ ๐Ÿฆ โšช๏ธโžก๏ธ ๐Ÿ’ฒ. - -โœ‹๏ธ โšซ๏ธ ๐Ÿ’ช ๐Ÿ›ƒ โšซ๏ธ, ๐Ÿ‘† ๐Ÿ’ช โš’ ๐ŸŽฏ ๐Ÿ’ฒ, โš–๏ธ ๐Ÿฆ ๐Ÿ“ ๐Ÿ‘†. - -๐Ÿ‘ˆ โš , ๐Ÿ–ผ, ๐Ÿšฅ ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ‘† ๐Ÿ“ฑ ๐Ÿšง ๐Ÿ‘ท โช ๐Ÿ“ฑ, ๐Ÿต ๐Ÿ“‚ ๐Ÿ•ธ ๐Ÿ”, โš–๏ธ ๐Ÿ‡ง๐Ÿ‡ฟ ๐Ÿ•ธ. - -๐Ÿ“ฅ ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ โ” ๐Ÿฆ ๐Ÿ‘ˆ ๐Ÿ“ ๐Ÿ‘†, ๐ŸŽ FastAPI ๐Ÿ“ฑ, & ๐Ÿ”— ๐Ÿฉบ โš™๏ธ ๐Ÿ‘ซ. - -### ๐Ÿ— ๐Ÿ“ ๐Ÿ“Š - -โžก๏ธ ๐Ÿ’ฌ ๐Ÿ‘† ๐Ÿ— ๐Ÿ“ ๐Ÿ“Š ๐Ÿ‘€ ๐Ÿ’– ๐Ÿ‘‰: - -``` -. -โ”œโ”€โ”€ app -โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”œโ”€โ”€ main.py -``` - -๐Ÿ”œ โœ ๐Ÿ“ ๐Ÿช ๐Ÿ“š ๐ŸŽป ๐Ÿ“. - -๐Ÿ‘† ๐Ÿ†• ๐Ÿ“ ๐Ÿ“Š ๐Ÿ’ช ๐Ÿ‘€ ๐Ÿ’– ๐Ÿ‘‰: - -``` -. -โ”œโ”€โ”€ app -โ”‚ย ย  โ”œโ”€โ”€ __init__.py -โ”‚ย ย  โ”œโ”€โ”€ main.py -โ””โ”€โ”€ static/ -``` - -### โฌ ๐Ÿ“ - -โฌ ๐ŸŽป ๐Ÿ“ ๐Ÿ’ช ๐Ÿฉบ & ๐Ÿšฎ ๐Ÿ‘ซ ๐Ÿ”› ๐Ÿ‘ˆ `static/` ๐Ÿ“. - -๐Ÿ‘† ๐Ÿ’ช ๐ŸŽฒ โ–ถ๏ธ๏ธ-๐Ÿ–Š ๐Ÿ”  ๐Ÿ”— & ๐Ÿ–Š ๐ŸŽ› ๐ŸŽ `Save link as...`. - -**๐Ÿฆ ๐ŸŽš** โš™๏ธ ๐Ÿ“: - -* `swagger-ui-bundle.js` -* `swagger-ui.css` - -& **๐Ÿ“„** โš™๏ธ ๐Ÿ“: - -* `redoc.standalone.js` - -โฎ๏ธ ๐Ÿ‘ˆ, ๐Ÿ‘† ๐Ÿ“ ๐Ÿ“Š ๐Ÿ’ช ๐Ÿ‘€ ๐Ÿ’–: - -``` -. -โ”œโ”€โ”€ app -โ”‚ย ย  โ”œโ”€โ”€ __init__.py -โ”‚ย ย  โ”œโ”€โ”€ main.py -โ””โ”€โ”€ static - โ”œโ”€โ”€ redoc.standalone.js - โ”œโ”€โ”€ swagger-ui-bundle.js - โ””โ”€โ”€ swagger-ui.css -``` - -### ๐Ÿฆ ๐ŸŽป ๐Ÿ“ - -* ๐Ÿ—„ `StaticFiles`. -* "๐Ÿ—ป" `StaticFiles()` ๐Ÿ‘ ๐ŸŽฏ โžก. - -```Python hl_lines="7 11" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -### ๐Ÿ’ฏ ๐ŸŽป ๐Ÿ“ - -โ–ถ๏ธ ๐Ÿ‘† ๐Ÿˆธ & ๐Ÿšถ http://127.0.0.1:8000/static/redoc.standalone.js. - -๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐Ÿ“ถ ๐Ÿ“ ๐Ÿ•ธ ๐Ÿ“ **๐Ÿ“„**. - -โšซ๏ธ ๐Ÿ’ช โ–ถ๏ธ โฎ๏ธ ๐Ÿ•ณ ๐Ÿ’–: - -```JavaScript -/*! - * ReDoc - OpenAPI/Swagger-generated API Reference Documentation - * ------------------------------------------------------------- - * Version: "2.0.0-rc.18" - * Repo: https://github.com/Redocly/redoc - */ -!function(e,t){"object"==typeof exports&&"object"==typeof m - -... -``` - -๐Ÿ‘ˆ โœ” ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ’†โ€โ™‚ ๐Ÿ’ช ๐Ÿฆ ๐ŸŽป ๐Ÿ“ โšช๏ธโžก๏ธ ๐Ÿ‘† ๐Ÿ“ฑ, & ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿฅ‰ ๐ŸŽป ๐Ÿ“ ๐Ÿฉบ โ˜‘ ๐Ÿฅ‰. - -๐Ÿ”œ ๐Ÿ‘ฅ ๐Ÿ’ช ๐Ÿ”— ๐Ÿ“ฑ โš™๏ธ ๐Ÿ“š ๐ŸŽป ๐Ÿ“ ๐Ÿฉบ. - -### โŽ ๐Ÿง ๐Ÿฉบ - -๐Ÿฅ‡ ๐Ÿ” โŽ ๐Ÿง ๐Ÿฉบ, ๐Ÿ“š โš™๏ธ ๐Ÿ’ฒ ๐Ÿ”ข. - -โŽ ๐Ÿ‘ซ, โš’ ๐Ÿ‘ซ ๐Ÿ“› `None` ๐Ÿ•โ” ๐Ÿ— ๐Ÿ‘† `FastAPI` ๐Ÿ“ฑ: - -```Python hl_lines="9" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -### ๐Ÿ”Œ ๐Ÿ›ƒ ๐Ÿฉบ - -๐Ÿ”œ ๐Ÿ‘† ๐Ÿ’ช โœ *โžก ๐Ÿ› ๏ธ* ๐Ÿ›ƒ ๐Ÿฉบ. - -๐Ÿ‘† ๐Ÿ’ช ๐Ÿค-โš™๏ธ FastAPI ๐Ÿ”— ๐Ÿ”ข โœ ๐Ÿ•ธ ๐Ÿ“ƒ ๐Ÿฉบ, & ๐Ÿšถโ€โ™€๏ธ ๐Ÿ‘ซ ๐Ÿ’ช โŒ: - -* `openapi_url`: ๐Ÿ“› ๐ŸŒโ” ๐Ÿ•ธ ๐Ÿ“ƒ ๐Ÿฉบ ๐Ÿ’ช ๐Ÿคš ๐Ÿ—„ ๐Ÿ”— ๐Ÿ‘† ๐Ÿ› ๏ธ. ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐Ÿ“ฅ ๐Ÿ”ข `app.openapi_url`. -* `title`: ๐Ÿ“› ๐Ÿ‘† ๐Ÿ› ๏ธ. -* `oauth2_redirect_url`: ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ `app.swagger_ui_oauth2_redirect_url` ๐Ÿ“ฅ โš™๏ธ ๐Ÿ”ข. -* `swagger_js_url`: ๐Ÿ“› ๐ŸŒโ” ๐Ÿ•ธ ๐Ÿ‘† ๐Ÿฆ ๐ŸŽš ๐Ÿฉบ ๐Ÿ’ช ๐Ÿคš **๐Ÿ•ธ** ๐Ÿ“. ๐Ÿ‘‰ 1๏ธโƒฃ ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ‘ ๐Ÿ“ฑ ๐Ÿ”œ ๐Ÿฆ. -* `swagger_css_url`: ๐Ÿ“› ๐ŸŒโ” ๐Ÿ•ธ ๐Ÿ‘† ๐Ÿฆ ๐ŸŽš ๐Ÿฉบ ๐Ÿ’ช ๐Ÿคš **๐ŸŽš** ๐Ÿ“. ๐Ÿ‘‰ 1๏ธโƒฃ ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ‘ ๐Ÿ“ฑ ๐Ÿ”œ ๐Ÿฆ. - -& โžก ๐Ÿ“„... - -```Python hl_lines="2-6 14-22 25-27 30-36" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -!!! tip - *โžก ๐Ÿ› ๏ธ* `swagger_ui_redirect` ๐Ÿ‘ฉโ€๐ŸŽ“ ๐Ÿ•โ” ๐Ÿ‘† โš™๏ธ Oauth2๏ธโƒฃ. - - ๐Ÿšฅ ๐Ÿ‘† ๐Ÿ› ๏ธ ๐Ÿ‘† ๐Ÿ› ๏ธ โฎ๏ธ Oauth2๏ธโƒฃ ๐Ÿ•โ€๐Ÿฆบ, ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ’ช ๐Ÿ”“ & ๐Ÿ‘Ÿ ๐Ÿ”™ ๐Ÿ› ๏ธ ๐Ÿฉบ โฎ๏ธ ๐Ÿ“Ž ๐ŸŽ“. & ๐Ÿ”— โฎ๏ธ โšซ๏ธ โš™๏ธ ๐ŸŽฐ Oauth2๏ธโƒฃ ๐Ÿค. - - ๐Ÿฆ ๐ŸŽš ๐Ÿ”œ ๐Ÿต โšซ๏ธ โ›… ๐ŸŽ‘ ๐Ÿ‘†, โœ‹๏ธ โšซ๏ธ ๐Ÿ’ช ๐Ÿ‘‰ "โŽ" ๐Ÿ‘ฉโ€๐ŸŽ“. - -### โœ *โžก ๐Ÿ› ๏ธ* ๐Ÿ’ฏ โšซ๏ธ - -๐Ÿ”œ, ๐Ÿ’ช ๐Ÿ’ฏ ๐Ÿ‘ˆ ๐ŸŒ ๐Ÿ‘ท, โœ *โžก ๐Ÿ› ๏ธ*: - -```Python hl_lines="39-41" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -### ๐Ÿ’ฏ โšซ๏ธ - -๐Ÿ”œ, ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ’ช ๐Ÿ”Œ ๐Ÿ‘† ๐Ÿ“ป, ๐Ÿšถ ๐Ÿ‘† ๐Ÿฉบ http://127.0.0.1:8000/docs, & ๐Ÿ”ƒ ๐Ÿ“ƒ. - -& ๐Ÿต ๐Ÿ•ธ, ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ’ช ๐Ÿ‘€ ๐Ÿฉบ ๐Ÿ‘† ๐Ÿ› ๏ธ & ๐Ÿ”— โฎ๏ธ โšซ๏ธ. - -## ๐Ÿ› ๏ธ ๐Ÿฆ ๐ŸŽš - -๐Ÿ‘† ๐Ÿ’ช ๐Ÿ”— โž• ๐Ÿฆ ๐ŸŽš ๐Ÿ”ข. - -๐Ÿ”— ๐Ÿ‘ซ, ๐Ÿšถโ€โ™€๏ธ `swagger_ui_parameters` โŒ ๐Ÿ•โ” ๐Ÿ— `FastAPI()` ๐Ÿ“ฑ ๐ŸŽš โš–๏ธ `get_swagger_ui_html()` ๐Ÿ”ข. - -`swagger_ui_parameters` ๐Ÿ“จ ๐Ÿ“– โฎ๏ธ ๐Ÿ“ณ ๐Ÿšถโ€โ™€๏ธ ๐Ÿฆ ๐ŸŽš ๐Ÿ”—. - -FastAPI ๐Ÿ—œ ๐Ÿ“ณ **๐ŸŽป** โš’ ๐Ÿ‘ซ ๐Ÿ”— โฎ๏ธ ๐Ÿ•ธ, ๐Ÿ‘ˆ โšซ๏ธโ” ๐Ÿฆ ๐ŸŽš ๐Ÿ’ช. - -### โŽ โ• ๐ŸŽฆ - -๐Ÿ–ผ, ๐Ÿ‘† ๐Ÿ’ช โŽ โ• ๐ŸŽฆ ๐Ÿฆ ๐ŸŽš. - -๐Ÿต ๐Ÿ”€ โš’, โ• ๐ŸŽฆ ๐Ÿ› ๏ธ ๐Ÿ”ข: - - - -โœ‹๏ธ ๐Ÿ‘† ๐Ÿ’ช โŽ โšซ๏ธ โš’ `syntaxHighlight` `False`: - -```Python hl_lines="3" -{!../../../docs_src/extending_openapi/tutorial003.py!} -``` - -...& โคด๏ธ ๐Ÿฆ ๐ŸŽš ๐Ÿ† ๐Ÿšซ ๐ŸŽฆ โ• ๐ŸŽฆ ๐Ÿšซ๐Ÿ”œ: - - - -### ๐Ÿ”€ ๐ŸŽข - -๐ŸŽ ๐ŸŒŒ ๐Ÿ‘† ๐Ÿ’ช โš’ โ• ๐ŸŽฆ ๐ŸŽข โฎ๏ธ ๐Ÿ”‘ `"syntaxHighlight.theme"` (๐Ÿ‘€ ๐Ÿ‘ˆ โšซ๏ธ โœ”๏ธ โฃ ๐Ÿ–•): - -```Python hl_lines="3" -{!../../../docs_src/extending_openapi/tutorial004.py!} -``` - -๐Ÿ‘ˆ ๐Ÿ“ณ ๐Ÿ”œ ๐Ÿ”€ โ• ๐ŸŽฆ ๐ŸŽจ ๐ŸŽข: - - - -### ๐Ÿ”€ ๐Ÿ”ข ๐Ÿฆ ๐ŸŽš ๐Ÿ”ข - -FastAPI ๐Ÿ”Œ ๐Ÿ”ข ๐Ÿ“ณ ๐Ÿ”ข โ˜‘ ๐ŸŒ… โš™๏ธ ๐Ÿ’ผ. - -โšซ๏ธ ๐Ÿ”Œ ๐Ÿ‘ซ ๐Ÿ”ข ๐Ÿ“ณ: - -```Python -{!../../../fastapi/openapi/docs.py[ln:7-13]!} -``` - -๐Ÿ‘† ๐Ÿ’ช ๐Ÿ” ๐Ÿ™† ๐Ÿ‘ซ โš’ ๐ŸŽ ๐Ÿ’ฒ โŒ `swagger_ui_parameters`. - -๐Ÿ–ผ, โŽ `deepLinking` ๐Ÿ‘† ๐Ÿ’ช ๐Ÿšถโ€โ™€๏ธ ๐Ÿ‘‰ โš’ `swagger_ui_parameters`: - -```Python hl_lines="3" -{!../../../docs_src/extending_openapi/tutorial005.py!} -``` - -### ๐ŸŽ ๐Ÿฆ ๐ŸŽš ๐Ÿ”ข - -๐Ÿ‘€ ๐ŸŒ ๐ŸŽ ๐Ÿ’ช ๐Ÿ“ณ ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ, โœ ๐Ÿ›‚ ๐Ÿฉบ ๐Ÿฆ ๐ŸŽš ๐Ÿ”ข. - -### ๐Ÿ•ธ-๐Ÿ•ด โš’ - -๐Ÿฆ ๐ŸŽš โœ” ๐ŸŽ ๐Ÿ“ณ **๐Ÿ•ธ-๐Ÿ•ด** ๐ŸŽš (๐Ÿ–ผ, ๐Ÿ•ธ ๐Ÿ”ข). - -FastAPI ๐Ÿ”Œ ๐Ÿ‘ซ ๐Ÿ•ธ-๐Ÿ•ด `presets` โš’: - -```JavaScript -presets: [ - SwaggerUIBundle.presets.apis, - SwaggerUIBundle.SwaggerUIStandalonePreset -] -``` - -๐Ÿ‘ซ **๐Ÿ•ธ** ๐ŸŽš, ๐Ÿšซ ๐ŸŽป, ๐Ÿ‘† ๐Ÿ’ช ๐Ÿšซ ๐Ÿšถโ€โ™€๏ธ ๐Ÿ‘ซ โšช๏ธโžก๏ธ ๐Ÿ ๐Ÿ“Ÿ ๐Ÿ”—. - -๐Ÿšฅ ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐Ÿ•ธ-๐Ÿ•ด ๐Ÿ“ณ ๐Ÿ’– ๐Ÿ“š, ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ 1๏ธโƒฃ ๐Ÿ‘ฉโ€๐Ÿ”ฌ ๐Ÿ”›. ๐Ÿ” ๐ŸŒ ๐Ÿฆ ๐ŸŽš *โžก ๐Ÿ› ๏ธ* & โŽ โœ ๐Ÿ™† ๐Ÿ•ธ ๐Ÿ‘† ๐Ÿ’ช. diff --git a/docs/em/docs/deployment/deta.md b/docs/em/docs/deployment/deta.md deleted file mode 100644 index 89b6c4bdbed57..0000000000000 --- a/docs/em/docs/deployment/deta.md +++ /dev/null @@ -1,258 +0,0 @@ -# ๐Ÿ› ๏ธ FastAPI ๐Ÿ”› ๐Ÿช” - -๐Ÿ‘‰ ๐Ÿ“„ ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ’ก โ” ๐Ÿ’ช ๐Ÿ› ๏ธ **FastAPI** ๐Ÿˆธ ๐Ÿ”› ๐Ÿช” โš™๏ธ ๐Ÿ†“ ๐Ÿ“„. ๐Ÿ‘ถ - -โšซ๏ธ ๐Ÿ”œ โœŠ ๐Ÿ‘† ๐Ÿ”ƒ **1๏ธโƒฃ0๏ธโƒฃ โฒ**. - -!!! info - ๐Ÿช” **FastAPI** ๐Ÿ’ฐ. ๐Ÿ‘ถ - -## ๐Ÿ”ฐ **FastAPI** ๐Ÿ“ฑ - -* โœ ๐Ÿ“ ๐Ÿ‘† ๐Ÿ“ฑ, ๐Ÿ–ผ, `./fastapideta/` & โ›” ๐Ÿ”˜ โšซ๏ธ. - -### FastAPI ๐Ÿ“Ÿ - -* โœ `main.py` ๐Ÿ“ โฎ๏ธ: - -```Python -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int): - return {"item_id": item_id} -``` - -### ๐Ÿ“„ - -๐Ÿ”œ, ๐ŸŽ ๐Ÿ“ โœ ๐Ÿ“ `requirements.txt` โฎ๏ธ: - -```text -fastapi -``` - -!!! tip - ๐Ÿ‘† ๐Ÿšซ ๐Ÿ’ช โŽ Uvicorn ๐Ÿ› ๏ธ ๐Ÿ”› ๐Ÿช”, ๐Ÿ‘ ๐Ÿ‘† ๐Ÿ”œ ๐ŸŽฒ ๐Ÿ’š โŽ โšซ๏ธ ๐ŸŒ ๐Ÿ’ฏ ๐Ÿ‘† ๐Ÿ“ฑ. - -### ๐Ÿ“ ๐Ÿ“Š - -๐Ÿ‘† ๐Ÿ”œ ๐Ÿ”œ โœ”๏ธ 1๏ธโƒฃ ๐Ÿ“ `./fastapideta/` โฎ๏ธ 2๏ธโƒฃ ๐Ÿ“: - -``` -. -โ””โ”€โ”€ main.py -โ””โ”€โ”€ requirements.txt -``` - -## โœ ๐Ÿ†“ ๐Ÿช” ๐Ÿง - -๐Ÿ”œ โœ ๐Ÿ†“ ๐Ÿง ๐Ÿ”› ๐Ÿช”, ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ“ง & ๐Ÿ”. - -๐Ÿ‘† ๐Ÿšซ ๐Ÿ’ช ๐Ÿ’ณ. - -## โŽ โœณ - -๐Ÿ• ๐Ÿ‘† โœ”๏ธ ๐Ÿ‘† ๐Ÿง, โŽ ๐Ÿช” โœณ: - -=== "๐Ÿ’พ, ๐Ÿ‡ธ๐Ÿ‡ป" - -
- - ```console - $ curl -fsSL https://get.deta.dev/cli.sh | sh - ``` - -
- -=== "๐Ÿšช ๐Ÿ“‹" - -
- - ```console - $ iwr https://get.deta.dev/cli.ps1 -useb | iex - ``` - -
- -โฎ๏ธ โŽ โšซ๏ธ, ๐Ÿ“‚ ๐Ÿ†• ๐Ÿ“ถ ๐Ÿ‘ˆ โŽ โœณ ๐Ÿ”. - -๐Ÿ†• ๐Ÿ“ถ, โœ” ๐Ÿ‘ˆ โšซ๏ธ โ˜‘ โŽ โฎ๏ธ: - -
- -```console -$ deta --help - -Deta command line interface for managing deta micros. -Complete documentation available at https://docs.deta.sh - -Usage: - deta [flags] - deta [command] - -Available Commands: - auth Change auth settings for a deta micro - -... -``` - -
- -!!! tip - ๐Ÿšฅ ๐Ÿ‘† โœ”๏ธ โš  โŽ โœณ, โœ… ๐Ÿ›‚ ๐Ÿช” ๐Ÿฉบ. - -## ๐Ÿ’ณ โฎ๏ธ โœณ - -๐Ÿ”œ ๐Ÿ’ณ ๐Ÿช” โšช๏ธโžก๏ธ โœณ โฎ๏ธ: - -
- -```console -$ deta login - -Please, log in from the web page. Waiting.. -Logged in successfully. -``` - -
- -๐Ÿ‘‰ ๐Ÿ”œ ๐Ÿ“‚ ๐Ÿ•ธ ๐Ÿ–ฅ & ๐Ÿ”“ ๐Ÿ”. - -## ๐Ÿ› ๏ธ โฎ๏ธ ๐Ÿช” - -โญ, ๐Ÿ› ๏ธ ๐Ÿ‘† ๐Ÿˆธ โฎ๏ธ ๐Ÿช” โœณ: - -
- -```console -$ deta new - -Successfully created a new micro - -// Notice the "endpoint" ๐Ÿ” - -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} - -Adding dependencies... - - ----> 100% - - -Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 -``` - -
- -๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐ŸŽป ๐Ÿ“ง ๐ŸŽ: - -```JSON hl_lines="4" -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} -``` - -!!! tip - ๐Ÿ‘† ๐Ÿ› ๏ธ ๐Ÿ”œ โœ”๏ธ ๐ŸŽ `"endpoint"` ๐Ÿ“›. - -## โœ… โšซ๏ธ - -๐Ÿ”œ ๐Ÿ“‚ ๐Ÿ‘† ๐Ÿ–ฅ ๐Ÿ‘† `endpoint` ๐Ÿ“›. ๐Ÿ–ผ ๐Ÿ”› โšซ๏ธ `https://qltnci.deta.dev`, โœ‹๏ธ ๐Ÿ‘† ๐Ÿ”œ ๐ŸŽ. - -๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐ŸŽป ๐Ÿ“จ โšช๏ธโžก๏ธ ๐Ÿ‘† FastAPI ๐Ÿ“ฑ: - -```JSON -{ - "Hello": "World" -} -``` - -& ๐Ÿ”œ ๐Ÿšถ `/docs` ๐Ÿ‘† ๐Ÿ› ๏ธ, ๐Ÿ–ผ ๐Ÿ”› โšซ๏ธ ๐Ÿ”œ `https://qltnci.deta.dev/docs`. - -โšซ๏ธ ๐Ÿ”œ ๐ŸŽฆ ๐Ÿ‘† ๐Ÿฉบ ๐Ÿ’–: - - - -## ๐Ÿ› ๏ธ ๐Ÿ“ข ๐Ÿ” - -๐Ÿ”ข, ๐Ÿช” ๐Ÿ”œ ๐Ÿต ๐Ÿค โš™๏ธ ๐Ÿช ๐Ÿ‘† ๐Ÿง. - -โœ‹๏ธ ๐Ÿ• ๐Ÿ‘† ๐Ÿ”œ, ๐Ÿ‘† ๐Ÿ’ช โš’ โšซ๏ธ ๐Ÿ“ข โฎ๏ธ: - -
- -```console -$ deta auth disable - -Successfully disabled http auth -``` - -
- -๐Ÿ”œ ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ’ฐ ๐Ÿ‘ˆ ๐Ÿ“› โฎ๏ธ ๐Ÿ™† & ๐Ÿ‘ซ ๐Ÿ”œ ๐Ÿ’ช ๐Ÿ” ๐Ÿ‘† ๐Ÿ› ๏ธ. ๐Ÿ‘ถ - -## ๐Ÿ‡บ๐Ÿ‡ธ๐Ÿ” - -ใŠ— โ— ๐Ÿ‘† ๐Ÿ› ๏ธ ๐Ÿ‘† FastAPI ๐Ÿ“ฑ ๐Ÿช” โ— ๐Ÿ‘ถ ๐Ÿ‘ถ - -, ๐Ÿ‘€ ๐Ÿ‘ˆ ๐Ÿช” โ˜‘ ๐Ÿต ๐Ÿ‡บ๐Ÿ‡ธ๐Ÿ” ๐Ÿ‘†, ๐Ÿ‘† ๐Ÿšซ โœ”๏ธ โœŠ ๐Ÿ’… ๐Ÿ‘ˆ & ๐Ÿ’ช ๐Ÿ’ญ ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ‘ฉโ€๐Ÿ’ป ๐Ÿ”œ โœ”๏ธ ๐Ÿ” ๐Ÿ—œ ๐Ÿ”—. ๐Ÿ‘ถ ๐Ÿ‘ถ - -## โœ… ๐Ÿ•ถ - -โšช๏ธโžก๏ธ ๐Ÿ‘† ๐Ÿฉบ ๐ŸŽš (๐Ÿ‘ซ ๐Ÿ”œ ๐Ÿ“› ๐Ÿ’– `https://qltnci.deta.dev/docs`) ๐Ÿ“จ ๐Ÿ“จ ๐Ÿ‘† *โžก ๐Ÿ› ๏ธ* `/items/{item_id}`. - -๐Ÿ–ผ โฎ๏ธ ๐Ÿ†” `5`. - -๐Ÿ”œ ๐Ÿšถ https://web.deta.sh. - -๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐Ÿ“ค ๐Ÿ“„ โ—€๏ธ ๐Ÿค™ "โ—พ" โฎ๏ธ ๐Ÿ”  ๐Ÿ‘† ๐Ÿ“ฑ. - -๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐Ÿ“‘ โฎ๏ธ "โ„น", & ๐Ÿ“‘ "๐Ÿ•ถ", ๐Ÿšถ ๐Ÿ“‘ "๐Ÿ•ถ". - -๐Ÿ“ค ๐Ÿ‘† ๐Ÿ’ช โœ” โฎ๏ธ ๐Ÿ“จ ๐Ÿ“จ ๐Ÿ‘† ๐Ÿ“ฑ. - -๐Ÿ‘† ๐Ÿ’ช โœ ๐Ÿ‘ซ & ๐Ÿค-๐Ÿคพ ๐Ÿ‘ซ. - - - -## ๐Ÿ’ก ๐ŸŒ… - -โ˜, ๐Ÿ‘† ๐Ÿ”œ ๐ŸŽฒ ๐Ÿ’š ๐Ÿช ๐Ÿ’ฝ ๐Ÿ‘† ๐Ÿ“ฑ ๐ŸŒŒ ๐Ÿ‘ˆ ๐Ÿ˜ฃ ๐Ÿ”˜ ๐Ÿ•ฐ. ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐Ÿช” ๐Ÿงข, โšซ๏ธ โœ”๏ธ ๐Ÿ‘ **๐Ÿ†“ ๐ŸŽš**. - -๐Ÿ‘† ๐Ÿ’ช โœ ๐ŸŒ… ๐Ÿช” ๐Ÿฉบ. - -## ๐Ÿ› ๏ธ ๐Ÿ”ง - -๐Ÿ‘Ÿ ๐Ÿ”™ ๐Ÿ”ง ๐Ÿ‘ฅ ๐Ÿ”ฌ [๐Ÿ› ๏ธ ๐Ÿ”ง](./concepts.md){.internal-link target=_blank}, ๐Ÿ“ฅ โ” ๐Ÿ”  ๐Ÿ‘ซ ๐Ÿ”œ ๐Ÿต โฎ๏ธ ๐Ÿช”: - -* **๐Ÿ‡บ๐Ÿ‡ธ๐Ÿ”**: ๐Ÿต ๐Ÿช”, ๐Ÿ‘ซ ๐Ÿ”œ ๐Ÿค ๐Ÿ‘† ๐Ÿ“ & ๐Ÿต ๐Ÿ‡บ๐Ÿ‡ธ๐Ÿ” ๐Ÿ”. -* **๐Ÿƒโ€โ™‚ ๐Ÿ”› ๐Ÿ•ด**: ๐Ÿต ๐Ÿช”, ๐Ÿ• ๐Ÿ‘ซ ๐Ÿ•โ€๐Ÿฆบ. -* **โ**: ๐Ÿต ๐Ÿช”, ๐Ÿ• ๐Ÿ‘ซ ๐Ÿ•โ€๐Ÿฆบ. -* **๐Ÿงฌ**: ๐Ÿต ๐Ÿช”, ๐Ÿ• ๐Ÿ‘ซ ๐Ÿ•โ€๐Ÿฆบ. -* **๐Ÿ’พ**: ๐Ÿ“‰ ๐Ÿ” ๐Ÿช”, ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ“ง ๐Ÿ‘ซ ๐Ÿ“ˆ โšซ๏ธ. -* **โฎ๏ธ ๐Ÿ” โญ โ–ถ๏ธ**: ๐Ÿšซ ๐Ÿ”— ๐Ÿ•โ€๐Ÿฆบ, ๐Ÿ‘† ๐Ÿ’ช โš’ โšซ๏ธ ๐Ÿ‘ท โฎ๏ธ ๐Ÿ‘ซ ๐Ÿ’พ โš™๏ธ โš–๏ธ ๐ŸŒ– โœ. - -!!! note - ๐Ÿช” ๐Ÿ”ง โš’ โšซ๏ธ โฉ (& ๐Ÿ†“) ๐Ÿ› ๏ธ ๐Ÿ™… ๐Ÿˆธ ๐Ÿ”œ. - - โšซ๏ธ ๐Ÿ’ช ๐Ÿ“‰ ๐Ÿ“š โš™๏ธ ๐Ÿ’ผ, โœ‹๏ธ ๐ŸŽ ๐Ÿ•ฐ, โšซ๏ธ ๐Ÿšซ ๐Ÿ•โ€๐Ÿฆบ ๐ŸŽ, ๐Ÿ’– โš™๏ธ ๐Ÿ”ข ๐Ÿ’ฝ (โ†–๏ธ โšช๏ธโžก๏ธ ๐Ÿช” ๐Ÿ‘ โ˜ ๐Ÿ’ฝ โš™๏ธ), ๐Ÿ›ƒ ๐Ÿ•น ๐ŸŽฐ, โ™’๏ธ. - - ๐Ÿ‘† ๐Ÿ’ช โœ ๐ŸŒ… โ„น ๐Ÿช” ๐Ÿฉบ ๐Ÿ‘€ ๐Ÿšฅ โšซ๏ธ โ–ถ๏ธ๏ธ โš’ ๐Ÿ‘†. diff --git a/docs/em/docs/advanced/conditional-openapi.md b/docs/em/docs/how-to/conditional-openapi.md similarity index 100% rename from docs/em/docs/advanced/conditional-openapi.md rename to docs/em/docs/how-to/conditional-openapi.md diff --git a/docs/em/docs/advanced/custom-request-and-route.md b/docs/em/docs/how-to/custom-request-and-route.md similarity index 100% rename from docs/em/docs/advanced/custom-request-and-route.md rename to docs/em/docs/how-to/custom-request-and-route.md diff --git a/docs/em/docs/how-to/extending-openapi.md b/docs/em/docs/how-to/extending-openapi.md new file mode 100644 index 0000000000000..6b3bc00757940 --- /dev/null +++ b/docs/em/docs/how-to/extending-openapi.md @@ -0,0 +1,90 @@ +# โ†” ๐Ÿ—„ + +!!! warning + ๐Ÿ‘‰ ๐Ÿ‘ ๐Ÿง โš’. ๐Ÿ‘† ๐ŸŽฒ ๐Ÿ’ช ๐Ÿšถ โšซ๏ธ. + + ๐Ÿšฅ ๐Ÿ‘† ๐Ÿ“„ ๐Ÿ”ฐ - ๐Ÿ‘ฉโ€๐Ÿ’ป ๐Ÿฆฎ, ๐Ÿ‘† ๐Ÿ’ช ๐ŸŽฒ ๐Ÿšถ ๐Ÿ‘‰ ๐Ÿ“„. + + ๐Ÿšฅ ๐Ÿ‘† โช ๐Ÿ’ญ ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ”€ ๐Ÿ— ๐Ÿ—„ ๐Ÿ”—, ๐Ÿ˜ฃ ๐Ÿ‘‚. + +๐Ÿ“ค ๐Ÿ’ผ ๐ŸŒโ” ๐Ÿ‘† ๐Ÿ’ช ๐Ÿ’ช ๐Ÿ”€ ๐Ÿ— ๐Ÿ—„ ๐Ÿ”—. + +๐Ÿ‘‰ ๐Ÿ“„ ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ โ”. + +## ๐Ÿ˜ ๐Ÿ› ๏ธ + +๐Ÿ˜ (๐Ÿ”ข) ๐Ÿ› ๏ธ, โฉ. + +`FastAPI` ๐Ÿˆธ (๐Ÿ‘) โœ”๏ธ `.openapi()` ๐Ÿ‘ฉโ€๐Ÿ”ฌ ๐Ÿ‘ˆ ๐Ÿ“ˆ ๐Ÿ“จ ๐Ÿ—„ ๐Ÿ”—. + +๐Ÿ• ๐Ÿˆธ ๐ŸŽš ๐Ÿ—, *โžก ๐Ÿ› ๏ธ* `/openapi.json` (โš–๏ธ โšซ๏ธโ” ๐Ÿ‘† โš’ ๐Ÿ‘† `openapi_url`) ยฎ. + +โšซ๏ธ ๐Ÿ“จ ๐ŸŽป ๐Ÿ“จ โฎ๏ธ ๐Ÿ ๐Ÿˆธ `.openapi()` ๐Ÿ‘ฉโ€๐Ÿ”ฌ. + +๐Ÿ”ข, โšซ๏ธโ” ๐Ÿ‘ฉโ€๐Ÿ”ฌ `.openapi()` ๐Ÿ”จ โœ… ๐Ÿ  `.openapi_schema` ๐Ÿ‘€ ๐Ÿšฅ โšซ๏ธ โœ”๏ธ ๐ŸŽš & ๐Ÿ“จ ๐Ÿ‘ซ. + +๐Ÿšฅ โšซ๏ธ ๐Ÿšซ, โšซ๏ธ ๐Ÿ— ๐Ÿ‘ซ โš™๏ธ ๐Ÿš™ ๐Ÿ”ข `fastapi.openapi.utils.get_openapi`. + +& ๐Ÿ‘ˆ ๐Ÿ”ข `get_openapi()` ๐Ÿ“จ ๐Ÿ”ข: + +* `title`: ๐Ÿ—„ ๐Ÿ“›, ๐ŸŽฆ ๐Ÿฉบ. +* `version`: โฌ ๐Ÿ‘† ๐Ÿ› ๏ธ, โœ… `2.5.0`. +* `openapi_version`: โฌ ๐Ÿ—„ ๐Ÿ”ง โš™๏ธ. ๐Ÿ”ข, โช: `3.0.2`. +* `description`: ๐Ÿ“› ๐Ÿ‘† ๐Ÿ› ๏ธ. +* `routes`: ๐Ÿ“‡ ๐Ÿ›ฃ, ๐Ÿ‘ซ ๐Ÿ”  ยฎ *โžก ๐Ÿ› ๏ธ*. ๐Ÿ‘ซ โœŠ โšช๏ธโžก๏ธ `app.routes`. + +## ๐Ÿ”‘ ๐Ÿ”ข + +โš™๏ธ โ„น ๐Ÿ”›, ๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐ŸŽ ๐Ÿš™ ๐Ÿ”ข ๐Ÿ— ๐Ÿ—„ ๐Ÿ”— & ๐Ÿ” ๐Ÿ”  ๐Ÿ• ๐Ÿ‘ˆ ๐Ÿ‘† ๐Ÿ’ช. + +๐Ÿ–ผ, โžก๏ธ ๐Ÿšฎ ๐Ÿ“„ ๐Ÿ—„ โ†” ๐Ÿ”Œ ๐Ÿ›ƒ ๐Ÿ”ฑ. + +### ๐Ÿ˜ **FastAPI** + +๐Ÿฅ‡, โœ ๐ŸŒ ๐Ÿ‘† **FastAPI** ๐Ÿˆธ ๐Ÿ›Ž: + +```Python hl_lines="1 4 7-9" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### ๐Ÿ— ๐Ÿ—„ ๐Ÿ”— + +โคด๏ธ, โš™๏ธ ๐ŸŽ ๐Ÿš™ ๐Ÿ”ข ๐Ÿ— ๐Ÿ—„ ๐Ÿ”—, ๐Ÿ”˜ `custom_openapi()` ๐Ÿ”ข: + +```Python hl_lines="2 15-20" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### ๐Ÿ”€ ๐Ÿ—„ ๐Ÿ”— + +๐Ÿ”œ ๐Ÿ‘† ๐Ÿ’ช ๐Ÿšฎ ๐Ÿ“„ โ†”, โŽ ๐Ÿ›ƒ `x-logo` `info` "๐ŸŽš" ๐Ÿ—„ ๐Ÿ”—: + +```Python hl_lines="21-23" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### ๐Ÿ’พ ๐Ÿ—„ ๐Ÿ”— + +๐Ÿ‘† ๐Ÿ’ช โš™๏ธ ๐Ÿ  `.openapi_schema` "๐Ÿ’พ", ๐Ÿช ๐Ÿ‘† ๐Ÿ— ๐Ÿ”—. + +๐Ÿ‘ˆ ๐ŸŒŒ, ๐Ÿ‘† ๐Ÿˆธ ๐Ÿ† ๐Ÿšซ โœ”๏ธ ๐Ÿ— ๐Ÿ”— ๐Ÿ”  ๐Ÿ•ฐ ๐Ÿ‘ฉโ€๐Ÿ’ป ๐Ÿ“‚ ๐Ÿ‘† ๐Ÿ› ๏ธ ๐Ÿฉบ. + +โšซ๏ธ ๐Ÿ”œ ๐Ÿ— ๐Ÿ•ด ๐Ÿ•, & โคด๏ธ ๐ŸŽ ๐Ÿ’พ ๐Ÿ”— ๐Ÿ”œ โš™๏ธ โญ ๐Ÿ“จ. + +```Python hl_lines="13-14 24-25" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### ๐Ÿ” ๐Ÿ‘ฉโ€๐Ÿ”ฌ + +๐Ÿ”œ ๐Ÿ‘† ๐Ÿ’ช โŽ `.openapi()` ๐Ÿ‘ฉโ€๐Ÿ”ฌ โฎ๏ธ ๐Ÿ‘† ๐Ÿ†• ๐Ÿ”ข. + +```Python hl_lines="28" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### โœ… โšซ๏ธ + +๐Ÿ• ๐Ÿ‘† ๐Ÿšถ http://127.0.0.1:8000/redoc ๐Ÿ‘† ๐Ÿ”œ ๐Ÿ‘€ ๐Ÿ‘ˆ ๐Ÿ‘† โš™๏ธ ๐Ÿ‘† ๐Ÿ›ƒ ๐Ÿ”ฑ (๐Ÿ‘‰ ๐Ÿ–ผ, **FastAPI**'โ“‚ ๐Ÿ”ฑ): + + diff --git a/docs/em/docs/advanced/graphql.md b/docs/em/docs/how-to/graphql.md similarity index 100% rename from docs/em/docs/advanced/graphql.md rename to docs/em/docs/how-to/graphql.md diff --git a/docs/em/docs/advanced/sql-databases-peewee.md b/docs/em/docs/how-to/sql-databases-peewee.md similarity index 100% rename from docs/em/docs/advanced/sql-databases-peewee.md rename to docs/em/docs/how-to/sql-databases-peewee.md diff --git a/docs/en/data/sponsors.yml b/docs/en/data/sponsors.yml index 6d9119520409f..0d9597f077b70 100644 --- a/docs/en/data/sponsors.yml +++ b/docs/en/data/sponsors.yml @@ -33,6 +33,9 @@ silver: - url: https://databento.com/ title: Pay as you go for market data img: https://fastapi.tiangolo.com/img/sponsors/databento.svg + - url: https://speakeasyapi.dev?utm_source=fastapi+repo&utm_medium=github+sponsorship + title: SDKs for your API | Speakeasy + img: https://fastapi.tiangolo.com/img/sponsors/speakeasy.png bronze: - url: https://www.exoflare.com/open-source/?utm_source=FastAPI&utm_campaign=open_source title: Biosecurity risk assessments made easy. diff --git a/docs/en/data/sponsors_badge.yml b/docs/en/data/sponsors_badge.yml index 7c3bb2f479aaf..7b605e0ffe0ab 100644 --- a/docs/en/data/sponsors_badge.yml +++ b/docs/en/data/sponsors_badge.yml @@ -19,3 +19,4 @@ logins: - Flint-company - porter-dev - fern-api + - ndimares diff --git a/docs/en/docs/advanced/extending-openapi.md b/docs/en/docs/advanced/extending-openapi.md deleted file mode 100644 index bec184deec34d..0000000000000 --- a/docs/en/docs/advanced/extending-openapi.md +++ /dev/null @@ -1,318 +0,0 @@ -# Extending OpenAPI - -!!! warning - This is a rather advanced feature. You probably can skip it. - - If you are just following the tutorial - user guide, you can probably skip this section. - - If you already know that you need to modify the generated OpenAPI schema, continue reading. - -There are some cases where you might need to modify the generated OpenAPI schema. - -In this section you will see how. - -## The normal process - -The normal (default) process, is as follows. - -A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema. - -As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered. - -It just returns a JSON response with the result of the application's `.openapi()` method. - -By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them. - -If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`. - -And that function `get_openapi()` receives as parameters: - -* `title`: The OpenAPI title, shown in the docs. -* `version`: The version of your API, e.g. `2.5.0`. -* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.1.0`. -* `summary`: A short summary of the API. -* `description`: The description of your API, this can include markdown and will be shown in the docs. -* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`. - -!!! info - The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by FastAPI 0.99.0 and above. - -## Overriding the defaults - -Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need. - -For example, let's add ReDoc's OpenAPI extension to include a custom logo. - -### Normal **FastAPI** - -First, write all your **FastAPI** application as normally: - -```Python hl_lines="1 4 7-9" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### Generate the OpenAPI schema - -Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function: - -```Python hl_lines="2 15-21" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### Modify the OpenAPI schema - -Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema: - -```Python hl_lines="22-24" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### Cache the OpenAPI schema - -You can use the property `.openapi_schema` as a "cache", to store your generated schema. - -That way, your application won't have to generate the schema every time a user opens your API docs. - -It will be generated only once, and then the same cached schema will be used for the next requests. - -```Python hl_lines="13-14 25-26" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### Override the method - -Now you can replace the `.openapi()` method with your new function. - -```Python hl_lines="29" -{!../../../docs_src/extending_openapi/tutorial001.py!} -``` - -### Check it - -Once you go to http://127.0.0.1:8000/redoc you will see that you are using your custom logo (in this example, **FastAPI**'s logo): - - - -## Self-hosting JavaScript and CSS for docs - -The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files. - -By default, those files are served from a CDN. - -But it's possible to customize it, you can set a specific CDN, or serve the files yourself. - -That's useful, for example, if you need your app to keep working even while offline, without open Internet access, or in a local network. - -Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them. - -### Project file structure - -Let's say your project file structure looks like this: - -``` -. -โ”œโ”€โ”€ app -โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”œโ”€โ”€ main.py -``` - -Now create a directory to store those static files. - -Your new file structure could look like this: - -``` -. -โ”œโ”€โ”€ app -โ”‚ย ย  โ”œโ”€โ”€ __init__.py -โ”‚ย ย  โ”œโ”€โ”€ main.py -โ””โ”€โ”€ static/ -``` - -### Download the files - -Download the static files needed for the docs and put them on that `static/` directory. - -You can probably right-click each link and select an option similar to `Save link as...`. - -**Swagger UI** uses the files: - -* `swagger-ui-bundle.js` -* `swagger-ui.css` - -And **ReDoc** uses the file: - -* `redoc.standalone.js` - -After that, your file structure could look like: - -``` -. -โ”œโ”€โ”€ app -โ”‚ย ย  โ”œโ”€โ”€ __init__.py -โ”‚ย ย  โ”œโ”€โ”€ main.py -โ””โ”€โ”€ static - โ”œโ”€โ”€ redoc.standalone.js - โ”œโ”€โ”€ swagger-ui-bundle.js - โ””โ”€โ”€ swagger-ui.css -``` - -### Serve the static files - -* Import `StaticFiles`. -* "Mount" a `StaticFiles()` instance in a specific path. - -```Python hl_lines="7 11" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -### Test the static files - -Start your application and go to http://127.0.0.1:8000/static/redoc.standalone.js. - -You should see a very long JavaScript file for **ReDoc**. - -It could start with something like: - -```JavaScript -/*! - * ReDoc - OpenAPI/Swagger-generated API Reference Documentation - * ------------------------------------------------------------- - * Version: "2.0.0-rc.18" - * Repo: https://github.com/Redocly/redoc - */ -!function(e,t){"object"==typeof exports&&"object"==typeof m - -... -``` - -That confirms that you are being able to serve static files from your app, and that you placed the static files for the docs in the correct place. - -Now we can configure the app to use those static files for the docs. - -### Disable the automatic docs - -The first step is to disable the automatic docs, as those use the CDN by default. - -To disable them, set their URLs to `None` when creating your `FastAPI` app: - -```Python hl_lines="9" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -### Include the custom docs - -Now you can create the *path operations* for the custom docs. - -You can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: - -* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. -* `title`: the title of your API. -* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. -* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. This is the one that your own app is now serving. -* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. This is the one that your own app is now serving. - -And similarly for ReDoc... - -```Python hl_lines="2-6 14-22 25-27 30-36" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -!!! tip - The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. - - If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. - - Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. - -### Create a *path operation* to test it - -Now, to be able to test that everything works, create a *path operation*: - -```Python hl_lines="39-41" -{!../../../docs_src/extending_openapi/tutorial002.py!} -``` - -### Test it - -Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page. - -And even without Internet, you would be able to see the docs for your API and interact with it. - -## Configuring Swagger UI - -You can configure some extra Swagger UI parameters. - -To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. - -`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. - -FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. - -### Disable Syntax Highlighting - -For example, you could disable syntax highlighting in Swagger UI. - -Without changing the settings, syntax highlighting is enabled by default: - - - -But you can disable it by setting `syntaxHighlight` to `False`: - -```Python hl_lines="3" -{!../../../docs_src/extending_openapi/tutorial003.py!} -``` - -...and then Swagger UI won't show the syntax highlighting anymore: - - - -### Change the Theme - -The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): - -```Python hl_lines="3" -{!../../../docs_src/extending_openapi/tutorial004.py!} -``` - -That configuration would change the syntax highlighting color theme: - - - -### Change Default Swagger UI Parameters - -FastAPI includes some default configuration parameters appropriate for most of the use cases. - -It includes these default configurations: - -```Python -{!../../../fastapi/openapi/docs.py[ln:7-13]!} -``` - -You can override any of them by setting a different value in the argument `swagger_ui_parameters`. - -For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: - -```Python hl_lines="3" -{!../../../docs_src/extending_openapi/tutorial005.py!} -``` - -### Other Swagger UI Parameters - -To see all the other possible configurations you can use, read the official docs for Swagger UI parameters. - -### JavaScript-only settings - -Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). - -FastAPI also includes these JavaScript-only `presets` settings: - -```JavaScript -presets: [ - SwaggerUIBundle.presets.apis, - SwaggerUIBundle.SwaggerUIStandalonePreset -] -``` - -These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. - -If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. diff --git a/docs/en/docs/advanced/generate-clients.md b/docs/en/docs/advanced/generate-clients.md index 3fed48b0bcf93..f439ed93ab54b 100644 --- a/docs/en/docs/advanced/generate-clients.md +++ b/docs/en/docs/advanced/generate-clients.md @@ -12,10 +12,18 @@ A common tool is openapi-typescript-codegen. -Another option you could consider for several languages is Fern. +## Client and SDK Generators - Sponsor -!!! info - Fern is also a FastAPI sponsor. ๐Ÿ˜Ž๐ŸŽ‰ +There are also some **company-backed** Client and SDK generators based on OpenAPI (FastAPI), in some cases they can offer you **additional features** on top of high-quality generated SDKs/clients. + +Some of them also โœจ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} โœจ, this ensures the continued and healthy **development** of FastAPI and its **ecosystem**. + +And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good service** but also want to make sure you have a **good and healthy framework**, FastAPI. ๐Ÿ™‡ + +You might want to try their services and follow their guides: + +* Fern +* Speakeasy ## Generate a TypeScript Frontend Client diff --git a/docs/en/docs/advanced/index.md b/docs/en/docs/advanced/index.md index 467f0833e60a6..d8dcd4ca6790a 100644 --- a/docs/en/docs/advanced/index.md +++ b/docs/en/docs/advanced/index.md @@ -17,8 +17,17 @@ You could still use most of the features in **FastAPI** with the knowledge from And the next sections assume you already read it, and assume that you know those main ideas. -## TestDriven.io course +## External Courses -If you would like to take an advanced-beginner course to complement this section of the docs, you might want to check: Test-Driven Development with FastAPI and Docker by **TestDriven.io**. +Although the [Tutorial - User Guide](../tutorial/){.internal-link target=_blank} and this **Advanced User Guide** are written as a guided tutorial (like a book) and should be enough for you to **learn FastAPI**, you might want to complement it with additional courses. -They are currently donating 10% of all profits to the development of **FastAPI**. ๐ŸŽ‰ ๐Ÿ˜„ +Or it might be the case that you just prefer to take other courses because they adapt better to your learning style. + +Some course providers โœจ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} โœจ, this ensures the continued and healthy **development** of FastAPI and its **ecosystem**. + +And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good learning experience** but also want to make sure you have a **good and healthy framework**, FastAPI. ๐Ÿ™‡ + +You might want to try their courses: + +* Talk Python Training +* Test-Driven Development diff --git a/docs/en/docs/css/custom.css b/docs/en/docs/css/custom.css index 066b51725ecce..187040792b1e8 100644 --- a/docs/en/docs/css/custom.css +++ b/docs/en/docs/css/custom.css @@ -144,3 +144,39 @@ code { margin-top: 2em; margin-bottom: 2em; } + +/* Screenshots */ +/* +Simulate a browser window frame. +Inspired by Termynal's CSS tricks with modifications +*/ + +.screenshot { + display: block; + background-color: #d3e0de; + border-radius: 4px; + padding: 45px 5px 5px; + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.screenshot img { + display: block; + border-radius: 2px; +} + +.screenshot:before { + content: ''; + position: absolute; + top: 15px; + left: 15px; + display: inline-block; + width: 15px; + height: 15px; + border-radius: 50%; + /* A little hack to display the window buttons in one pseudo element. */ + background: #d9515d; + -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; + box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; +} diff --git a/docs/en/docs/deployment/cloud.md b/docs/en/docs/deployment/cloud.md new file mode 100644 index 0000000000000..b2836aeb49deb --- /dev/null +++ b/docs/en/docs/deployment/cloud.md @@ -0,0 +1,17 @@ +# Deploy FastAPI on Cloud Providers + +You can use virtually **any cloud provider** to deploy your FastAPI application. + +In most of the cases, the main cloud providers have guides to deploy FastAPI with them. + +## Cloud Providers - Sponsors + +Some cloud providers โœจ [**sponsor FastAPI**](../help-fastapi.md#sponsor-the-author){.internal-link target=_blank} โœจ, this ensures the continued and healthy **development** of FastAPI and its **ecosystem**. + +And it shows their true commitment to FastAPI and its **community** (you), as they not only want to provide you a **good service** but also want to make sure you have a **good and healthy framework**, FastAPI. ๐Ÿ™‡ + +You might want to try their services and follow their guides: + +* Platform.sh +* Porter +* Deta diff --git a/docs/en/docs/deployment/deta.md b/docs/en/docs/deployment/deta.md deleted file mode 100644 index 229d7fd5d8eb4..0000000000000 --- a/docs/en/docs/deployment/deta.md +++ /dev/null @@ -1,391 +0,0 @@ -# Deploy FastAPI on Deta Space - -In this section you will learn how to easily deploy a **FastAPI** application on Deta Space, for free. ๐ŸŽ - -It will take you about **10 minutes** to deploy an API that you can use. After that, you can optionally release it to anyone. - -Let's dive in. - -!!! info - Deta is a **FastAPI** sponsor. ๐ŸŽ‰ - -## A simple **FastAPI** app - -* To start, create an empty directory with the name of your app, for example `./fastapi-deta/`, and then navigate into it. - -```console -$ mkdir fastapi-deta -$ cd fastapi-deta -``` - -### FastAPI code - -* Create a `main.py` file with: - -```Python -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int): - return {"item_id": item_id} -``` - -### Requirements - -Now, in the same directory create a file `requirements.txt` with: - -```text -fastapi -uvicorn[standard] -``` - -### Directory structure - -You will now have a directory `./fastapi-deta/` with two files: - -``` -. -โ””โ”€โ”€ main.py -โ””โ”€โ”€ requirements.txt -``` - -## Create a free **Deta Space** account - -Next, create a free account on Deta Space, you just need an email and password. - -You don't even need a credit card, but make sure **Developer Mode** is enabled when you sign up. - - -## Install the CLI - -Once you have your account, install the Deta Space CLI: - -=== "Linux, macOS" - -
- - ```console - $ curl -fsSL https://get.deta.dev/space-cli.sh | sh - ``` - -
- -=== "Windows PowerShell" - -
- - ```console - $ iwr https://get.deta.dev/space-cli.ps1 -useb | iex - ``` - -
- -After installing it, open a new terminal so that the installed CLI is detected. - -In a new terminal, confirm that it was correctly installed with: - -
- -```console -$ space --help - -Deta command line interface for managing deta micros. -Complete documentation available at https://deta.space/docs - -Usage: - space [flags] - space [command] - -Available Commands: - help Help about any command - link link code to project - login login to space - new create new project - push push code for project - release create release for a project - validate validate spacefile in dir - version Space CLI version -... -``` - -
- -!!! tip - If you have problems installing the CLI, check the official Deta Space Documentation. - -## Login with the CLI - -In order to authenticate your CLI with Deta Space, you will need an access token. - -To obtain this token, open your Deta Space Canvas, open the **Teletype** (command bar at the bottom of the Canvas), and then click on **Settings**. From there, select **Generate Token** and copy the resulting token. - - - -Now run `space login` from the Space CLI. Upon pasting the token into the CLI prompt and pressing enter, you should see a confirmation message. - -
- -```console -$ space login - -To authenticate the Space CLI with your Space account, generate a new access token in your Space settings and paste it below: - -# Enter access token (41 chars) >$ ***************************************** - -๐Ÿ‘ Login Successful! -``` - -
- -## Create a new project in Space - -Now that you've authenticated with the Space CLI, use it to create a new Space Project: - -```console -$ space new - -# What is your project's name? >$ fastapi-deta -``` - -The Space CLI will ask you to name the project, we will call ours `fastapi-deta`. - -Then, it will try to automatically detect which framework or language you are using, showing you what it finds. In our case it will identify the Python app with the following message, prompting you to confirm: - -```console -โš™๏ธ No Spacefile found, trying to auto-detect configuration ... -๐Ÿ‘‡ Deta detected the following configuration: - -Micros: -name: fastapi-deta - L src: . - L engine: python3.9 - -# Do you want to bootstrap "fastapi-deta" with this configuration? (y/n)$ y -``` - -After you confirm, your project will be created in Deta Space inside a special app called Builder. Builder is a toolbox that helps you to create and manage your apps in Deta Space. - -The CLI will also create a `Spacefile` locally in the `fastapi-deta` directory. The Spacefile is a configuration file which tells Deta Space how to run your app. The `Spacefile` for your app will be as follows: - -```yaml -v: 0 -micros: - - name: fastapi-deta - src: . - engine: python3.9 -``` - -It is a `yaml` file, and you can use it to add features like scheduled tasks or modify how your app functions, which we'll do later. To learn more, read the `Spacefile` documentation. - -!!! tip - The Space CLI will also create a hidden `.space` folder in your local directory to link your local environment with Deta Space. This folder should not be included in your version control and will automatically be added to your `.gitignore` file, if you have initialized a Git repository. - -## Define the run command in the Spacefile - -The `run` command in the Spacefile tells Space what command should be executed to start your app. In this case it would be `uvicorn main:app`. - -```diff -v: 0 -micros: - - name: fastapi-deta - src: . - engine: python3.9 -+ run: uvicorn main:app -``` - -## Deploy to Deta Space - -To get your FastAPI live in the cloud, use one more CLI command: - -
- -```console -$ space push - ----> 100% - -build complete... created revision: satyr-jvjk - -โœ” Successfully pushed your code and created a new Revision! -โ„น Updating your development instance with the latest Revision, it will be available on your Canvas shortly. -``` -
- -This command will package your code, upload all the necessary files to Deta Space, and run a remote build of your app, resulting in a **revision**. Whenever you run `space push` successfully, a live instance of your API is automatically updated with the latest revision. - -!!! tip - You can manage your revisions by opening your project in the Builder app. The live copy of your API will be visible under the **Develop** tab in Builder. - -## Check it - -The live instance of your API will also be added automatically to your Canvas (the dashboard) on Deta Space. - - - -Click on the new app called `fastapi-deta`, and it will open your API in a new browser tab on a URL like `https://fastapi-deta-gj7ka8.deta.app/`. - -You will get a JSON response from your FastAPI app: - -```JSON -{ - "Hello": "World" -} -``` - -And now you can head over to the `/docs` of your API. For this example, it would be `https://fastapi-deta-gj7ka8.deta.app/docs`. - - - -## Enable public access - -Deta will handle authentication for your account using cookies. By default, every app or API that you `push` or install to your Space is personal - it's only accessible to you. - -But you can also make your API public using the `Spacefile` from earlier. - -With a `public_routes` parameter, you can specify which paths of your API should be available to the public. - -Set your `public_routes` to `"*"` to open every route of your API to the public: - -```yaml -v: 0 -micros: - - name: fastapi-deta - src: . - engine: python3.9 - public_routes: - - "/*" -``` - -Then run `space push` again to update your live API on Deta Space. - -Once it deploys, you can share your URL with anyone and they will be able to access your API. ๐Ÿš€ - -## HTTPS - -Congrats! You deployed your FastAPI app to Deta Space! ๐ŸŽ‰ ๐Ÿฐ - -Also, notice that Deta Space correctly handles HTTPS for you, so you don't have to take care of that and can be sure that your users will have a secure encrypted connection. โœ… ๐Ÿ”’ - -## Create a release - -Space also allows you to publish your API. When you publish it, anyone else can install their own copy of your API, in their own Deta Space cloud. - -To do so, run `space release` in the Space CLI to create an **unlisted release**: - -
- -```console -$ space release - -# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y - -~ Creating a Release with the latest Revision - ----> 100% - -creating release... -publishing release in edge locations.. -completed... -released: fastapi-deta-exp-msbu -https://deta.space/discovery/r/5kjhgyxewkdmtotx - - Lift off -- successfully created a new Release! - Your Release is available globally on 5 Deta Edges - Anyone can install their own copy of your app. -``` -
- -This command publishes your revision as a release and gives you a link. Anyone you give this link to can install your API. - - -You can also make your app publicly discoverable by creating a **listed release** with `space release --listed` in the Space CLI: - -
- -```console -$ space release --listed - -# Do you want to use the latest revision (buzzard-hczt)? (y/n)$ y - -~ Creating a listed Release with the latest Revision ... - -creating release... -publishing release in edge locations.. -completed... -released: fastapi-deta-exp-msbu -https://deta.space/discovery/@user/fastapi-deta - - Lift off -- successfully created a new Release! - Your Release is available globally on 5 Deta Edges - Anyone can install their own copy of your app. - Listed on Discovery for others to find! -``` -
- -This will allow anyone to find and install your app via Deta Discovery. Read more about releasing your app in the docs. - -## Check runtime logs - -Deta Space also lets you inspect the logs of every app you build or install. - -Add some logging functionality to your app by adding a `print` statement to your `main.py` file. - -```py -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int): - print(item_id) - return {"item_id": item_id} -``` - -The code within the `read_item` function includes a print statement that will output the `item_id` that is included in the URL. Send a request to your _path operation_ `/items/{item_id}` from the docs UI (which will have a URL like `https://fastapi-deta-gj7ka8.deta.app/docs`), using an ID like `5` as an example. - -Now go to your Space's Canvas. Click on the context menu (`...`) of your live app instance, and then click on **View Logs**. Here you can view your app's logs, sorted by time. - - - -## Learn more - -At some point, you will probably want to store some data for your app in a way that persists through time. For that you can use Deta Base and Deta Drive, both of which have a generous **free tier**. - -You can also read more in the Deta Space Documentation. - -!!! tip - If you have any Deta related questions, comments, or feedback, head to the Deta Discord server. - - -## Deployment Concepts - -Coming back to the concepts we discussed in [Deployments Concepts](./concepts.md){.internal-link target=_blank}, here's how each of them would be handled with Deta Space: - -- **HTTPS**: Handled by Deta Space, they will give you a subdomain and handle HTTPS automatically. -- **Running on startup**: Handled by Deta Space, as part of their service. -- **Restarts**: Handled by Deta Space, as part of their service. -- **Replication**: Handled by Deta Space, as part of their service. -- **Authentication**: Handled by Deta Space, as part of their service. -- **Memory**: Limit predefined by Deta Space, you could contact them to increase it. -- **Previous steps before starting**: Can be configured using the `Spacefile`. - -!!! note - Deta Space is designed to make it easy and free to build cloud applications for yourself. Then you can optionally share them with anyone. - - It can simplify several use cases, but at the same time, it doesn't support others, like using external databases (apart from Deta's own NoSQL database system), custom virtual machines, etc. - - You can read more details in the Deta Space Documentation to see if it's the right choice for you. diff --git a/docs/en/docs/advanced/async-sql-databases.md b/docs/en/docs/how-to/async-sql-encode-databases.md similarity index 98% rename from docs/en/docs/advanced/async-sql-databases.md rename to docs/en/docs/how-to/async-sql-encode-databases.md index 12549a1903cf7..697167f790267 100644 --- a/docs/en/docs/advanced/async-sql-databases.md +++ b/docs/en/docs/how-to/async-sql-encode-databases.md @@ -1,4 +1,4 @@ -# Async SQL (Relational) Databases +# Async SQL (Relational) Databases with Encode/Databases !!! info These docs are about to be updated. ๐ŸŽ‰ diff --git a/docs/en/docs/advanced/conditional-openapi.md b/docs/en/docs/how-to/conditional-openapi.md similarity index 100% rename from docs/en/docs/advanced/conditional-openapi.md rename to docs/en/docs/how-to/conditional-openapi.md diff --git a/docs/en/docs/how-to/configure-swagger-ui.md b/docs/en/docs/how-to/configure-swagger-ui.md new file mode 100644 index 0000000000000..f36ba5ba8c230 --- /dev/null +++ b/docs/en/docs/how-to/configure-swagger-ui.md @@ -0,0 +1,78 @@ +# Configure Swagger UI + +You can configure some extra Swagger UI parameters. + +To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. + +`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. + +FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. + +## Disable Syntax Highlighting + +For example, you could disable syntax highlighting in Swagger UI. + +Without changing the settings, syntax highlighting is enabled by default: + + + +But you can disable it by setting `syntaxHighlight` to `False`: + +```Python hl_lines="3" +{!../../../docs_src/configure_swagger_ui/tutorial001.py!} +``` + +...and then Swagger UI won't show the syntax highlighting anymore: + + + +## Change the Theme + +The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): + +```Python hl_lines="3" +{!../../../docs_src/configure_swagger_ui/tutorial002.py!} +``` + +That configuration would change the syntax highlighting color theme: + + + +## Change Default Swagger UI Parameters + +FastAPI includes some default configuration parameters appropriate for most of the use cases. + +It includes these default configurations: + +```Python +{!../../../fastapi/openapi/docs.py[ln:7-13]!} +``` + +You can override any of them by setting a different value in the argument `swagger_ui_parameters`. + +For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: + +```Python hl_lines="3" +{!../../../docs_src/configure_swagger_ui/tutorial003.py!} +``` + +## Other Swagger UI Parameters + +To see all the other possible configurations you can use, read the official docs for Swagger UI parameters. + +## JavaScript-only settings + +Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). + +FastAPI also includes these JavaScript-only `presets` settings: + +```JavaScript +presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIBundle.SwaggerUIStandalonePreset +] +``` + +These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. + +If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. diff --git a/docs/en/docs/how-to/custom-docs-ui-assets.md b/docs/en/docs/how-to/custom-docs-ui-assets.md new file mode 100644 index 0000000000000..f263248692192 --- /dev/null +++ b/docs/en/docs/how-to/custom-docs-ui-assets.md @@ -0,0 +1,199 @@ +# Custom Docs UI Static Assets (Self-Hosting) + +The API docs use **Swagger UI** and **ReDoc**, and each of those need some JavaScript and CSS files. + +By default, those files are served from a CDN. + +But it's possible to customize it, you can set a specific CDN, or serve the files yourself. + +## Custom CDN for JavaScript and CSS + +Let's say that you want to use a different CDN, for example you want to use `https://unpkg.com/`. + +This could be useful if for example you live in a country that restricts some URLs. + +### Disable the automatic docs + +The first step is to disable the automatic docs, as by default, those use the default CDN. + +To disable them, set their URLs to `None` when creating your `FastAPI` app: + +```Python hl_lines="8" +{!../../../docs_src/custom_docs_ui/tutorial001.py!} +``` + +### Include the custom docs + +Now you can create the *path operations* for the custom docs. + +You can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: + +* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. +* `title`: the title of your API. +* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. +* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. This is the custom CDN URL. +* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. This is the custom CDN URL. + +And similarly for ReDoc... + +```Python hl_lines="2-6 11-19 22-24 27-33" +{!../../../docs_src/custom_docs_ui/tutorial001.py!} +``` + +!!! tip + The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. + + If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. + + Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. + +### Create a *path operation* to test it + +Now, to be able to test that everything works, create a *path operation*: + +```Python hl_lines="36-38" +{!../../../docs_src/custom_docs_ui/tutorial001.py!} +``` + +### Test it + +Now, you should be able to go to your docs at http://127.0.0.1:8000/docs, and reload the page, it will load those assets from the new CDN. + +## Self-hosting JavaScript and CSS for docs + +Self-hosting the JavaScript and CSS could be useful if, for example, you need your app to keep working even while offline, without open Internet access, or in a local network. + +Here you'll see how to serve those files yourself, in the same FastAPI app, and configure the docs to use them. + +### Project file structure + +Let's say your project file structure looks like this: + +``` +. +โ”œโ”€โ”€ app +โ”‚ โ”œโ”€โ”€ __init__.py +โ”‚ โ”œโ”€โ”€ main.py +``` + +Now create a directory to store those static files. + +Your new file structure could look like this: + +``` +. +โ”œโ”€โ”€ app +โ”‚ย ย  โ”œโ”€โ”€ __init__.py +โ”‚ย ย  โ”œโ”€โ”€ main.py +โ””โ”€โ”€ static/ +``` + +### Download the files + +Download the static files needed for the docs and put them on that `static/` directory. + +You can probably right-click each link and select an option similar to `Save link as...`. + +**Swagger UI** uses the files: + +* `swagger-ui-bundle.js` +* `swagger-ui.css` + +And **ReDoc** uses the file: + +* `redoc.standalone.js` + +After that, your file structure could look like: + +``` +. +โ”œโ”€โ”€ app +โ”‚ย ย  โ”œโ”€โ”€ __init__.py +โ”‚ย ย  โ”œโ”€โ”€ main.py +โ””โ”€โ”€ static + โ”œโ”€โ”€ redoc.standalone.js + โ”œโ”€โ”€ swagger-ui-bundle.js + โ””โ”€โ”€ swagger-ui.css +``` + +### Serve the static files + +* Import `StaticFiles`. +* "Mount" a `StaticFiles()` instance in a specific path. + +```Python hl_lines="7 11" +{!../../../docs_src/custom_docs_ui/tutorial002.py!} +``` + +### Test the static files + +Start your application and go to http://127.0.0.1:8000/static/redoc.standalone.js. + +You should see a very long JavaScript file for **ReDoc**. + +It could start with something like: + +```JavaScript +/*! + * ReDoc - OpenAPI/Swagger-generated API Reference Documentation + * ------------------------------------------------------------- + * Version: "2.0.0-rc.18" + * Repo: https://github.com/Redocly/redoc + */ +!function(e,t){"object"==typeof exports&&"object"==typeof m + +... +``` + +That confirms that you are being able to serve static files from your app, and that you placed the static files for the docs in the correct place. + +Now we can configure the app to use those static files for the docs. + +### Disable the automatic docs for static files + +The same as when using a custom CDN, the first step is to disable the automatic docs, as those use the CDN by default. + +To disable them, set their URLs to `None` when creating your `FastAPI` app: + +```Python hl_lines="9" +{!../../../docs_src/custom_docs_ui/tutorial002.py!} +``` + +### Include the custom docs for static files + +And the same way as with a custom CDN, now you can create the *path operations* for the custom docs. + +Again, you can re-use FastAPI's internal functions to create the HTML pages for the docs, and pass them the needed arguments: + +* `openapi_url`: the URL where the HTML page for the docs can get the OpenAPI schema for your API. You can use here the attribute `app.openapi_url`. +* `title`: the title of your API. +* `oauth2_redirect_url`: you can use `app.swagger_ui_oauth2_redirect_url` here to use the default. +* `swagger_js_url`: the URL where the HTML for your Swagger UI docs can get the **JavaScript** file. **This is the one that your own app is now serving**. +* `swagger_css_url`: the URL where the HTML for your Swagger UI docs can get the **CSS** file. **This is the one that your own app is now serving**. + +And similarly for ReDoc... + +```Python hl_lines="2-6 14-22 25-27 30-36" +{!../../../docs_src/custom_docs_ui/tutorial002.py!} +``` + +!!! tip + The *path operation* for `swagger_ui_redirect` is a helper for when you use OAuth2. + + If you integrate your API with an OAuth2 provider, you will be able to authenticate and come back to the API docs with the acquired credentials. And interact with it using the real OAuth2 authentication. + + Swagger UI will handle it behind the scenes for you, but it needs this "redirect" helper. + +### Create a *path operation* to test static files + +Now, to be able to test that everything works, create a *path operation*: + +```Python hl_lines="39-41" +{!../../../docs_src/custom_docs_ui/tutorial002.py!} +``` + +### Test Static Files UI + +Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page. + +And even without Internet, you would be able to see the docs for your API and interact with it. diff --git a/docs/en/docs/advanced/custom-request-and-route.md b/docs/en/docs/how-to/custom-request-and-route.md similarity index 100% rename from docs/en/docs/advanced/custom-request-and-route.md rename to docs/en/docs/how-to/custom-request-and-route.md diff --git a/docs/en/docs/how-to/extending-openapi.md b/docs/en/docs/how-to/extending-openapi.md new file mode 100644 index 0000000000000..a18fd737e6b4e --- /dev/null +++ b/docs/en/docs/how-to/extending-openapi.md @@ -0,0 +1,87 @@ +# Extending OpenAPI + +There are some cases where you might need to modify the generated OpenAPI schema. + +In this section you will see how. + +## The normal process + +The normal (default) process, is as follows. + +A `FastAPI` application (instance) has an `.openapi()` method that is expected to return the OpenAPI schema. + +As part of the application object creation, a *path operation* for `/openapi.json` (or for whatever you set your `openapi_url`) is registered. + +It just returns a JSON response with the result of the application's `.openapi()` method. + +By default, what the method `.openapi()` does is check the property `.openapi_schema` to see if it has contents and return them. + +If it doesn't, it generates them using the utility function at `fastapi.openapi.utils.get_openapi`. + +And that function `get_openapi()` receives as parameters: + +* `title`: The OpenAPI title, shown in the docs. +* `version`: The version of your API, e.g. `2.5.0`. +* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.1.0`. +* `summary`: A short summary of the API. +* `description`: The description of your API, this can include markdown and will be shown in the docs. +* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`. + +!!! info + The parameter `summary` is available in OpenAPI 3.1.0 and above, supported by FastAPI 0.99.0 and above. + +## Overriding the defaults + +Using the information above, you can use the same utility function to generate the OpenAPI schema and override each part that you need. + +For example, let's add ReDoc's OpenAPI extension to include a custom logo. + +### Normal **FastAPI** + +First, write all your **FastAPI** application as normally: + +```Python hl_lines="1 4 7-9" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Generate the OpenAPI schema + +Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function: + +```Python hl_lines="2 15-21" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Modify the OpenAPI schema + +Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema: + +```Python hl_lines="22-24" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Cache the OpenAPI schema + +You can use the property `.openapi_schema` as a "cache", to store your generated schema. + +That way, your application won't have to generate the schema every time a user opens your API docs. + +It will be generated only once, and then the same cached schema will be used for the next requests. + +```Python hl_lines="13-14 25-26" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Override the method + +Now you can replace the `.openapi()` method with your new function. + +```Python hl_lines="29" +{!../../../docs_src/extending_openapi/tutorial001.py!} +``` + +### Check it + +Once you go to http://127.0.0.1:8000/redoc you will see that you are using your custom logo (in this example, **FastAPI**'s logo): + + diff --git a/docs/en/docs/how-to/general.md b/docs/en/docs/how-to/general.md new file mode 100644 index 0000000000000..04367c6b76353 --- /dev/null +++ b/docs/en/docs/how-to/general.md @@ -0,0 +1,39 @@ +# General - How To - Recipes + +Here are several pointers to other places in the docs, for general or frequent questions. + +## Filter Data - Security + +To ensure that you don't return more data than you should, read the docs for [Tutorial - Response Model - Return Type](../tutorial/response-model.md){.internal-link target=_blank}. + +## Documentation Tags - OpenAPI + +To add tags to your *path operations*, and group them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags){.internal-link target=_blank}. + +## Documentation Summary and Description - OpenAPI + +To add a summary and description to your *path operations*, and show them in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description){.internal-link target=_blank}. + +## Documentation Response description - OpenAPI + +To define the description of the response, shown in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description){.internal-link target=_blank}. + +## Documentation Deprecate a *Path Operation* - OpenAPI + +To deprecate a *path operation*, and show it in the docs UI, read the docs for [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation){.internal-link target=_blank}. + +## Convert any Data to JSON-compatible + +To convert any data to JSON-compatible, read the docs for [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md){.internal-link target=_blank}. + +## OpenAPI Metadata - Docs + +To add metadata to your OpenAPI schema, including a license, version, contact, etc, read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md){.internal-link target=_blank}. + +## OpenAPI Custom URL + +To customize the OpenAPI URL (or remove it), read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url){.internal-link target=_blank}. + +## OpenAPI Docs URLs + +To update the URLs used for the automatically generated docs user interfaces, read the docs for [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls){.internal-link target=_blank}. diff --git a/docs/en/docs/advanced/graphql.md b/docs/en/docs/how-to/graphql.md similarity index 100% rename from docs/en/docs/advanced/graphql.md rename to docs/en/docs/how-to/graphql.md diff --git a/docs/en/docs/how-to/index.md b/docs/en/docs/how-to/index.md new file mode 100644 index 0000000000000..ec7fd38f8f277 --- /dev/null +++ b/docs/en/docs/how-to/index.md @@ -0,0 +1,11 @@ +# How To - Recipes + +Here you will see different recipes or "how to" guides for **several topics**. + +Most of these ideas would be more or less **independent**, and in most cases you should only need to study them if they apply directly to **your project**. + +If something seems interesting and useful to your project, go ahead and check it, but otherwise, you might probably just skip them. + +!!! tip + + If you want to **learn FastAPI** in a structured way (recommended), go and read the [Tutorial - User Guide](../tutorial/index.md){.internal-link target=_blank} chapter by chapter instead. diff --git a/docs/en/docs/advanced/nosql-databases.md b/docs/en/docs/how-to/nosql-databases-couchbase.md similarity index 99% rename from docs/en/docs/advanced/nosql-databases.md rename to docs/en/docs/how-to/nosql-databases-couchbase.md index 606db35c7512b..ae6ad604baa94 100644 --- a/docs/en/docs/advanced/nosql-databases.md +++ b/docs/en/docs/how-to/nosql-databases-couchbase.md @@ -1,4 +1,4 @@ -# NoSQL (Distributed / Big Data) Databases +# NoSQL (Distributed / Big Data) Databases with Couchbase !!! info These docs are about to be updated. ๐ŸŽ‰ diff --git a/docs/en/docs/how-to/separate-openapi-schemas.md b/docs/en/docs/how-to/separate-openapi-schemas.md new file mode 100644 index 0000000000000..d289391ca344b --- /dev/null +++ b/docs/en/docs/how-to/separate-openapi-schemas.md @@ -0,0 +1,231 @@ +# Separate OpenAPI Schemas for Input and Output or Not + +When using **Pydantic v2**, the generated OpenAPI is a bit more exact and **correct** than before. ๐Ÿ˜Ž + +In fact, in some cases, it will even have **two JSON Schemas** in OpenAPI for the same Pydantic model, for input and output, depending on if they have **default values**. + +Let's see how that works and how to change it if you need to do that. + +## Pydantic Models for Input and Output + +Let's say you have a Pydantic model with default values, like this one: + +=== "Python 3.10+" + + ```Python hl_lines="7" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py[ln:1-7]!} + + # Code below omitted ๐Ÿ‘‡ + ``` + +
+ ๐Ÿ‘€ Full file preview + + ```Python + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!} + ``` + +
+ +=== "Python 3.9+" + + ```Python hl_lines="9" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py[ln:1-9]!} + + # Code below omitted ๐Ÿ‘‡ + ``` + +
+ ๐Ÿ‘€ Full file preview + + ```Python + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!} + ``` + +
+ +=== "Python 3.7+" + + ```Python hl_lines="9" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001.py[ln:1-9]!} + + # Code below omitted ๐Ÿ‘‡ + ``` + +
+ ๐Ÿ‘€ Full file preview + + ```Python + {!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!} + ``` + +
+ +### Model for Input + +If you use this model as an input like here: + +=== "Python 3.10+" + + ```Python hl_lines="14" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py[ln:1-15]!} + + # Code below omitted ๐Ÿ‘‡ + ``` + +
+ ๐Ÿ‘€ Full file preview + + ```Python + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!} + ``` + +
+ +=== "Python 3.9+" + + ```Python hl_lines="16" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py[ln:1-17]!} + + # Code below omitted ๐Ÿ‘‡ + ``` + +
+ ๐Ÿ‘€ Full file preview + + ```Python + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!} + ``` + +
+ +=== "Python 3.7+" + + ```Python hl_lines="16" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001.py[ln:1-17]!} + + # Code below omitted ๐Ÿ‘‡ + ``` + +
+ ๐Ÿ‘€ Full file preview + + ```Python + {!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!} + ``` + +
+ +...then the `description` field will **not be required**. Because it has a default value of `None`. + +### Input Model in Docs + +You can confirm that in the docs, the `description` field doesn't have a **red asterisk**, it's not marked as required: + +
+ +
+ +### Model for Output + +But if you use the same model as an output, like here: + +=== "Python 3.10+" + + ```Python hl_lines="19" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="21" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001_py39.py!} + ``` + +=== "Python 3.7+" + + ```Python hl_lines="21" + {!> ../../../docs_src/separate_openapi_schemas/tutorial001.py!} + ``` + +...then because `description` has a default value, if you **don't return anything** for that field, it will still have that **default value**. + +### Model for Output Response Data + +If you interact with the docs and check the response, even though the code didn't add anything in one of the `description` fields, the JSON response contains the default value (`null`): + +
+ +
+ +This means that it will **always have a value**, it's just that sometimes the value could be `None` (or `null` in JSON). + +That means that, clients using your API don't have to check if the value exists or not, they can **asume the field will always be there**, but just that in some cases it will have the default value of `None`. + +The way to describe this in OpenAPI, is to mark that field as **required**, because it will always be there. + +Because of that, the JSON Schema for a model can be different depending on if it's used for **input or output**: + +* for **input** the `description` will **not be required** +* for **output** it will be **required** (and possibly `None`, or in JSON terms, `null`) + +### Model for Output in Docs + +You can check the output model in the docs too, **both** `name` and `description` are marked as **required** with a **red asterisk**: + +
+ +
+ +### Model for Input and Output in Docs + +And if you check all the available Schemas (JSON Schemas) in OpenAPI, you will see that there are two, one `Item-Input` and one `Item-Output`. + +For `Item-Input`, `description` is **not required**, it doesn't have a red asterisk. + +But for `Item-Output`, `description` is **required**, it has a red asterisk. + +
+ +
+ +With this feature from **Pydantic v2**, your API documentation is more **precise**, and if you have autogenerated clients and SDKs, they will be more precise too, with a better **developer experience** and consistency. ๐ŸŽ‰ + +## Do not Separate Schemas + +Now, there are some cases where you might want to have the **same schema for input and output**. + +Probably the main use case for this is if you already have some autogenerated client code/SDKs and you don't want to update all the autogenerated client code/SDKs yet, you probably will want to do it at some point, but maybe not right now. + +In that case, you can disable this feature in **FastAPI**, with the parameter `separate_input_output_schemas=False`. + +!!! info + Support for `separate_input_output_schemas` was added in FastAPI `0.102.0`. ๐Ÿค“ + +=== "Python 3.10+" + + ```Python hl_lines="10" + {!> ../../../docs_src/separate_openapi_schemas/tutorial002_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="12" + {!> ../../../docs_src/separate_openapi_schemas/tutorial002_py39.py!} + ``` + +=== "Python 3.7+" + + ```Python hl_lines="12" + {!> ../../../docs_src/separate_openapi_schemas/tutorial002.py!} + ``` + +### Same Schema for Input and Output Models in Docs + +And now there will be one single schema for input and output for the model, only `Item`, and it will have `description` as **not required**: + +
+ +
+ +This is the same behavior as in Pydantic v1. ๐Ÿค“ diff --git a/docs/en/docs/advanced/sql-databases-peewee.md b/docs/en/docs/how-to/sql-databases-peewee.md similarity index 99% rename from docs/en/docs/advanced/sql-databases-peewee.md rename to docs/en/docs/how-to/sql-databases-peewee.md index 6a469634fa4bb..bf2f2e714a78a 100644 --- a/docs/en/docs/advanced/sql-databases-peewee.md +++ b/docs/en/docs/how-to/sql-databases-peewee.md @@ -12,6 +12,8 @@ Because Pewee doesn't play well with anything async and there are better alternatives, I won't update these docs for Pydantic v2, they are kept for now only for historical purposes. + The examples here are no longer tested in CI (as they were before). + If you are starting a project from scratch, you are probably better off with SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), or any other async ORM. If you already have a code base that uses Peewee ORM, you can check here how to use it with **FastAPI**. diff --git a/docs/en/docs/img/sponsors/speakeasy.png b/docs/en/docs/img/sponsors/speakeasy.png new file mode 100644 index 0000000000000..001b4b4caffe2 Binary files /dev/null and b/docs/en/docs/img/sponsors/speakeasy.png differ diff --git a/docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png b/docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png new file mode 100644 index 0000000000000..aa085f88df45e Binary files /dev/null and b/docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png differ diff --git a/docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png b/docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png new file mode 100644 index 0000000000000..672ef1d2b71df Binary files /dev/null and b/docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png differ diff --git a/docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png b/docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png new file mode 100644 index 0000000000000..81340fbece942 Binary files /dev/null and b/docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png differ diff --git a/docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png b/docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png new file mode 100644 index 0000000000000..fc2302aa77ce2 Binary files /dev/null and b/docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png differ diff --git a/docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png b/docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png new file mode 100644 index 0000000000000..674dd0b2e2730 Binary files /dev/null and b/docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png differ diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 9570bef363759..91b29e7725ebe 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,41 @@ ## Latest Changes + +## 0.103.0 + +### Features + +* โœจ Add support for `openapi_examples` in all FastAPI parameters. PR [#10152](https://github.com/tiangolo/fastapi/pull/10152) by [@tiangolo](https://github.com/tiangolo). + +### Docs + +* ๐Ÿ“ Add note to docs about Separate Input and Output Schemas with FastAPI version. PR [#10150](https://github.com/tiangolo/fastapi/pull/10150) by [@tiangolo](https://github.com/tiangolo). + +## 0.102.0 + +### Features + +* โœจ Add support for disabling the separation of input and output JSON Schemas in OpenAPI with Pydantic v2 with `separate_input_output_schemas=False`. PR [#10145](https://github.com/tiangolo/fastapi/pull/10145) by [@tiangolo](https://github.com/tiangolo). + * New docs [Separate OpenAPI Schemas for Input and Output or Not](https://fastapi.tiangolo.com/how-to/separate-openapi-schemas/). + * This PR also includes a new setup (internal tools) for generating screenshots for the docs. + +### Refactors + +* โ™ป๏ธ Refactor tests for new Pydantic 2.2.1. PR [#10115](https://github.com/tiangolo/fastapi/pull/10115) by [@tiangolo](https://github.com/tiangolo). + +### Docs + +* ๐Ÿ“ Add new docs section, How To - Recipes, move docs that don't have to be read by everyone to How To. PR [#10114](https://github.com/tiangolo/fastapi/pull/10114) by [@tiangolo](https://github.com/tiangolo). +* ๐Ÿ“ Update Advanced docs, add links to sponsor courses. PR [#10113](https://github.com/tiangolo/fastapi/pull/10113) by [@tiangolo](https://github.com/tiangolo). +* ๐Ÿ“ Update docs for generating clients. PR [#10112](https://github.com/tiangolo/fastapi/pull/10112) by [@tiangolo](https://github.com/tiangolo). +* ๐Ÿ“ Tweak MkDocs and add redirects. PR [#10111](https://github.com/tiangolo/fastapi/pull/10111) by [@tiangolo](https://github.com/tiangolo). +* ๐Ÿ“ Restructure docs for cloud providers, include links to sponsors. PR [#10110](https://github.com/tiangolo/fastapi/pull/10110) by [@tiangolo](https://github.com/tiangolo). + +### Internal + +* ๐Ÿ”ง Update sponsors, add Speakeasy. PR [#10098](https://github.com/tiangolo/fastapi/pull/10098) by [@tiangolo](https://github.com/tiangolo). + ## 0.101.1 ### Fixes diff --git a/docs/en/docs/tutorial/schema-extra-example.md b/docs/en/docs/tutorial/schema-extra-example.md index 39d184763fef3..8cf1b9c091cb4 100644 --- a/docs/en/docs/tutorial/schema-extra-example.md +++ b/docs/en/docs/tutorial/schema-extra-example.md @@ -74,7 +74,7 @@ When using `Field()` with Pydantic models, you can also declare additional `exam {!> ../../../docs_src/schema_extra_example/tutorial002.py!} ``` -## `examples` in OpenAPI +## `examples` in JSON Schema - OpenAPI When using any of: @@ -86,7 +86,7 @@ When using any of: * `Form()` * `File()` -you can also declare a group of `examples` with additional information that will be added to **OpenAPI**. +you can also declare a group of `examples` with additional information that will be added to their **JSON Schemas** inside of **OpenAPI**. ### `Body` with `examples` @@ -174,9 +174,84 @@ You can of course also pass multiple `examples`: {!> ../../../docs_src/schema_extra_example/tutorial004.py!} ``` -### Examples in the docs UI +When you do this, the examples will be part of the internal **JSON Schema** for that body data. -With `examples` added to `Body()` the `/docs` would look like: +Nevertheless, at the time of writing this, Swagger UI, the tool in charge of showing the docs UI, doesn't support showing multiple examples for the data in **JSON Schema**. But read below for a workaround. + +### OpenAPI-specific `examples` + +Since before **JSON Schema** supported `examples` OpenAPI had support for a different field also called `examples`. + +This **OpenAPI-specific** `examples` goes in another section in the OpenAPI specification. It goes in the **details for each *path operation***, not inside each JSON Schema. + +And Swagger UI has supported this particular `examples` field for a while. So, you can use it to **show** different **examples in the docs UI**. + +The shape of this OpenAPI-specific field `examples` is a `dict` with **multiple examples** (instead of a `list`), each with extra information that will be added to **OpenAPI** too. + +This doesn't go inside of each JSON Schema contained in OpenAPI, this goes outside, in the *path operation* directly. + +### Using the `openapi_examples` Parameter + +You can declare the OpenAPI-specific `examples` in FastAPI with the parameter `openapi_examples` for: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +The keys of the `dict` identify each example, and each value is another `dict`. + +Each specific example `dict` in the `examples` can contain: + +* `summary`: Short description for the example. +* `description`: A long description that can contain Markdown text. +* `value`: This is the actual example shown, e.g. a `dict`. +* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`. + +You can use it like this: + +=== "Python 3.10+" + + ```Python hl_lines="23-49" + {!> ../../../docs_src/schema_extra_example/tutorial005_an_py310.py!} + ``` + +=== "Python 3.9+" + + ```Python hl_lines="23-49" + {!> ../../../docs_src/schema_extra_example/tutorial005_an_py39.py!} + ``` + +=== "Python 3.6+" + + ```Python hl_lines="24-50" + {!> ../../../docs_src/schema_extra_example/tutorial005_an.py!} + ``` + +=== "Python 3.10+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="19-45" + {!> ../../../docs_src/schema_extra_example/tutorial005_py310.py!} + ``` + +=== "Python 3.6+ non-Annotated" + + !!! tip + Prefer to use the `Annotated` version if possible. + + ```Python hl_lines="21-47" + {!> ../../../docs_src/schema_extra_example/tutorial005.py!} + ``` + +### OpenAPI Examples in the Docs UI + +With `openapi_examples` added to `Body()` the `/docs` would look like: @@ -210,20 +285,8 @@ OpenAPI also added `example` and `examples` fields to other parts of the specifi * `File()` * `Form()` -### OpenAPI's `examples` field - -The shape of this field `examples` from OpenAPI is a `dict` with **multiple examples**, each with extra information that will be added to **OpenAPI** too. - -The keys of the `dict` identify each example, and each value is another `dict`. - -Each specific example `dict` in the `examples` can contain: - -* `summary`: Short description for the example. -* `description`: A long description that can contain Markdown text. -* `value`: This is the actual example shown, e.g. a `dict`. -* `externalValue`: alternative to `value`, a URL pointing to the example. Although this might not be supported by as many tools as `value`. - -This applies to those other parts of the OpenAPI specification apart from JSON Schema. +!!! info + This old OpenAPI-specific `examples` parameter is now `openapi_examples` since FastAPI `0.103.0`. ### JSON Schema's `examples` field @@ -250,6 +313,12 @@ In versions of FastAPI before 0.99.0 (0.99.0 and above use the newer OpenAPI 3.1 But now that FastAPI 0.99.0 and above uses OpenAPI 3.1.0, that uses JSON Schema 2020-12, and Swagger UI 5.0.0 and above, everything is more consistent and the examples are included in JSON Schema. +### Swagger UI and OpenAPI-specific `examples` + +Now, as Swagger UI didn't support multiple JSON Schema examples (as of 2023-08-26), users didn't have a way to show multiple examples in the docs. + +To solve that, FastAPI `0.103.0` **added support** for declaring the same old **OpenAPI-specific** `examples` field with the new parameter `openapi_examples`. ๐Ÿค“ + ### Summary I used to say I didn't like history that much... and look at me now giving "tech history" lessons. ๐Ÿ˜… diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index a66b6c14795dc..c56e4c9426f8f 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -42,6 +42,16 @@ plugins: search: null markdownextradata: data: ../en/data + redirects: + redirect_maps: + deployment/deta.md: deployment/cloud.md + advanced/sql-databases-peewee.md: how-to/sql-databases-peewee.md + advanced/async-sql-databases.md: how-to/async-sql-encode-databases.md + advanced/nosql-databases.md: how-to/nosql-databases-couchbase.md + advanced/graphql.md: how-to/graphql.md + advanced/custom-request-and-route.md: how-to/custom-request-and-route.md + advanced/conditional-openapi.md: how-to/conditional-openapi.md + advanced/extending-openapi.md: how-to/extending-openapi.md nav: - FastAPI: index.md - Languages: @@ -60,6 +70,7 @@ nav: - ru: /ru/ - tr: /tr/ - uk: /uk/ + - ur: /ur/ - vi: /vi/ - zh: /zh/ - features.md @@ -130,24 +141,17 @@ nav: - advanced/using-request-directly.md - advanced/dataclasses.md - advanced/middleware.md - - advanced/sql-databases-peewee.md - - advanced/async-sql-databases.md - - advanced/nosql-databases.md - advanced/sub-applications.md - advanced/behind-a-proxy.md - advanced/templates.md - - advanced/graphql.md - advanced/websockets.md - advanced/events.md - - advanced/custom-request-and-route.md - advanced/testing-websockets.md - advanced/testing-events.md - advanced/testing-dependencies.md - advanced/testing-database.md - advanced/async-tests.md - advanced/settings.md - - advanced/conditional-openapi.md - - advanced/extending-openapi.md - advanced/openapi-callbacks.md - advanced/openapi-webhooks.md - advanced/wsgi.md @@ -159,9 +163,22 @@ nav: - deployment/https.md - deployment/manually.md - deployment/concepts.md - - deployment/deta.md + - deployment/cloud.md - deployment/server-workers.md - deployment/docker.md +- How To - Recipes: + - how-to/index.md + - how-to/general.md + - how-to/sql-databases-peewee.md + - how-to/async-sql-encode-databases.md + - how-to/nosql-databases-couchbase.md + - how-to/graphql.md + - how-to/custom-request-and-route.md + - how-to/conditional-openapi.md + - how-to/extending-openapi.md + - how-to/separate-openapi-schemas.md + - how-to/custom-docs-ui-assets.md + - how-to/configure-swagger-ui.md - project-generation.md - alternatives.md - history-design-future.md @@ -178,9 +195,9 @@ markdown_extensions: guess_lang: false mdx_include: base_path: docs - admonition: - codehilite: - extra: + admonition: null + codehilite: null + extra: null pymdownx.superfences: custom_fences: - name: mermaid @@ -188,8 +205,8 @@ markdown_extensions: format: !!python/name:pymdownx.superfences.fence_code_format '' pymdownx.tabbed: alternate_style: true - attr_list: - md_in_html: + attr_list: null + md_in_html: null extra: analytics: provider: google @@ -240,6 +257,8 @@ extra: name: tr - Tรผrkรงe - link: /uk/ name: uk + - link: /ur/ + name: ur - link: /vi/ name: vi - Tiแบฟng Viแป‡t - link: /zh/ diff --git a/docs/fr/docs/deployment/deta.md b/docs/fr/docs/deployment/deta.md deleted file mode 100644 index cceb7b058c58b..0000000000000 --- a/docs/fr/docs/deployment/deta.md +++ /dev/null @@ -1,245 +0,0 @@ -# Dรฉployer FastAPI sur Deta - -Dans cette section, vous apprendrez ร  dรฉployer facilement une application **FastAPI** sur Deta en utilisant le plan tarifaire gratuit. ๐ŸŽ - -Cela vous prendra environ **10 minutes**. - -!!! info - Deta sponsorise **FastAPI**. ๐ŸŽ‰ - -## Une application **FastAPI** de base - -* Crรฉez un rรฉpertoire pour votre application, par exemple `./fastapideta/` et dรฉplacez-vous dedans. - -### Le code FastAPI - -* Crรฉer un fichier `main.py` avecย : - -```Python -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int): - return {"item_id": item_id} -``` - -### Dรฉpendances - -Maintenant, dans le mรชme rรฉpertoire, crรฉez un fichier `requirements.txt` avecย : - -```text -fastapi -``` - -!!! tip "Astuce" - Il n'est pas nรฉcessaire d'installer Uvicorn pour dรฉployer sur Deta, bien qu'il soit probablement souhaitable de l'installer localement pour tester votre application. - -### Structure du rรฉpertoire - -Vous aurez maintenant un rรฉpertoire `./fastapideta/` avec deux fichiersย : - -``` -. -โ””โ”€โ”€ main.py -โ””โ”€โ”€ requirements.txt -``` - -## Crรฉer un compte gratuit sur Deta - -Crรฉez maintenant un compte gratuit -sur Deta, vous avez juste besoin d'une adresse email et d'un mot de passe. - -Vous n'avez mรชme pas besoin d'une carte de crรฉdit. - -## Installer le CLI (Interface en Ligne de Commande) - -Une fois que vous avez votre compte, installez le CLI de Deta : - -=== "Linux, macOS" - -
- - ```console - $ curl -fsSL https://get.deta.dev/cli.sh | sh - ``` - -
- -=== "Windows PowerShell" - -
- - ```console - $ iwr https://get.deta.dev/cli.ps1 -useb | iex - ``` - -
- -Aprรจs l'avoir installรฉ, ouvrez un nouveau terminal afin que la nouvelle installation soit dรฉtectรฉe. - -Dans un nouveau terminal, confirmez qu'il a รฉtรฉ correctement installรฉ avecย : - -
- -```console -$ deta --help - -Deta command line interface for managing deta micros. -Complete documentation available at https://docs.deta.sh - -Usage: - deta [flags] - deta [command] - -Available Commands: - auth Change auth settings for a deta micro - -... -``` - -
- -!!! tip "Astuce" - Si vous rencontrez des problรจmes pour installer le CLI, consultez la documentation officielle de Deta (en anglais). - -## Connexion avec le CLI - -Maintenant, connectez-vous ร  Deta depuis le CLI avecย : - -
- -```console -$ deta login - -Please, log in from the web page. Waiting.. -Logged in successfully. -``` - -
- -Cela ouvrira un navigateur web et permettra une authentification automatique. - -## Dรฉployer avec Deta - -Ensuite, dรฉployez votre application avec le CLI de Detaย : - -
- -```console -$ deta new - -Successfully created a new micro - -// Notice the "endpoint" ๐Ÿ” - -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} - -Adding dependencies... - - ----> 100% - - -Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 -``` - -
- -Vous verrez un message JSON similaire ร ย : - -```JSON hl_lines="4" -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} -``` - -!!! tip "Astuce" - Votre dรฉploiement aura une URL `"endpoint"` diffรฉrente. - -## Vรฉrifiez - -Maintenant, dans votre navigateur ouvrez votre URL `endpoint`. Dans l'exemple ci-dessus, c'รฉtait -`https://qltnci.deta.dev`, mais la vรดtre sera diffรฉrente. - -Vous verrez la rรฉponse JSON de votre application FastAPIย : - -```JSON -{ - "Hello": "World" -} -``` - -Et maintenant naviguez vers `/docs` dans votre API, dans l'exemple ci-dessus ce serait `https://qltnci.deta.dev/docs`. - -Vous verrez votre documentation comme suitย : - - - -## Activer l'accรจs public - -Par dรฉfaut, Deta va gรฉrer l'authentification en utilisant des cookies pour votre compte. - -Mais une fois que vous รชtes prรชt, vous pouvez le rendre public avecย : - -
- -```console -$ deta auth disable - -Successfully disabled http auth -``` - -
- -Maintenant, vous pouvez partager cette URL avec n'importe qui et ils seront en mesure d'accรฉder ร  votre API. ๐Ÿš€ - -## HTTPS - -Fรฉlicitationsโ€ฏ! Vous avez dรฉployรฉ votre application FastAPI sur Detaโ€ฏ! ๐ŸŽ‰ ๐Ÿฐ - -Remarquez รฉgalement que Deta gรจre correctement HTTPS pour vous, vous n'avez donc pas ร  vous en occuper et pouvez รชtre sรปr que vos clients auront une connexion cryptรฉe sรฉcurisรฉe. โœ… ๐Ÿ”’ - -## Vรฉrifiez le Visor - -ร€ partir de l'interface graphique de votre documentation (dans une URL telle que `https://qltnci.deta.dev/docs`) -envoyez une requรชte ร  votre *opรฉration de chemin* `/items/{item_id}`. - -Par exemple avec l'ID `5`. - -Allez maintenant sur https://web.deta.sh. - -Vous verrez qu'il y a une section ร  gauche appelรฉe "Micros" avec chacune de vos applications. - -Vous verrez un onglet avec "Details", et aussi un onglet "Visor", allez ร  l'onglet "Visor". - -Vous pouvez y consulter les requรชtes rรฉcentes envoyรฉes ร  votre application. - -Vous pouvez รฉgalement les modifier et les relancer. - - - -## En savoir plus - -ร€ un moment donnรฉ, vous voudrez probablement stocker certaines donnรฉes pour votre application d'une maniรจre qui -persiste dans le temps. Pour cela, vous pouvez utiliser Deta Base, il dispose รฉgalement d'un gรฉnรฉreux **plan gratuit**. - -Vous pouvez รฉgalement en lire plus dans la documentation Deta. diff --git a/docs/ja/docs/deployment/deta.md b/docs/ja/docs/deployment/deta.md deleted file mode 100644 index 723f169a00f10..0000000000000 --- a/docs/ja/docs/deployment/deta.md +++ /dev/null @@ -1,240 +0,0 @@ -# Deta ใซใƒ‡ใƒ—ใƒญใ‚ค - -ใ“ใฎใ‚ปใ‚ฏใ‚ทใƒงใƒณใงใฏใ€**FastAPI** ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใ‚’ Deta ใฎ็„กๆ–™ใƒ—ใƒฉใƒณใ‚’ๅˆฉ็”จใ—ใฆใ€็ฐกๅ˜ใซใƒ‡ใƒ—ใƒญใ‚คใ™ใ‚‹ๆ–นๆณ•ใ‚’ๅญฆ็ฟ’ใ—ใพใ™ใ€‚๐ŸŽ - -ๆ‰€่ฆๆ™‚้–“ใฏ็ด„**10ๅˆ†**ใงใ™ใ€‚ - -!!! info "ๅ‚™่€ƒ" - Deta ใฏ **FastAPI** ใฎใ‚นใƒใƒณใ‚ตใƒผใงใ™ใ€‚๐ŸŽ‰ - -## ใƒ™ใƒผใ‚ทใƒƒใ‚ฏใช **FastAPI** ใ‚ขใƒ—ใƒช - -* ใ‚ขใƒ—ใƒชใฎใŸใ‚ใฎใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒช (ไพ‹ใˆใฐ `./fastapideta/`) ใ‚’ไฝœๆˆใ—ใ€ใใฎไธญใซๅ…ฅใฃใฆใใ ใ•ใ„ใ€‚ - -### FastAPI ใฎใ‚ณใƒผใƒ‰ - -* ไปฅไธ‹ใฎ `main.py` ใƒ•ใ‚กใ‚คใƒซใ‚’ไฝœๆˆใ—ใฆใใ ใ•ใ„: - -```Python -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int): - return {"item_id": item_id} -``` - -### Requirements - -ใงใฏใ€ๅŒใ˜ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชใซไปฅไธ‹ใฎ `requirements.txt` ใƒ•ใ‚กใ‚คใƒซใ‚’ไฝœๆˆใ—ใฆใใ ใ•ใ„: - -```text -fastapi -``` - -!!! tip "่ฑ†็Ÿฅ่ญ˜" - ใ‚ขใƒ—ใƒชใฎใƒญใƒผใ‚ซใƒซใƒ†ใ‚นใƒˆใฎใŸใ‚ใซ Uvicorn ใ‚’ใ‚คใƒณใ‚นใƒˆใƒผใƒซใ—ใŸใใชใ‚‹ใ‹ใ‚‚ใ—ใ‚Œใพใ›ใ‚“ใŒใ€Deta ใธใฎใƒ‡ใƒ—ใƒญใ‚คใซใฏไธ่ฆใงใ™ใ€‚ - -### ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชๆง‹้€  - -ไปฅไธ‹ใฎ2ใคใฎใƒ•ใ‚กใ‚คใƒซใจ1ใคใฎ `./fastapideta/` ใƒ‡ใ‚ฃใƒฌใ‚ฏใƒˆใƒชใŒใ‚ใ‚‹ใฏใšใงใ™: - -``` -. -โ””โ”€โ”€ main.py -โ””โ”€โ”€ requirements.txt -``` - -## Detaใฎ็„กๆ–™ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใฎไฝœๆˆ - -ใใ‚Œใงใฏใ€Detaใฎ็„กๆ–™ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‚’ไฝœๆˆใ—ใพใ—ใ‚‡ใ†ใ€‚ๅฟ…่ฆใชใ‚‚ใฎใฏใƒกใƒผใƒซใ‚ขใƒ‰ใƒฌใ‚นใจใƒ‘ใ‚นใƒฏใƒผใƒ‰ใ ใ‘ใงใ™ใ€‚ - -ใ‚ฏใƒฌใ‚ธใƒƒใƒˆใ‚ซใƒผใƒ‰ใ•ใˆๅฟ…่ฆใ‚ใ‚Šใพใ›ใ‚“ใ€‚ - -## CLIใฎใ‚คใƒณใ‚นใƒˆใƒผใƒซ - -ใ‚ขใ‚ซใ‚ฆใƒณใƒˆใ‚’ๅ–ๅพ—ใ—ใŸใ‚‰ใ€Deta CLI ใ‚’ใ‚คใƒณใ‚นใƒˆใƒผใƒซใ—ใฆใใ ใ•ใ„: - -=== "Linux, macOS" - -
- - ```console - $ curl -fsSL https://get.deta.dev/cli.sh | sh - ``` - -
- -=== "Windows PowerShell" - -
- - ```console - $ iwr https://get.deta.dev/cli.ps1 -useb | iex - ``` - -
- -ใ‚คใƒณใ‚นใƒˆใƒผใƒซใ—ใŸใ‚‰ใ€ใ‚คใƒณใ‚นใƒˆใƒผใƒซใ—ใŸ CLI ใ‚’ๆœ‰ๅŠนใซใ™ใ‚‹ใŸใ‚ใซๆ–ฐใŸใชใ‚ฟใƒผใƒŸใƒŠใƒซใ‚’้–‹ใ„ใฆใใ ใ•ใ„ใ€‚ - -ๆ–ฐใŸใชใ‚ฟใƒผใƒŸใƒŠใƒซไธŠใงใ€ๆญฃใ—ใใ‚คใƒณใ‚นใƒˆใƒผใƒซใ•ใ‚ŒใŸใ‹็ขบ่ชใ—ใพใ™: - -
- -```console -$ deta --help - -Deta command line interface for managing deta micros. -Complete documentation available at https://docs.deta.sh - -Usage: - deta [flags] - deta [command] - -Available Commands: - auth Change auth settings for a deta micro - -... -``` - -
- -!!! tip "่ฑ†็Ÿฅ่ญ˜" - CLI ใฎใ‚คใƒณใ‚นใƒˆใƒผใƒซใซๅ•้กŒใŒ็™บ็”Ÿใ—ใŸๅ ดๅˆใฏใ€Deta ๅ…ฌๅผใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใ‚’ๅ‚็…งใ—ใฆใใ ใ•ใ„ใ€‚ - -## CLIใงใƒญใ‚ฐใ‚คใƒณ - -CLI ใ‹ใ‚‰ Deta ใซใƒญใ‚ฐใ‚คใƒณใ—ใฆใฟใพใ—ใ‚‡ใ†: - -
- -```console -$ deta login - -Please, log in from the web page. Waiting.. -Logged in successfully. -``` - -
- -่‡ชๅ‹•็š„ใซใ‚ฆใ‚งใƒ–ใƒ–ใƒฉใ‚ฆใ‚ถใŒ้–‹ใ„ใฆใ€่ช่จผๅ‡ฆ็†ใŒ่กŒใ‚ใ‚Œใพใ™ใ€‚ - -## Deta ใงใƒ‡ใƒ—ใƒญใ‚ค - -ๆฌกใซใ€ใ‚ขใƒ—ใƒชใ‚ฑใƒผใ‚ทใƒงใƒณใ‚’ Deta CLIใงใƒ‡ใƒ—ใƒญใ‚คใ—ใพใ—ใ‚‡ใ†: - -
- -```console -$ deta new - -Successfully created a new micro - -// Notice the "endpoint" ๐Ÿ” - -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} - -Adding dependencies... - - ----> 100% - - -Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 -``` - -
- -ๆฌกใฎใ‚ˆใ†ใชJSONใƒกใƒƒใ‚ปใƒผใ‚ธใŒ่กจ็คบใ•ใ‚Œใพใ™: - -```JSON hl_lines="4" -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} -``` - -!!! tip "่ฑ†็Ÿฅ่ญ˜" - ใ‚ใชใŸใฎใƒ‡ใƒ—ใƒญใ‚คใงใฏ็•ฐใชใ‚‹ `"endpoint"` URLใŒ่กจ็คบใ•ใ‚Œใ‚‹ใงใ—ใ‚‡ใ†ใ€‚ - -## ็ขบ่ช - -ใใ‚Œใงใฏใ€`endpoint` URLใ‚’ใƒ–ใƒฉใ‚ฆใ‚ถใง้–‹ใ„ใฆใฟใพใ—ใ‚‡ใ†ใ€‚ไธŠ่จ˜ใฎไพ‹ใงใฏ `https://qltnci.deta.dev` ใงใ™ใŒใ€ใ‚ใชใŸใฎURLใฏ็•ฐใชใ‚‹ใฏใšใงใ™ใ€‚ - -FastAPIใ‚ขใƒ—ใƒชใ‹ใ‚‰่ฟ”ใฃใฆใใŸJSONใƒฌใ‚นใƒใƒณใ‚นใŒ่กจ็คบใ•ใ‚Œใพใ™: - -```JSON -{ - "Hello": "World" -} -``` - -ใใ—ใฆ `/docs` ใธ็งปๅ‹•ใ—ใฆใใ ใ•ใ„ใ€‚ไธŠ่จ˜ใฎไพ‹ใงใฏใ€`https://qltnci.deta.dev/docs` ใงใ™ใ€‚ - -ๆฌกใฎใ‚ˆใ†ใชใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใŒ่กจ็คบใ•ใ‚Œใพใ™: - - - -## ใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใ‚ขใ‚ฏใ‚ปใ‚นใฎๆœ‰ๅŠนๅŒ– - -ใƒ‡ใƒ•ใ‚ฉใƒซใƒˆใงใฏใ€Deta ใฏใ‚ฏใƒƒใ‚ญใƒผใ‚’็”จใ„ใฆใ‚ขใ‚ซใ‚ฆใƒณใƒˆใฎ่ช่จผใ‚’่กŒใ„ใพใ™ใ€‚ - -ใ—ใ‹ใ—ใ€ๆบ–ๅ‚™ใŒๆ•ดใˆใฐใ€ไปฅไธ‹ใฎๆง˜ใซๅ…ฌ้–‹ใงใใพใ™: - -
- -```console -$ deta auth disable - -Successfully disabled http auth -``` - -
- -ใ“ใ“ใงใ€URLใ‚’ๅ…ฑๆœ‰ใ™ใ‚‹ใจAPIใซใ‚ขใ‚ฏใ‚ปใ‚นใงใใ‚‹ใ‚ˆใ†ใซใชใ‚Šใพใ™ใ€‚๐Ÿš€ - -## HTTPS - -ใŠใ‚ใงใจใ†ใ”ใ–ใ„ใพใ™๏ผใ‚ใชใŸใฎ FastAPI ใ‚ขใƒ—ใƒชใŒ Deta ใธใƒ‡ใƒ—ใƒญใ‚คใ•ใ‚Œใพใ—ใŸ๏ผ๐ŸŽ‰ ๐Ÿฐ - -ใพใŸใ€DetaใŒHTTPSใ‚’ๆญฃใ—ใๅ‡ฆ็†ใ™ใ‚‹ใŸใ‚ใ€ใใฎๅ‡ฆ็†ใ‚’่กŒใ†ๅฟ…่ฆใŒใชใใ€ใ‚ฏใƒฉใ‚คใ‚ขใƒณใƒˆใฏๆš—ๅทๅŒ–ใ•ใ‚ŒใŸๅฎ‰ๅ…จใช้€šไฟกใŒๅˆฉ็”จใงใใพใ™ใ€‚โœ… ๐Ÿ”’ - -## Visor ใ‚’็ขบ่ช - -ใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆUI (`https://qltnci.deta.dev/docs` ใฎใ‚ˆใ†ใชURLใซใ‚ใ‚‹) ใฏ *path operation* `/items/{item_id}` ใธใƒชใ‚ฏใ‚จใ‚นใƒˆใ‚’้€ใ‚‹ใ“ใจใŒใงใใพใ™ใ€‚ - -ID `5` ใฎไพ‹ใ‚’็คบใ—ใพใ™ใ€‚ - -ใพใšใ€https://web.deta.sh ใธใ‚ขใ‚ฏใ‚ปใ‚นใ—ใพใ™ใ€‚ - -ๅทฆๅดใซๅ„ใ‚ขใƒ—ใƒชใฎ ใ€ŒMicrosใ€ ใจใ„ใ†ใ‚ปใ‚ฏใ‚ทใƒงใƒณใŒ่กจ็คบใ•ใ‚Œใพใ™ใ€‚ - -ใพใŸใ€ใ€ŒDetailsใ€ใ‚„ใ€ŒVisorใ€ใ‚ฟใƒ–ใŒ่กจ็คบใ•ใ‚Œใฆใ„ใพใ™ใ€‚ใ€ŒVisorใ€ใ‚ฟใƒ–ใธ็งปๅ‹•ใ—ใฆใใ ใ•ใ„ใ€‚ - -ใใ“ใงใ‚ขใƒ—ใƒชใซ้€ใ‚‰ใ‚ŒใŸ็›ด่ฟ‘ใฎใƒชใ‚ฏใ‚จใ‚นใƒˆใŒ่ชฟในใ‚‰ใ‚Œใพใ™ใ€‚ - -ใพใŸใ€ใใ‚Œใ‚‰ใ‚’็ทจ้›†ใ—ใฆใƒชใƒ—ใƒฌใ‚คใงใใพใ™ใ€‚ - - - -## ใ•ใ‚‰ใซ่ฉณใ—ใ็Ÿฅใ‚‹ - -ๆง˜ใ€…ใช็ฎ‡ๆ‰€ใงๆฐธ็ถš็š„ใซใƒ‡ใƒผใ‚ฟใ‚’ไฟๅญ˜ใ—ใŸใใชใ‚‹ใงใ—ใ‚‡ใ†ใ€‚ใใฎใŸใ‚ใซใฏ Deta Base ใ‚’ไฝฟ็”จใงใใพใ™ใ€‚ๆƒœใ—ใฟใชใ„ **็„กๆ–™ๅˆฉ็”จๆž ** ใ‚‚ใ‚ใ‚Šใพใ™ใ€‚ - -่ฉณใ—ใใฏ Deta ใƒ‰ใ‚ญใƒฅใƒกใƒณใƒˆใ‚’ๅ‚็…งใ—ใฆใใ ใ•ใ„ใ€‚ diff --git a/docs/ja/docs/advanced/conditional-openapi.md b/docs/ja/docs/how-to/conditional-openapi.md similarity index 100% rename from docs/ja/docs/advanced/conditional-openapi.md rename to docs/ja/docs/how-to/conditional-openapi.md diff --git a/docs/pt/docs/deployment/deta.md b/docs/pt/docs/deployment/deta.md deleted file mode 100644 index 9271bba42ea85..0000000000000 --- a/docs/pt/docs/deployment/deta.md +++ /dev/null @@ -1,258 +0,0 @@ -# Implantaรงรฃo FastAPI na Deta - -Nessa seรงรฃo vocรช aprenderรก sobre como realizar a implantaรงรฃo de uma aplicaรงรฃo **FastAPI** na Deta utilizando o plano gratuito. ๐ŸŽ - -Isso tudo levarรก aproximadamente **10 minutos**. - -!!! info "Informaรงรฃo" - Deta รฉ uma patrocinadora do **FastAPI**. ๐ŸŽ‰ - -## Uma aplicaรงรฃo **FastAPI** simples - -* Crie e entre em um diretรณrio para a sua aplicaรงรฃo, por exemplo, `./fastapideta/`. - -### Cรณdigo FastAPI - -* Crie o arquivo `main.py` com: - -```Python -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int): - return {"item_id": item_id} -``` - -### Requisitos - -Agora, no mesmo diretรณrio crie o arquivo `requirements.txt` com: - -```text -fastapi -``` - -!!! tip "Dica" - Vocรช nรฃo precisa instalar Uvicorn para realizar a implantaรงรฃo na Deta, embora provavelmente queira instalรก-lo para testar seu aplicativo localmente. - -### Estrutura de diretรณrio - -Agora vocรช terรก o diretรณrio `./fastapideta/` com dois arquivos: - -``` -. -โ””โ”€โ”€ main.py -โ””โ”€โ”€ requirements.txt -``` - -## Crie uma conta gratuita na Deta - -Agora crie uma conta gratuita na Deta, vocรช precisarรก apenas de um email e senha. - -Vocรช nem precisa de um cartรฃo de crรฉdito. - -## Instale a CLI - -Depois de ter sua conta criada, instale Deta CLI: - -=== "Linux, macOS" - -
- - ```console - $ curl -fsSL https://get.deta.dev/cli.sh | sh - ``` - -
- -=== "Windows PowerShell" - -
- - ```console - $ iwr https://get.deta.dev/cli.ps1 -useb | iex - ``` - -
- -Apรณs a instalaรงรฃo, abra um novo terminal para que a CLI seja detectada. - -Em um novo terminal, confirme se foi instalado corretamente com: - -
- -```console -$ deta --help - -Deta command line interface for managing deta micros. -Complete documentation available at https://docs.deta.sh - -Usage: - deta [flags] - deta [command] - -Available Commands: - auth Change auth settings for a deta micro - -... -``` - -
- -!!! tip "Dica" - Se vocรช tiver problemas ao instalar a CLI, verifique a documentaรงรฃo oficial da Deta. - -## Login pela CLI - -Agora faรงa login na Deta pela CLI com: - -
- -```console -$ deta login - -Please, log in from the web page. Waiting.. -Logged in successfully. -``` - -
- -Isso abrirรก um navegador da Web e autenticarรก automaticamente. - -## Implantaรงรฃo com Deta - -Em seguida, implante seu aplicativo com a Deta CLI: - -
- -```console -$ deta new - -Successfully created a new micro - -// Notice the "endpoint" ๐Ÿ” - -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} - -Adding dependencies... - - ----> 100% - - -Successfully installed fastapi-0.61.1 pydantic-1.7.2 starlette-0.13.6 -``` - -
- -Vocรช verรก uma mensagem JSON semelhante a: - -```JSON hl_lines="4" -{ - "name": "fastapideta", - "runtime": "python3.7", - "endpoint": "https://qltnci.deta.dev", - "visor": "enabled", - "http_auth": "enabled" -} -``` - -!!! tip "Dica" - Sua implantaรงรฃo terรก um URL `"endpoint"` diferente. - -## Confira - -Agora, abra seu navegador na URL do `endpoint`. No exemplo acima foi `https://qltnci.deta.dev`, mas o seu serรก diferente. - -Vocรช verรก a resposta JSON do seu aplicativo FastAPI: - -```JSON -{ - "Hello": "World" -} -``` - -Agora vรก para o `/docs` da sua API, no exemplo acima seria `https://qltnci.deta.dev/docs`. - -Ele mostrarรก sua documentaรงรฃo como: - - - -## Permitir acesso pรบblico - -Por padrรฃo, a Deta lidarรก com a autenticaรงรฃo usando cookies para sua conta. - -Mas quando estiver pronto, vocรช pode tornรก-lo pรบblico com: - -
- -```console -$ deta auth disable - -Successfully disabled http auth -``` - -
- -Agora vocรช pode compartilhar essa URL com qualquer pessoa e elas conseguirรฃo acessar sua API. ๐Ÿš€ - -## HTTPS - -Parabรฉns! Vocรช realizou a implantaรงรฃo do seu app FastAPI na Deta! ๐ŸŽ‰ ๐Ÿฐ - -Alรฉm disso, observe que a Deta lida corretamente com HTTPS para vocรช, para que vocรช nรฃo precise cuidar disso e tenha a certeza de que seus clientes terรฃo uma conexรฃo criptografada segura. โœ… ๐Ÿ”’ - -## Verifique o Visor - -Na UI da sua documentaรงรฃo (vocรช estarรก em um URL como `https://qltnci.deta.dev/docs`) envie um request para *operaรงรฃo de rota* `/items/{item_id}`. - -Por exemplo com ID `5`. - -Agora vรก para https://web.deta.sh. - -Vocรช verรก que hรก uma seรงรฃo ร  esquerda chamada "Micros" com cada um dos seus apps. - -Vocรช verรก uma aba com "Detalhes", e tambรฉm a aba "Visor", vรก para "Visor". - -Lรก vocรช pode inspecionar as solicitaรงรตes recentes enviadas ao seu aplicativo. - -Vocรช tambรฉm pode editรก-los e reproduzi-los novamente. - - - -## Saiba mais - -Em algum momento, vocรช provavelmente desejarรก armazenar alguns dados para seu aplicativo de uma forma que persista ao longo do tempo. Para isso vocรช pode usar Deta Base, que tambรฉm tem um generoso **nรญvel gratuito**. - -Vocรช tambรฉm pode ler mais na documentaรงรฃo da Deta. - -## Conceitos de implantaรงรฃo - -Voltando aos conceitos que discutimos em [Deployments Concepts](./concepts.md){.internal-link target=_blank}, veja como cada um deles seria tratado com a Deta: - -* **HTTPS**: Realizado pela Deta, eles fornecerรฃo um subdomรญnio e lidarรฃo com HTTPS automaticamente. -* **Executando na inicializaรงรฃo**: Realizado pela Deta, como parte de seu serviรงo. -* **Reinicializaรงรฃo**: Realizado pela Deta, como parte de seu serviรงo. -* **Replicaรงรฃo**: Realizado pela Deta, como parte de seu serviรงo. -* **Memรณria**: Limite predefinido pela Deta, vocรช pode contatรก-los para aumentรก-lo. -* **Etapas anteriores a inicializaรงรฃo**: Nรฃo suportado diretamente, vocรช pode fazรช-lo funcionar com o sistema Cron ou scripts adicionais. - -!!! note "Nota" - O Deta foi projetado para facilitar (e gratuitamente) a implantaรงรฃo rรกpida de aplicativos simples. - - Ele pode simplificar vรกrios casos de uso, mas, ao mesmo tempo, nรฃo suporta outros, como o uso de bancos de dados externos (alรฉm do prรณprio sistema de banco de dados NoSQL da Deta), mรกquinas virtuais personalizadas, etc. - - Vocรช pode ler mais detalhes na documentaรงรฃo da Deta para ver se รฉ a escolha certa para vocรช. diff --git a/docs_src/extending_openapi/tutorial003.py b/docs_src/configure_swagger_ui/tutorial001.py similarity index 100% rename from docs_src/extending_openapi/tutorial003.py rename to docs_src/configure_swagger_ui/tutorial001.py diff --git a/docs_src/extending_openapi/tutorial004.py b/docs_src/configure_swagger_ui/tutorial002.py similarity index 100% rename from docs_src/extending_openapi/tutorial004.py rename to docs_src/configure_swagger_ui/tutorial002.py diff --git a/docs_src/extending_openapi/tutorial005.py b/docs_src/configure_swagger_ui/tutorial003.py similarity index 100% rename from docs_src/extending_openapi/tutorial005.py rename to docs_src/configure_swagger_ui/tutorial003.py diff --git a/docs_src/custom_docs_ui/tutorial001.py b/docs_src/custom_docs_ui/tutorial001.py new file mode 100644 index 0000000000000..f7ceb0c2fcf5c --- /dev/null +++ b/docs_src/custom_docs_ui/tutorial001.py @@ -0,0 +1,38 @@ +from fastapi import FastAPI +from fastapi.openapi.docs import ( + get_redoc_html, + get_swagger_ui_html, + get_swagger_ui_oauth2_redirect_html, +) + +app = FastAPI(docs_url=None, redoc_url=None) + + +@app.get("/docs", include_in_schema=False) +async def custom_swagger_ui_html(): + return get_swagger_ui_html( + openapi_url=app.openapi_url, + title=app.title + " - Swagger UI", + oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, + swagger_js_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js", + swagger_css_url="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css", + ) + + +@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False) +async def swagger_ui_redirect(): + return get_swagger_ui_oauth2_redirect_html() + + +@app.get("/redoc", include_in_schema=False) +async def redoc_html(): + return get_redoc_html( + openapi_url=app.openapi_url, + title=app.title + " - ReDoc", + redoc_js_url="https://unpkg.com/redoc@next/bundles/redoc.standalone.js", + ) + + +@app.get("/users/{username}") +async def read_user(username: str): + return {"message": f"Hello {username}"} diff --git a/docs_src/extending_openapi/tutorial002.py b/docs_src/custom_docs_ui/tutorial002.py similarity index 100% rename from docs_src/extending_openapi/tutorial002.py rename to docs_src/custom_docs_ui/tutorial002.py diff --git a/docs_src/schema_extra_example/tutorial005.py b/docs_src/schema_extra_example/tutorial005.py new file mode 100644 index 0000000000000..b8217c27e9971 --- /dev/null +++ b/docs_src/schema_extra_example/tutorial005.py @@ -0,0 +1,51 @@ +from typing import Union + +from fastapi import Body, FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: Union[str, None] = None + price: float + tax: Union[float, None] = None + + +@app.put("/items/{item_id}") +async def update_item( + *, + item_id: int, + item: Item = Body( + openapi_examples={ + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": { + "name": "Bar", + "price": "35.4", + }, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + ), +): + results = {"item_id": item_id, "item": item} + return results diff --git a/docs_src/schema_extra_example/tutorial005_an.py b/docs_src/schema_extra_example/tutorial005_an.py new file mode 100644 index 0000000000000..4b2d9c662b7d6 --- /dev/null +++ b/docs_src/schema_extra_example/tutorial005_an.py @@ -0,0 +1,55 @@ +from typing import Union + +from fastapi import Body, FastAPI +from pydantic import BaseModel +from typing_extensions import Annotated + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: Union[str, None] = None + price: float + tax: Union[float, None] = None + + +@app.put("/items/{item_id}") +async def update_item( + *, + item_id: int, + item: Annotated[ + Item, + Body( + openapi_examples={ + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": { + "name": "Bar", + "price": "35.4", + }, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + ), + ], +): + results = {"item_id": item_id, "item": item} + return results diff --git a/docs_src/schema_extra_example/tutorial005_an_py310.py b/docs_src/schema_extra_example/tutorial005_an_py310.py new file mode 100644 index 0000000000000..64dc2cf90a723 --- /dev/null +++ b/docs_src/schema_extra_example/tutorial005_an_py310.py @@ -0,0 +1,54 @@ +from typing import Annotated + +from fastapi import Body, FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: str | None = None + price: float + tax: float | None = None + + +@app.put("/items/{item_id}") +async def update_item( + *, + item_id: int, + item: Annotated[ + Item, + Body( + openapi_examples={ + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": { + "name": "Bar", + "price": "35.4", + }, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + ), + ], +): + results = {"item_id": item_id, "item": item} + return results diff --git a/docs_src/schema_extra_example/tutorial005_an_py39.py b/docs_src/schema_extra_example/tutorial005_an_py39.py new file mode 100644 index 0000000000000..edeb1affce572 --- /dev/null +++ b/docs_src/schema_extra_example/tutorial005_an_py39.py @@ -0,0 +1,54 @@ +from typing import Annotated, Union + +from fastapi import Body, FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: Union[str, None] = None + price: float + tax: Union[float, None] = None + + +@app.put("/items/{item_id}") +async def update_item( + *, + item_id: int, + item: Annotated[ + Item, + Body( + openapi_examples={ + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": { + "name": "Bar", + "price": "35.4", + }, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + ), + ], +): + results = {"item_id": item_id, "item": item} + return results diff --git a/docs_src/schema_extra_example/tutorial005_py310.py b/docs_src/schema_extra_example/tutorial005_py310.py new file mode 100644 index 0000000000000..eef973343dffe --- /dev/null +++ b/docs_src/schema_extra_example/tutorial005_py310.py @@ -0,0 +1,49 @@ +from fastapi import Body, FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: str | None = None + price: float + tax: float | None = None + + +@app.put("/items/{item_id}") +async def update_item( + *, + item_id: int, + item: Item = Body( + openapi_examples={ + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": { + "name": "Bar", + "price": "35.4", + }, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + ), +): + results = {"item_id": item_id, "item": item} + return results diff --git a/docs_src/separate_openapi_schemas/tutorial001.py b/docs_src/separate_openapi_schemas/tutorial001.py new file mode 100644 index 0000000000000..415eef8e2824f --- /dev/null +++ b/docs_src/separate_openapi_schemas/tutorial001.py @@ -0,0 +1,28 @@ +from typing import List, Union + +from fastapi import FastAPI +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + description: Union[str, None] = None + + +app = FastAPI() + + +@app.post("/items/") +def create_item(item: Item): + return item + + +@app.get("/items/") +def read_items() -> List[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + ), + Item(name="Plumbus"), + ] diff --git a/docs_src/separate_openapi_schemas/tutorial001_py310.py b/docs_src/separate_openapi_schemas/tutorial001_py310.py new file mode 100644 index 0000000000000..289cb54eda2a2 --- /dev/null +++ b/docs_src/separate_openapi_schemas/tutorial001_py310.py @@ -0,0 +1,26 @@ +from fastapi import FastAPI +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + description: str | None = None + + +app = FastAPI() + + +@app.post("/items/") +def create_item(item: Item): + return item + + +@app.get("/items/") +def read_items() -> list[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + ), + Item(name="Plumbus"), + ] diff --git a/docs_src/separate_openapi_schemas/tutorial001_py39.py b/docs_src/separate_openapi_schemas/tutorial001_py39.py new file mode 100644 index 0000000000000..63cffd1e307fb --- /dev/null +++ b/docs_src/separate_openapi_schemas/tutorial001_py39.py @@ -0,0 +1,28 @@ +from typing import Optional + +from fastapi import FastAPI +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + description: Optional[str] = None + + +app = FastAPI() + + +@app.post("/items/") +def create_item(item: Item): + return item + + +@app.get("/items/") +def read_items() -> list[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + ), + Item(name="Plumbus"), + ] diff --git a/docs_src/separate_openapi_schemas/tutorial002.py b/docs_src/separate_openapi_schemas/tutorial002.py new file mode 100644 index 0000000000000..7df93783b9585 --- /dev/null +++ b/docs_src/separate_openapi_schemas/tutorial002.py @@ -0,0 +1,28 @@ +from typing import List, Union + +from fastapi import FastAPI +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + description: Union[str, None] = None + + +app = FastAPI(separate_input_output_schemas=False) + + +@app.post("/items/") +def create_item(item: Item): + return item + + +@app.get("/items/") +def read_items() -> List[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + ), + Item(name="Plumbus"), + ] diff --git a/docs_src/separate_openapi_schemas/tutorial002_py310.py b/docs_src/separate_openapi_schemas/tutorial002_py310.py new file mode 100644 index 0000000000000..5db21087293f1 --- /dev/null +++ b/docs_src/separate_openapi_schemas/tutorial002_py310.py @@ -0,0 +1,26 @@ +from fastapi import FastAPI +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + description: str | None = None + + +app = FastAPI(separate_input_output_schemas=False) + + +@app.post("/items/") +def create_item(item: Item): + return item + + +@app.get("/items/") +def read_items() -> list[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + ), + Item(name="Plumbus"), + ] diff --git a/docs_src/separate_openapi_schemas/tutorial002_py39.py b/docs_src/separate_openapi_schemas/tutorial002_py39.py new file mode 100644 index 0000000000000..50d997d92aa43 --- /dev/null +++ b/docs_src/separate_openapi_schemas/tutorial002_py39.py @@ -0,0 +1,28 @@ +from typing import Optional + +from fastapi import FastAPI +from pydantic import BaseModel + + +class Item(BaseModel): + name: str + description: Optional[str] = None + + +app = FastAPI(separate_input_output_schemas=False) + + +@app.post("/items/") +def create_item(item: Item): + return item + + +@app.get("/items/") +def read_items() -> list[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + ), + Item(name="Plumbus"), + ] diff --git a/fastapi/__init__.py b/fastapi/__init__.py index d8abf2103efc4..ff8b98d3b5ffe 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.101.1" +__version__ = "0.103.0" from starlette import status as status diff --git a/fastapi/_compat.py b/fastapi/_compat.py index 9ffcaf40925ea..eb55b08f2e6e3 100644 --- a/fastapi/_compat.py +++ b/fastapi/_compat.py @@ -181,9 +181,13 @@ def get_schema_from_model_field( field_mapping: Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue ], + separate_input_output_schemas: bool = True, ) -> Dict[str, Any]: + override_mode: Union[Literal["validation"], None] = ( + None if separate_input_output_schemas else "validation" + ) # This expects that GenerateJsonSchema was already used to generate the definitions - json_schema = field_mapping[(field, field.mode)] + json_schema = field_mapping[(field, override_mode or field.mode)] if "$ref" not in json_schema: # TODO remove when deprecating Pydantic v1 # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207 @@ -200,14 +204,19 @@ def get_definitions( fields: List[ModelField], schema_generator: GenerateJsonSchema, model_name_map: ModelNameMap, + separate_input_output_schemas: bool = True, ) -> Tuple[ Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue ], Dict[str, Dict[str, Any]], ]: + override_mode: Union[Literal["validation"], None] = ( + None if separate_input_output_schemas else "validation" + ) inputs = [ - (field, field.mode, field._type_adapter.core_schema) for field in fields + (field, override_mode or field.mode, field._type_adapter.core_schema) + for field in fields ] field_mapping, definitions = schema_generator.generate_definitions( inputs=inputs @@ -429,6 +438,7 @@ def get_schema_from_model_field( field_mapping: Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue ], + separate_input_output_schemas: bool = True, ) -> Dict[str, Any]: # This expects that GenerateJsonSchema was already used to generate the definitions return field_schema( # type: ignore[no-any-return] @@ -444,6 +454,7 @@ def get_definitions( fields: List[ModelField], schema_generator: GenerateJsonSchema, model_name_map: ModelNameMap, + separate_input_output_schemas: bool = True, ) -> Tuple[ Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue diff --git a/fastapi/applications.py b/fastapi/applications.py index e32cfa03d20cb..b681e50b395d7 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -92,6 +92,7 @@ def __init__( generate_unique_id_function: Callable[[routing.APIRoute], str] = Default( generate_unique_id ), + separate_input_output_schemas: bool = True, **extra: Any, ) -> None: self.debug = debug @@ -111,6 +112,7 @@ def __init__( self.swagger_ui_init_oauth = swagger_ui_init_oauth self.swagger_ui_parameters = swagger_ui_parameters self.servers = servers or [] + self.separate_input_output_schemas = separate_input_output_schemas self.extra = extra self.openapi_version = "3.1.0" self.openapi_schema: Optional[Dict[str, Any]] = None @@ -227,6 +229,7 @@ def openapi(self) -> Dict[str, Any]: webhooks=self.webhooks.routes, tags=self.openapi_tags, servers=self.servers, + separate_input_output_schemas=self.separate_input_output_schemas, ) return self.openapi_schema diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py index 2268dd229091d..3d982eb9aec98 100644 --- a/fastapi/openapi/models.py +++ b/fastapi/openapi/models.py @@ -11,7 +11,7 @@ ) from fastapi.logger import logger from pydantic import AnyUrl, BaseModel, Field -from typing_extensions import Annotated, Literal +from typing_extensions import Annotated, Literal, TypedDict from typing_extensions import deprecated as typing_deprecated try: @@ -267,14 +267,14 @@ class Config: SchemaOrBool = Union[Schema, bool] -class Example(BaseModel): - summary: Optional[str] = None - description: Optional[str] = None - value: Optional[Any] = None - externalValue: Optional[AnyUrl] = None +class Example(TypedDict, total=False): + summary: Optional[str] + description: Optional[str] + value: Optional[Any] + externalValue: Optional[AnyUrl] - if PYDANTIC_V2: - model_config = {"extra": "allow"} + if PYDANTIC_V2: # type: ignore [misc] + __pydantic_config__ = {"extra": "allow"} else: diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index e295361e6a9a1..5bfb5acef7fc4 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -95,6 +95,7 @@ def get_openapi_operation_parameters( field_mapping: Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue ], + separate_input_output_schemas: bool = True, ) -> List[Dict[str, Any]]: parameters = [] for param in all_route_params: @@ -107,6 +108,7 @@ def get_openapi_operation_parameters( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) parameter = { "name": param.alias, @@ -116,7 +118,9 @@ def get_openapi_operation_parameters( } if field_info.description: parameter["description"] = field_info.description - if field_info.example != Undefined: + if field_info.openapi_examples: + parameter["examples"] = jsonable_encoder(field_info.openapi_examples) + elif field_info.example != Undefined: parameter["example"] = jsonable_encoder(field_info.example) if field_info.deprecated: parameter["deprecated"] = field_info.deprecated @@ -132,6 +136,7 @@ def get_openapi_operation_request_body( field_mapping: Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue ], + separate_input_output_schemas: bool = True, ) -> Optional[Dict[str, Any]]: if not body_field: return None @@ -141,6 +146,7 @@ def get_openapi_operation_request_body( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) field_info = cast(Body, body_field.field_info) request_media_type = field_info.media_type @@ -149,7 +155,11 @@ def get_openapi_operation_request_body( if required: request_body_oai["required"] = required request_media_content: Dict[str, Any] = {"schema": body_schema} - if field_info.example != Undefined: + if field_info.openapi_examples: + request_media_content["examples"] = jsonable_encoder( + field_info.openapi_examples + ) + elif field_info.example != Undefined: request_media_content["example"] = jsonable_encoder(field_info.example) request_body_oai["content"] = {request_media_type: request_media_content} return request_body_oai @@ -211,6 +221,7 @@ def get_openapi_path( field_mapping: Dict[ Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue ], + separate_input_output_schemas: bool = True, ) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]: path = {} security_schemes: Dict[str, Any] = {} @@ -242,6 +253,7 @@ def get_openapi_path( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) parameters.extend(operation_parameters) if parameters: @@ -263,6 +275,7 @@ def get_openapi_path( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) if request_body_oai: operation["requestBody"] = request_body_oai @@ -280,6 +293,7 @@ def get_openapi_path( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) callbacks[callback.name] = {callback.path: cb_path} operation["callbacks"] = callbacks @@ -310,6 +324,7 @@ def get_openapi_path( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) else: response_schema = {} @@ -343,6 +358,7 @@ def get_openapi_path( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) media_type = route_response_media_type or "application/json" additional_schema = ( @@ -433,6 +449,7 @@ def get_openapi( terms_of_service: Optional[str] = None, contact: Optional[Dict[str, Union[str, Any]]] = None, license_info: Optional[Dict[str, Union[str, Any]]] = None, + separate_input_output_schemas: bool = True, ) -> Dict[str, Any]: info: Dict[str, Any] = {"title": title, "version": version} if summary: @@ -459,6 +476,7 @@ def get_openapi( fields=all_fields, schema_generator=schema_generator, model_name_map=model_name_map, + separate_input_output_schemas=separate_input_output_schemas, ) for route in routes or []: if isinstance(route, routing.APIRoute): @@ -468,6 +486,7 @@ def get_openapi( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) if result: path, security_schemes, path_definitions = result @@ -487,6 +506,7 @@ def get_openapi( schema_generator=schema_generator, model_name_map=model_name_map, field_mapping=field_mapping, + separate_input_output_schemas=separate_input_output_schemas, ) if result: path, security_schemes, path_definitions = result diff --git a/fastapi/param_functions.py b/fastapi/param_functions.py index a43afaf311798..63914d1d68ff4 100644 --- a/fastapi/param_functions.py +++ b/fastapi/param_functions.py @@ -2,6 +2,7 @@ from fastapi import params from fastapi._compat import Undefined +from fastapi.openapi.models import Example from typing_extensions import Annotated, deprecated _Unset: Any = Undefined @@ -46,6 +47,7 @@ def Path( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -76,6 +78,7 @@ def Path( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, @@ -122,6 +125,7 @@ def Query( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -152,6 +156,7 @@ def Query( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, @@ -199,6 +204,7 @@ def Header( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -230,6 +236,7 @@ def Header( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, @@ -276,6 +283,7 @@ def Cookie( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -306,6 +314,7 @@ def Cookie( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, @@ -354,6 +363,7 @@ def Body( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -386,6 +396,7 @@ def Body( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, @@ -433,6 +444,7 @@ def Form( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -464,6 +476,7 @@ def Form( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, @@ -511,6 +524,7 @@ def File( # noqa: N802 "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -542,6 +556,7 @@ def File( # noqa: N802 decimal_places=decimal_places, example=example, examples=examples, + openapi_examples=openapi_examples, deprecated=deprecated, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, diff --git a/fastapi/params.py b/fastapi/params.py index 2d8100650e492..b40944dba6f56 100644 --- a/fastapi/params.py +++ b/fastapi/params.py @@ -2,6 +2,7 @@ from enum import Enum from typing import Any, Callable, Dict, List, Optional, Sequence, Union +from fastapi.openapi.models import Example from pydantic.fields import FieldInfo from typing_extensions import Annotated, deprecated @@ -61,6 +62,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -75,6 +77,7 @@ def __init__( ) self.example = example self.include_in_schema = include_in_schema + self.openapi_examples = openapi_examples kwargs = dict( default=default, default_factory=default_factory, @@ -170,6 +173,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -204,6 +208,7 @@ def __init__( deprecated=deprecated, example=example, examples=examples, + openapi_examples=openapi_examples, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, **extra, @@ -254,6 +259,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -286,6 +292,7 @@ def __init__( deprecated=deprecated, example=example, examples=examples, + openapi_examples=openapi_examples, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, **extra, @@ -337,6 +344,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -370,6 +378,7 @@ def __init__( deprecated=deprecated, example=example, examples=examples, + openapi_examples=openapi_examples, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, **extra, @@ -420,6 +429,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -452,6 +462,7 @@ def __init__( deprecated=deprecated, example=example, examples=examples, + openapi_examples=openapi_examples, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, **extra, @@ -502,6 +513,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -518,6 +530,7 @@ def __init__( ) self.example = example self.include_in_schema = include_in_schema + self.openapi_examples = openapi_examples kwargs = dict( default=default, default_factory=default_factory, @@ -613,6 +626,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -647,6 +661,7 @@ def __init__( deprecated=deprecated, example=example, examples=examples, + openapi_examples=openapi_examples, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, **extra, @@ -696,6 +711,7 @@ def __init__( "although still supported. Use examples instead." ), ] = _Unset, + openapi_examples: Optional[Dict[str, Example]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, json_schema_extra: Union[Dict[str, Any], None] = None, @@ -729,6 +745,7 @@ def __init__( deprecated=deprecated, example=example, examples=examples, + openapi_examples=openapi_examples, include_in_schema=include_in_schema, json_schema_extra=json_schema_extra, **extra, diff --git a/requirements-docs.txt b/requirements-docs.txt index 220d1ec3a5aa1..2e667720e41e4 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -2,6 +2,7 @@ mkdocs-material==9.1.21 mdx-include >=1.4.1,<2.0.0 mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0 +mkdocs-redirects>=1.2.1,<1.3.0 typer-cli >=0.0.13,<0.0.14 typer[all] >=0.6.1,<0.8.0 pyyaml >=5.3.1,<7.0.0 diff --git a/requirements-tests.txt b/requirements-tests.txt index 0113b6f7ae9f1..6f7f4ac235d40 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -11,7 +11,6 @@ dirty-equals ==0.6.0 # TODO: once removing databases from tutorial, upgrade SQLAlchemy # probably when including SQLModel sqlalchemy >=1.3.18,<1.4.43 -peewee >=3.13.3,<4.0.0 databases[sqlite] >=0.3.2,<0.7.0 orjson >=3.2.1,<4.0.0 ujson >=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0 diff --git a/requirements.txt b/requirements.txt index 7e746016a42de..ef25ec483fccb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,5 @@ -r requirements-docs.txt uvicorn[standard] >=0.12.0,<0.23.0 pre-commit >=2.17.0,<4.0.0 +# For generating screenshots +playwright diff --git a/scripts/playwright/separate_openapi_schemas/image01.py b/scripts/playwright/separate_openapi_schemas/image01.py new file mode 100644 index 0000000000000..0b40f3bbcf1c7 --- /dev/null +++ b/scripts/playwright/separate_openapi_schemas/image01.py @@ -0,0 +1,29 @@ +import subprocess + +from playwright.sync_api import Playwright, sync_playwright + + +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_text("POST/items/Create Item").click() + page.get_by_role("tab", name="Schema").first.click() + page.screenshot( + path="docs/en/docs/img/tutorial/separate-openapi-schemas/image01.png" + ) + + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"] +) +try: + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/scripts/playwright/separate_openapi_schemas/image02.py b/scripts/playwright/separate_openapi_schemas/image02.py new file mode 100644 index 0000000000000..f76af7ee22177 --- /dev/null +++ b/scripts/playwright/separate_openapi_schemas/image02.py @@ -0,0 +1,30 @@ +import subprocess + +from playwright.sync_api import Playwright, sync_playwright + + +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_text("GET/items/Read Items").click() + page.get_by_role("button", name="Try it out").click() + page.get_by_role("button", name="Execute").click() + page.screenshot( + path="docs/en/docs/img/tutorial/separate-openapi-schemas/image02.png" + ) + + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"] +) +try: + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/scripts/playwright/separate_openapi_schemas/image03.py b/scripts/playwright/separate_openapi_schemas/image03.py new file mode 100644 index 0000000000000..127f5c428e86e --- /dev/null +++ b/scripts/playwright/separate_openapi_schemas/image03.py @@ -0,0 +1,30 @@ +import subprocess + +from playwright.sync_api import Playwright, sync_playwright + + +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_text("GET/items/Read Items").click() + page.get_by_role("tab", name="Schema").click() + page.get_by_label("Schema").get_by_role("button", name="Expand all").click() + page.screenshot( + path="docs/en/docs/img/tutorial/separate-openapi-schemas/image03.png" + ) + + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"] +) +try: + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/scripts/playwright/separate_openapi_schemas/image04.py b/scripts/playwright/separate_openapi_schemas/image04.py new file mode 100644 index 0000000000000..208eaf8a0c781 --- /dev/null +++ b/scripts/playwright/separate_openapi_schemas/image04.py @@ -0,0 +1,29 @@ +import subprocess + +from playwright.sync_api import Playwright, sync_playwright + + +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_role("button", name="Item-Input").click() + page.get_by_role("button", name="Item-Output").click() + page.set_viewport_size({"width": 960, "height": 820}) + page.screenshot( + path="docs/en/docs/img/tutorial/separate-openapi-schemas/image04.png" + ) + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["uvicorn", "docs_src.separate_openapi_schemas.tutorial001:app"] +) +try: + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/scripts/playwright/separate_openapi_schemas/image05.py b/scripts/playwright/separate_openapi_schemas/image05.py new file mode 100644 index 0000000000000..83966b4498cac --- /dev/null +++ b/scripts/playwright/separate_openapi_schemas/image05.py @@ -0,0 +1,29 @@ +import subprocess + +from playwright.sync_api import Playwright, sync_playwright + + +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_role("button", name="Item", exact=True).click() + page.set_viewport_size({"width": 960, "height": 700}) + page.screenshot( + path="docs/en/docs/img/tutorial/separate-openapi-schemas/image05.png" + ) + + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["uvicorn", "docs_src.separate_openapi_schemas.tutorial002:app"] +) +try: + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py index 931f08fc1cf5c..a51ca7253f825 100644 --- a/tests/test_multi_body_errors.py +++ b/tests/test_multi_body_errors.py @@ -51,7 +51,7 @@ def test_jsonable_encoder_requiring_error(): "loc": ["body", 0, "age"], "msg": "Input should be greater than 0", "input": -1.0, - "ctx": {"gt": "0"}, + "ctx": {"gt": 0}, "url": match_pydantic_error_url("greater_than"), } ] @@ -84,25 +84,12 @@ def test_put_incorrect_body_multiple(): "input": {"age": "five"}, "url": match_pydantic_error_url("missing"), }, - { - "ctx": {"class": "Decimal"}, - "input": "five", - "loc": ["body", 0, "age", "is-instance[Decimal]"], - "msg": "Input should be an instance of Decimal", - "type": "is_instance_of", - "url": match_pydantic_error_url("is_instance_of"), - }, { "type": "decimal_parsing", - "loc": [ - "body", - 0, - "age", - "function-after[to_decimal(), " - "union[int,constrained-str,function-plain[str()]]]", - ], + "loc": ["body", 0, "age"], "msg": "Input should be a valid decimal", "input": "five", + "url": match_pydantic_error_url("decimal_parsing"), }, { "type": "missing", @@ -111,25 +98,12 @@ def test_put_incorrect_body_multiple(): "input": {"age": "six"}, "url": match_pydantic_error_url("missing"), }, - { - "ctx": {"class": "Decimal"}, - "input": "six", - "loc": ["body", 1, "age", "is-instance[Decimal]"], - "msg": "Input should be an instance of Decimal", - "type": "is_instance_of", - "url": match_pydantic_error_url("is_instance_of"), - }, { "type": "decimal_parsing", - "loc": [ - "body", - 1, - "age", - "function-after[to_decimal(), " - "union[int,constrained-str,function-plain[str()]]]", - ], + "loc": ["body", 1, "age"], "msg": "Input should be a valid decimal", "input": "six", + "url": match_pydantic_error_url("decimal_parsing"), }, ] } diff --git a/tests/test_openapi_examples.py b/tests/test_openapi_examples.py new file mode 100644 index 0000000000000..d0e35953e156c --- /dev/null +++ b/tests/test_openapi_examples.py @@ -0,0 +1,455 @@ +from typing import Union + +from dirty_equals import IsDict +from fastapi import Body, Cookie, FastAPI, Header, Path, Query +from fastapi.testclient import TestClient +from pydantic import BaseModel + +app = FastAPI() + + +class Item(BaseModel): + data: str + + +@app.post("/examples/") +def examples( + item: Item = Body( + examples=[ + {"data": "Data in Body examples, example1"}, + ], + openapi_examples={ + "Example One": { + "summary": "Example One Summary", + "description": "Example One Description", + "value": {"data": "Data in Body examples, example1"}, + }, + "Example Two": { + "value": {"data": "Data in Body examples, example2"}, + }, + }, + ) +): + return item + + +@app.get("/path_examples/{item_id}") +def path_examples( + item_id: str = Path( + examples=[ + "json_schema_item_1", + "json_schema_item_2", + ], + openapi_examples={ + "Path One": { + "summary": "Path One Summary", + "description": "Path One Description", + "value": "item_1", + }, + "Path Two": { + "value": "item_2", + }, + }, + ), +): + return item_id + + +@app.get("/query_examples/") +def query_examples( + data: Union[str, None] = Query( + default=None, + examples=[ + "json_schema_query1", + "json_schema_query2", + ], + openapi_examples={ + "Query One": { + "summary": "Query One Summary", + "description": "Query One Description", + "value": "query1", + }, + "Query Two": { + "value": "query2", + }, + }, + ), +): + return data + + +@app.get("/header_examples/") +def header_examples( + data: Union[str, None] = Header( + default=None, + examples=[ + "json_schema_header1", + "json_schema_header2", + ], + openapi_examples={ + "Header One": { + "summary": "Header One Summary", + "description": "Header One Description", + "value": "header1", + }, + "Header Two": { + "value": "header2", + }, + }, + ), +): + return data + + +@app.get("/cookie_examples/") +def cookie_examples( + data: Union[str, None] = Cookie( + default=None, + examples=["json_schema_cookie1", "json_schema_cookie2"], + openapi_examples={ + "Cookie One": { + "summary": "Cookie One Summary", + "description": "Cookie One Description", + "value": "cookie1", + }, + "Cookie Two": { + "value": "cookie2", + }, + }, + ), +): + return data + + +client = TestClient(app) + + +def test_call_api(): + response = client.get("/path_examples/foo") + assert response.status_code == 200, response.text + + response = client.get("/query_examples/") + assert response.status_code == 200, response.text + + response = client.get("/header_examples/") + assert response.status_code == 200, response.text + + response = client.get("/cookie_examples/") + assert response.status_code == 200, response.text + + +def test_openapi_schema(): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/examples/": { + "post": { + "summary": "Examples", + "operationId": "examples_examples__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "allOf": [{"$ref": "#/components/schemas/Item"}], + "title": "Item", + "examples": [ + {"data": "Data in Body examples, example1"} + ], + }, + "examples": { + "Example One": { + "summary": "Example One Summary", + "description": "Example One Description", + "value": { + "data": "Data in Body examples, example1" + }, + }, + "Example Two": { + "value": { + "data": "Data in Body examples, example2" + } + }, + }, + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + "/path_examples/{item_id}": { + "get": { + "summary": "Path Examples", + "operationId": "path_examples_path_examples__item_id__get", + "parameters": [ + { + "name": "item_id", + "in": "path", + "required": True, + "schema": { + "type": "string", + "examples": [ + "json_schema_item_1", + "json_schema_item_2", + ], + "title": "Item Id", + }, + "examples": { + "Path One": { + "summary": "Path One Summary", + "description": "Path One Description", + "value": "item_1", + }, + "Path Two": {"value": "item_2"}, + }, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + "/query_examples/": { + "get": { + "summary": "Query Examples", + "operationId": "query_examples_query_examples__get", + "parameters": [ + { + "name": "data", + "in": "query", + "required": False, + "schema": IsDict( + { + "anyOf": [{"type": "string"}, {"type": "null"}], + "examples": [ + "json_schema_query1", + "json_schema_query2", + ], + "title": "Data", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "examples": [ + "json_schema_query1", + "json_schema_query2", + ], + "type": "string", + "title": "Data", + } + ), + "examples": { + "Query One": { + "summary": "Query One Summary", + "description": "Query One Description", + "value": "query1", + }, + "Query Two": {"value": "query2"}, + }, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + "/header_examples/": { + "get": { + "summary": "Header Examples", + "operationId": "header_examples_header_examples__get", + "parameters": [ + { + "name": "data", + "in": "header", + "required": False, + "schema": IsDict( + { + "anyOf": [{"type": "string"}, {"type": "null"}], + "examples": [ + "json_schema_header1", + "json_schema_header2", + ], + "title": "Data", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "string", + "examples": [ + "json_schema_header1", + "json_schema_header2", + ], + "title": "Data", + } + ), + "examples": { + "Header One": { + "summary": "Header One Summary", + "description": "Header One Description", + "value": "header1", + }, + "Header Two": {"value": "header2"}, + }, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + "/cookie_examples/": { + "get": { + "summary": "Cookie Examples", + "operationId": "cookie_examples_cookie_examples__get", + "parameters": [ + { + "name": "data", + "in": "cookie", + "required": False, + "schema": IsDict( + { + "anyOf": [{"type": "string"}, {"type": "null"}], + "examples": [ + "json_schema_cookie1", + "json_schema_cookie2", + ], + "title": "Data", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "string", + "examples": [ + "json_schema_cookie1", + "json_schema_cookie2", + ], + "title": "Data", + } + ), + "examples": { + "Cookie One": { + "summary": "Cookie One Summary", + "description": "Cookie One Description", + "value": "cookie1", + }, + "Cookie Two": {"value": "cookie2"}, + }, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item": { + "properties": {"data": {"type": "string", "title": "Data"}}, + "type": "object", + "required": ["data"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_openapi_separate_input_output_schemas.py b/tests/test_openapi_separate_input_output_schemas.py new file mode 100644 index 0000000000000..70f4b90d75128 --- /dev/null +++ b/tests/test_openapi_separate_input_output_schemas.py @@ -0,0 +1,490 @@ +from typing import List, Optional + +from fastapi import FastAPI +from fastapi.testclient import TestClient +from pydantic import BaseModel + +from .utils import needs_pydanticv2 + + +class SubItem(BaseModel): + subname: str + sub_description: Optional[str] = None + tags: List[str] = [] + + +class Item(BaseModel): + name: str + description: Optional[str] = None + sub: Optional[SubItem] = None + + +def get_app_client(separate_input_output_schemas: bool = True) -> TestClient: + app = FastAPI(separate_input_output_schemas=separate_input_output_schemas) + + @app.post("/items/") + def create_item(item: Item): + return item + + @app.post("/items-list/") + def create_item_list(item: List[Item]): + return item + + @app.get("/items/") + def read_items() -> List[Item]: + return [ + Item( + name="Portal Gun", + description="Device to travel through the multi-rick-verse", + sub=SubItem(subname="subname"), + ), + Item(name="Plumbus"), + ] + + client = TestClient(app) + return client + + +def test_create_item(): + client = get_app_client() + client_no = get_app_client(separate_input_output_schemas=False) + response = client.post("/items/", json={"name": "Plumbus"}) + response2 = client_no.post("/items/", json={"name": "Plumbus"}) + assert response.status_code == response2.status_code == 200, response.text + assert ( + response.json() + == response2.json() + == {"name": "Plumbus", "description": None, "sub": None} + ) + + +def test_create_item_with_sub(): + client = get_app_client() + client_no = get_app_client(separate_input_output_schemas=False) + data = { + "name": "Plumbus", + "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF"}, + } + response = client.post("/items/", json=data) + response2 = client_no.post("/items/", json=data) + assert response.status_code == response2.status_code == 200, response.text + assert ( + response.json() + == response2.json() + == { + "name": "Plumbus", + "description": None, + "sub": {"subname": "SubPlumbus", "sub_description": "Sub WTF", "tags": []}, + } + ) + + +def test_create_item_list(): + client = get_app_client() + client_no = get_app_client(separate_input_output_schemas=False) + data = [ + {"name": "Plumbus"}, + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + ] + response = client.post("/items-list/", json=data) + response2 = client_no.post("/items-list/", json=data) + assert response.status_code == response2.status_code == 200, response.text + assert ( + response.json() + == response2.json() + == [ + {"name": "Plumbus", "description": None, "sub": None}, + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + "sub": None, + }, + ] + ) + + +def test_read_items(): + client = get_app_client() + client_no = get_app_client(separate_input_output_schemas=False) + response = client.get("/items/") + response2 = client_no.get("/items/") + assert response.status_code == response2.status_code == 200, response.text + assert ( + response.json() + == response2.json() + == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + "sub": {"subname": "subname", "sub_description": None, "tags": []}, + }, + {"name": "Plumbus", "description": None, "sub": None}, + ] + ) + + +@needs_pydanticv2 +def test_openapi_schema(): + client = get_app_client() + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": { + "$ref": "#/components/schemas/Item-Output" + }, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item-Input"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/items-list/": { + "post": { + "summary": "Create Item List", + "operationId": "create_item_list_items_list__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Item-Input" + }, + "type": "array", + "title": "Item", + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item-Input": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "sub": { + "anyOf": [ + {"$ref": "#/components/schemas/SubItem-Input"}, + {"type": "null"}, + ] + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "Item-Output": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "sub": { + "anyOf": [ + {"$ref": "#/components/schemas/SubItem-Output"}, + {"type": "null"}, + ] + }, + }, + "type": "object", + "required": ["name", "description", "sub"], + "title": "Item", + }, + "SubItem-Input": { + "properties": { + "subname": {"type": "string", "title": "Subname"}, + "sub_description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Sub Description", + }, + "tags": { + "items": {"type": "string"}, + "type": "array", + "title": "Tags", + "default": [], + }, + }, + "type": "object", + "required": ["subname"], + "title": "SubItem", + }, + "SubItem-Output": { + "properties": { + "subname": {"type": "string", "title": "Subname"}, + "sub_description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Sub Description", + }, + "tags": { + "items": {"type": "string"}, + "type": "array", + "title": "Tags", + "default": [], + }, + }, + "type": "object", + "required": ["subname", "sub_description", "tags"], + "title": "SubItem", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } + + +@needs_pydanticv2 +def test_openapi_schema_no_separate(): + client = get_app_client(separate_input_output_schemas=False) + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": {"$ref": "#/components/schemas/Item"}, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/items-list/": { + "post": { + "summary": "Create Item List", + "operationId": "create_item_list_items_list__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "items": {"$ref": "#/components/schemas/Item"}, + "type": "array", + "title": "Item", + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + "sub": { + "anyOf": [ + {"$ref": "#/components/schemas/SubItem"}, + {"type": "null"}, + ] + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "SubItem": { + "properties": { + "subname": {"type": "string", "title": "Subname"}, + "sub_description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Sub Description", + }, + "tags": { + "items": {"type": "string"}, + "type": "array", + "title": "Tags", + "default": [], + }, + }, + "type": "object", + "required": ["subname"], + "title": "SubItem", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001.py b/tests/test_tutorial/test_body_updates/test_tutorial001.py index f1a46210aadea..58587885efdb0 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001.py @@ -53,7 +53,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -87,7 +87,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -116,7 +116,7 @@ def test_openapi_schema(client: TestClient): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -126,7 +126,7 @@ def test_openapi_schema(client: TestClient): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "type": "object", "properties": { @@ -151,7 +151,7 @@ def test_openapi_schema(client: TestClient): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "type": "object", "required": ["name", "description", "price", "tax", "tags"], diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py index ab696e4c8dfec..d8a62502f0a84 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001_py310.py @@ -56,7 +56,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -90,7 +90,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -119,7 +119,7 @@ def test_openapi_schema(client: TestClient): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -129,7 +129,7 @@ def test_openapi_schema(client: TestClient): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "type": "object", "properties": { @@ -154,7 +154,7 @@ def test_openapi_schema(client: TestClient): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "type": "object", "required": ["name", "description", "price", "tax", "tags"], diff --git a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py index 2ee6a5cb4c695..c604df6ecb7e9 100644 --- a/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py +++ b/tests/test_tutorial/test_body_updates/test_tutorial001_py39.py @@ -56,7 +56,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -90,7 +90,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -119,7 +119,7 @@ def test_openapi_schema(client: TestClient): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -129,7 +129,7 @@ def test_openapi_schema(client: TestClient): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "type": "object", "properties": { @@ -154,7 +154,7 @@ def test_openapi_schema(client: TestClient): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "type": "object", "required": ["name", "description", "price", "tax", "tags"], diff --git a/docs_src/sql_databases_peewee/__init__.py b/tests/test_tutorial/test_configure_swagger_ui/__init__.py similarity index 100% rename from docs_src/sql_databases_peewee/__init__.py rename to tests/test_tutorial/test_configure_swagger_ui/__init__.py diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial003.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py similarity index 95% rename from tests/test_tutorial/test_extending_openapi/test_tutorial003.py rename to tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py index 0184dd9f8384b..72db54bd20271 100644 --- a/tests/test_tutorial/test_extending_openapi/test_tutorial003.py +++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial001.py @@ -1,6 +1,6 @@ from fastapi.testclient import TestClient -from docs_src.extending_openapi.tutorial003 import app +from docs_src.configure_swagger_ui.tutorial001 import app client = TestClient(app) diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial004.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py similarity index 96% rename from tests/test_tutorial/test_extending_openapi/test_tutorial004.py rename to tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py index 4f7615126a0cd..1669011885043 100644 --- a/tests/test_tutorial/test_extending_openapi/test_tutorial004.py +++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial002.py @@ -1,6 +1,6 @@ from fastapi.testclient import TestClient -from docs_src.extending_openapi.tutorial004 import app +from docs_src.configure_swagger_ui.tutorial002 import app client = TestClient(app) diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial005.py b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py similarity index 96% rename from tests/test_tutorial/test_extending_openapi/test_tutorial005.py rename to tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py index 24aeb93db32ec..187e89ace0dca 100644 --- a/tests/test_tutorial/test_extending_openapi/test_tutorial005.py +++ b/tests/test_tutorial/test_configure_swagger_ui/test_tutorial003.py @@ -1,6 +1,6 @@ from fastapi.testclient import TestClient -from docs_src.extending_openapi.tutorial005 import app +from docs_src.configure_swagger_ui.tutorial003 import app client = TestClient(app) diff --git a/tests/test_tutorial/test_sql_databases_peewee/__init__.py b/tests/test_tutorial/test_custom_docs_ui/__init__.py similarity index 100% rename from tests/test_tutorial/test_sql_databases_peewee/__init__.py rename to tests/test_tutorial/test_custom_docs_ui/__init__.py diff --git a/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py b/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py new file mode 100644 index 0000000000000..aff070d747310 --- /dev/null +++ b/tests/test_tutorial/test_custom_docs_ui/test_tutorial001.py @@ -0,0 +1,42 @@ +import os +from pathlib import Path + +import pytest +from fastapi.testclient import TestClient + + +@pytest.fixture(scope="module") +def client(): + static_dir: Path = Path(os.getcwd()) / "static" + print(static_dir) + static_dir.mkdir(exist_ok=True) + from docs_src.custom_docs_ui.tutorial001 import app + + with TestClient(app) as client: + yield client + static_dir.rmdir() + + +def test_swagger_ui_html(client: TestClient): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert "https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js" in response.text + assert "https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" in response.text + + +def test_swagger_ui_oauth2_redirect_html(client: TestClient): + response = client.get("/docs/oauth2-redirect") + assert response.status_code == 200, response.text + assert "window.opener.swaggerUIRedirectOauth2" in response.text + + +def test_redoc_html(client: TestClient): + response = client.get("/redoc") + assert response.status_code == 200, response.text + assert "https://unpkg.com/redoc@next/bundles/redoc.standalone.js" in response.text + + +def test_api(client: TestClient): + response = client.get("/users/john") + assert response.status_code == 200, response.text + assert response.json()["message"] == "Hello john" diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial002.py b/tests/test_tutorial/test_custom_docs_ui/test_tutorial002.py similarity index 95% rename from tests/test_tutorial/test_extending_openapi/test_tutorial002.py rename to tests/test_tutorial/test_custom_docs_ui/test_tutorial002.py index 654db2e4c8ab8..712618807c054 100644 --- a/tests/test_tutorial/test_extending_openapi/test_tutorial002.py +++ b/tests/test_tutorial/test_custom_docs_ui/test_tutorial002.py @@ -10,7 +10,7 @@ def client(): static_dir: Path = Path(os.getcwd()) / "static" print(static_dir) static_dir.mkdir(exist_ok=True) - from docs_src.extending_openapi.tutorial002 import app + from docs_src.custom_docs_ui.tutorial002 import app with TestClient(app) as client: yield client diff --git a/tests/test_tutorial/test_dataclasses/test_tutorial003.py b/tests/test_tutorial/test_dataclasses/test_tutorial003.py index 2e5809914f850..f2ca858235faf 100644 --- a/tests/test_tutorial/test_dataclasses/test_tutorial003.py +++ b/tests/test_tutorial/test_dataclasses/test_tutorial003.py @@ -79,7 +79,9 @@ def test_openapi_schema(): "schema": { "title": "Items", "type": "array", - "items": {"$ref": "#/components/schemas/ItemInput"}, + "items": { + "$ref": "#/components/schemas/Item-Input" + }, } } }, @@ -141,7 +143,7 @@ def test_openapi_schema(): "items": { "title": "Items", "type": "array", - "items": {"$ref": "#/components/schemas/ItemOutput"}, + "items": {"$ref": "#/components/schemas/Item-Output"}, }, }, }, @@ -156,7 +158,7 @@ def test_openapi_schema(): } }, }, - "ItemInput": { + "Item-Input": { "title": "Item", "required": ["name"], "type": "object", @@ -168,7 +170,7 @@ def test_openapi_schema(): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "required": ["name", "description"], "type": "object", diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py index 3ffc0bca7ce62..c5b2fb670b213 100644 --- a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py +++ b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial004.py @@ -35,7 +35,7 @@ def test_openapi_schema(): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -57,7 +57,7 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -67,7 +67,7 @@ def test_openapi_schema(): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "required": ["name", "price"], "type": "object", @@ -91,7 +91,7 @@ def test_openapi_schema(): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "required": ["name", "description", "price", "tax", "tags"], "type": "object", diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py index ff98295a60a44..458923b5a08f8 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005.py @@ -35,7 +35,7 @@ def test_openapi_schema(): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -57,7 +57,7 @@ def test_openapi_schema(): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -67,7 +67,7 @@ def test_openapi_schema(): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "required": ["name", "price"], "type": "object", @@ -91,7 +91,7 @@ def test_openapi_schema(): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "required": ["name", "description", "price", "tax", "tags"], "type": "object", diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py index ad1c09eaec508..1fcc5c4e0cba0 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py310.py @@ -42,7 +42,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -64,7 +64,7 @@ def test_openapi_schema(client: TestClient): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -74,7 +74,7 @@ def test_openapi_schema(client: TestClient): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "required": ["name", "price"], "type": "object", @@ -98,7 +98,7 @@ def test_openapi_schema(client: TestClient): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "required": ["name", "description", "price", "tax", "tags"], "type": "object", diff --git a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py index 045d1d402c949..470fe032b1624 100644 --- a/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py +++ b/tests/test_tutorial/test_path_operation_configurations/test_tutorial005_py39.py @@ -42,7 +42,7 @@ def test_openapi_schema(client: TestClient): "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ItemOutput" + "$ref": "#/components/schemas/Item-Output" } } }, @@ -64,7 +64,7 @@ def test_openapi_schema(client: TestClient): "requestBody": { "content": { "application/json": { - "schema": {"$ref": "#/components/schemas/ItemInput"} + "schema": {"$ref": "#/components/schemas/Item-Input"} } }, "required": True, @@ -74,7 +74,7 @@ def test_openapi_schema(client: TestClient): }, "components": { "schemas": { - "ItemInput": { + "Item-Input": { "title": "Item", "required": ["name", "price"], "type": "object", @@ -98,7 +98,7 @@ def test_openapi_schema(client: TestClient): }, }, }, - "ItemOutput": { + "Item-Output": { "title": "Item", "required": ["name", "description", "price", "tax", "tags"], "type": "object", diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py new file mode 100644 index 0000000000000..94a40ed5a3a9a --- /dev/null +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial005.py @@ -0,0 +1,166 @@ +import pytest +from dirty_equals import IsDict +from fastapi.testclient import TestClient + + +@pytest.fixture(name="client") +def get_client(): + from docs_src.schema_extra_example.tutorial005 import app + + client = TestClient(app) + return client + + +def test_post_body_example(client: TestClient): + response = client.put( + "/items/5", + json={ + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + ) + assert response.status_code == 200 + + +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/{item_id}": { + "put": { + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "integer"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": IsDict({"$ref": "#/components/schemas/Item"}) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "allOf": [ + {"$ref": "#/components/schemas/Item"} + ], + "title": "Item", + } + ), + "examples": { + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": {"name": "Bar", "price": "35.4"}, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "Item": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": IsDict( + { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Description", "type": "string"} + ), + "price": {"title": "Price", "type": "number"}, + "tax": IsDict( + { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Tax", "type": "number"} + ), + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an.py new file mode 100644 index 0000000000000..da92f98f6ae7f --- /dev/null +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an.py @@ -0,0 +1,166 @@ +import pytest +from dirty_equals import IsDict +from fastapi.testclient import TestClient + + +@pytest.fixture(name="client") +def get_client(): + from docs_src.schema_extra_example.tutorial005_an import app + + client = TestClient(app) + return client + + +def test_post_body_example(client: TestClient): + response = client.put( + "/items/5", + json={ + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + ) + assert response.status_code == 200 + + +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/{item_id}": { + "put": { + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "integer"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": IsDict({"$ref": "#/components/schemas/Item"}) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "allOf": [ + {"$ref": "#/components/schemas/Item"} + ], + "title": "Item", + } + ), + "examples": { + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": {"name": "Bar", "price": "35.4"}, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "Item": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": IsDict( + { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Description", "type": "string"} + ), + "price": {"title": "Price", "type": "number"}, + "tax": IsDict( + { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Tax", "type": "number"} + ), + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an_py310.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an_py310.py new file mode 100644 index 0000000000000..9109cb14efea0 --- /dev/null +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an_py310.py @@ -0,0 +1,170 @@ +import pytest +from dirty_equals import IsDict +from fastapi.testclient import TestClient + +from ...utils import needs_py310 + + +@pytest.fixture(name="client") +def get_client(): + from docs_src.schema_extra_example.tutorial005_an_py310 import app + + client = TestClient(app) + return client + + +@needs_py310 +def test_post_body_example(client: TestClient): + response = client.put( + "/items/5", + json={ + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + ) + assert response.status_code == 200 + + +@needs_py310 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/{item_id}": { + "put": { + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "integer"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": IsDict({"$ref": "#/components/schemas/Item"}) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "allOf": [ + {"$ref": "#/components/schemas/Item"} + ], + "title": "Item", + } + ), + "examples": { + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": {"name": "Bar", "price": "35.4"}, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "Item": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": IsDict( + { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Description", "type": "string"} + ), + "price": {"title": "Price", "type": "number"}, + "tax": IsDict( + { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Tax", "type": "number"} + ), + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an_py39.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an_py39.py new file mode 100644 index 0000000000000..fd4ec0575e60c --- /dev/null +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_an_py39.py @@ -0,0 +1,170 @@ +import pytest +from dirty_equals import IsDict +from fastapi.testclient import TestClient + +from ...utils import needs_py39 + + +@pytest.fixture(name="client") +def get_client(): + from docs_src.schema_extra_example.tutorial005_an_py39 import app + + client = TestClient(app) + return client + + +@needs_py39 +def test_post_body_example(client: TestClient): + response = client.put( + "/items/5", + json={ + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + ) + assert response.status_code == 200 + + +@needs_py39 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/{item_id}": { + "put": { + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "integer"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": IsDict({"$ref": "#/components/schemas/Item"}) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "allOf": [ + {"$ref": "#/components/schemas/Item"} + ], + "title": "Item", + } + ), + "examples": { + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": {"name": "Bar", "price": "35.4"}, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "Item": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": IsDict( + { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Description", "type": "string"} + ), + "price": {"title": "Price", "type": "number"}, + "tax": IsDict( + { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Tax", "type": "number"} + ), + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial005_py310.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_py310.py new file mode 100644 index 0000000000000..05df534223bf9 --- /dev/null +++ b/tests/test_tutorial/test_schema_extra_example/test_tutorial005_py310.py @@ -0,0 +1,170 @@ +import pytest +from dirty_equals import IsDict +from fastapi.testclient import TestClient + +from ...utils import needs_py310 + + +@pytest.fixture(name="client") +def get_client(): + from docs_src.schema_extra_example.tutorial005_py310 import app + + client = TestClient(app) + return client + + +@needs_py310 +def test_post_body_example(client: TestClient): + response = client.put( + "/items/5", + json={ + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + ) + assert response.status_code == 200 + + +@needs_py310 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/items/{item_id}": { + "put": { + "summary": "Update Item", + "operationId": "update_item_items__item_id__put", + "parameters": [ + { + "required": True, + "schema": {"title": "Item Id", "type": "integer"}, + "name": "item_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": IsDict({"$ref": "#/components/schemas/Item"}) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "allOf": [ + {"$ref": "#/components/schemas/Item"} + ], + "title": "Item", + } + ), + "examples": { + "normal": { + "summary": "A normal example", + "description": "A **normal** item works correctly.", + "value": { + "name": "Foo", + "description": "A very nice Item", + "price": 35.4, + "tax": 3.2, + }, + }, + "converted": { + "summary": "An example with converted data", + "description": "FastAPI can convert price `strings` to actual `numbers` automatically", + "value": {"name": "Bar", "price": "35.4"}, + }, + "invalid": { + "summary": "Invalid data is rejected with an error", + "value": { + "name": "Baz", + "price": "thirty five point four", + }, + }, + }, + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": {"$ref": "#/components/schemas/ValidationError"}, + } + }, + }, + "Item": { + "title": "Item", + "required": ["name", "price"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "description": IsDict( + { + "title": "Description", + "anyOf": [{"type": "string"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Description", "type": "string"} + ), + "price": {"title": "Price", "type": "number"}, + "tax": IsDict( + { + "title": "Tax", + "anyOf": [{"type": "number"}, {"type": "null"}], + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + {"title": "Tax", "type": "number"} + ), + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_separate_openapi_schemas/__init__.py b/tests/test_tutorial/test_separate_openapi_schemas/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py new file mode 100644 index 0000000000000..8079c11343c1b --- /dev/null +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001.py @@ -0,0 +1,147 @@ +import pytest +from fastapi.testclient import TestClient + +from ...utils import needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client() -> TestClient: + from docs_src.separate_openapi_schemas.tutorial001 import app + + client = TestClient(app) + return client + + +def test_create_item(client: TestClient) -> None: + response = client.post("/items/", json={"name": "Foo"}) + assert response.status_code == 200, response.text + assert response.json() == {"name": "Foo", "description": None} + + +def test_read_items(client: TestClient) -> None: + response = client.get("/items/") + assert response.status_code == 200, response.text + assert response.json() == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + {"name": "Plumbus", "description": None}, + ] + + +@needs_pydanticv2 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": { + "$ref": "#/components/schemas/Item-Output" + }, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item-Input"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item-Input": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "Item-Output": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name", "description"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001_py310.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001_py310.py new file mode 100644 index 0000000000000..4fa98ccbefbf8 --- /dev/null +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001_py310.py @@ -0,0 +1,150 @@ +import pytest +from fastapi.testclient import TestClient + +from ...utils import needs_py310, needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client() -> TestClient: + from docs_src.separate_openapi_schemas.tutorial001_py310 import app + + client = TestClient(app) + return client + + +@needs_py310 +def test_create_item(client: TestClient) -> None: + response = client.post("/items/", json={"name": "Foo"}) + assert response.status_code == 200, response.text + assert response.json() == {"name": "Foo", "description": None} + + +@needs_py310 +def test_read_items(client: TestClient) -> None: + response = client.get("/items/") + assert response.status_code == 200, response.text + assert response.json() == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + {"name": "Plumbus", "description": None}, + ] + + +@needs_py310 +@needs_pydanticv2 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": { + "$ref": "#/components/schemas/Item-Output" + }, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item-Input"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item-Input": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "Item-Output": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name", "description"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001_py39.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001_py39.py new file mode 100644 index 0000000000000..ad36582ed5e01 --- /dev/null +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial001_py39.py @@ -0,0 +1,150 @@ +import pytest +from fastapi.testclient import TestClient + +from ...utils import needs_py39, needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client() -> TestClient: + from docs_src.separate_openapi_schemas.tutorial001_py39 import app + + client = TestClient(app) + return client + + +@needs_py39 +def test_create_item(client: TestClient) -> None: + response = client.post("/items/", json={"name": "Foo"}) + assert response.status_code == 200, response.text + assert response.json() == {"name": "Foo", "description": None} + + +@needs_py39 +def test_read_items(client: TestClient) -> None: + response = client.get("/items/") + assert response.status_code == 200, response.text + assert response.json() == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + {"name": "Plumbus", "description": None}, + ] + + +@needs_py39 +@needs_pydanticv2 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": { + "$ref": "#/components/schemas/Item-Output" + }, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item-Input"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item-Input": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "Item-Output": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name", "description"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py new file mode 100644 index 0000000000000..d2cf7945b71d7 --- /dev/null +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002.py @@ -0,0 +1,133 @@ +import pytest +from fastapi.testclient import TestClient + +from ...utils import needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client() -> TestClient: + from docs_src.separate_openapi_schemas.tutorial002 import app + + client = TestClient(app) + return client + + +def test_create_item(client: TestClient) -> None: + response = client.post("/items/", json={"name": "Foo"}) + assert response.status_code == 200, response.text + assert response.json() == {"name": "Foo", "description": None} + + +def test_read_items(client: TestClient) -> None: + response = client.get("/items/") + assert response.status_code == 200, response.text + assert response.json() == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + {"name": "Plumbus", "description": None}, + ] + + +@needs_pydanticv2 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": {"$ref": "#/components/schemas/Item"}, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002_py310.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002_py310.py new file mode 100644 index 0000000000000..89c9ce977043e --- /dev/null +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002_py310.py @@ -0,0 +1,136 @@ +import pytest +from fastapi.testclient import TestClient + +from ...utils import needs_py310, needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client() -> TestClient: + from docs_src.separate_openapi_schemas.tutorial002_py310 import app + + client = TestClient(app) + return client + + +@needs_py310 +def test_create_item(client: TestClient) -> None: + response = client.post("/items/", json={"name": "Foo"}) + assert response.status_code == 200, response.text + assert response.json() == {"name": "Foo", "description": None} + + +@needs_py310 +def test_read_items(client: TestClient) -> None: + response = client.get("/items/") + assert response.status_code == 200, response.text + assert response.json() == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + {"name": "Plumbus", "description": None}, + ] + + +@needs_py310 +@needs_pydanticv2 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": {"$ref": "#/components/schemas/Item"}, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002_py39.py b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002_py39.py new file mode 100644 index 0000000000000..6ac3d8f7912da --- /dev/null +++ b/tests/test_tutorial/test_separate_openapi_schemas/test_tutorial002_py39.py @@ -0,0 +1,136 @@ +import pytest +from fastapi.testclient import TestClient + +from ...utils import needs_py39, needs_pydanticv2 + + +@pytest.fixture(name="client") +def get_client() -> TestClient: + from docs_src.separate_openapi_schemas.tutorial002_py39 import app + + client = TestClient(app) + return client + + +@needs_py39 +def test_create_item(client: TestClient) -> None: + response = client.post("/items/", json={"name": "Foo"}) + assert response.status_code == 200, response.text + assert response.json() == {"name": "Foo", "description": None} + + +@needs_py39 +def test_read_items(client: TestClient) -> None: + response = client.get("/items/") + assert response.status_code == 200, response.text + assert response.json() == [ + { + "name": "Portal Gun", + "description": "Device to travel through the multi-rick-verse", + }, + {"name": "Plumbus", "description": None}, + ] + + +@needs_py39 +@needs_pydanticv2 +def test_openapi_schema(client: TestClient) -> None: + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "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": { + "items": {"$ref": "#/components/schemas/Item"}, + "type": "array", + "title": "Response Read Items Items Get", + } + } + }, + } + }, + }, + "post": { + "summary": "Create Item", + "operationId": "create_item_items__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Item"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": {"$ref": "#/components/schemas/ValidationError"}, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Item": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "description": { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Description", + }, + }, + "type": "object", + "required": ["name"], + "title": "Item", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } diff --git a/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py b/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py deleted file mode 100644 index 4350567d1ae99..0000000000000 --- a/tests/test_tutorial/test_sql_databases_peewee/test_sql_databases_peewee.py +++ /dev/null @@ -1,454 +0,0 @@ -import time -from pathlib import Path -from unittest.mock import MagicMock - -import pytest -from fastapi.testclient import TestClient - -from ...utils import needs_pydanticv1 - - -@pytest.fixture(scope="module") -def client(): - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases_peewee.sql_app.main import app - - test_db = Path("./test.db") - with TestClient(app) as c: - yield c - test_db.unlink() - - -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -@needs_pydanticv1 -def test_inexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -time.sleep = MagicMock() - - -@needs_pydanticv1 -def test_get_slowusers(client): - response = client.get("/slowusers/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -@needs_pydanticv1 -def test_openapi_schema(client): - response = client.get("/openapi.json") - assert response.status_code == 200, response.text - assert response.json() == { - "openapi": "3.1.0", - "info": {"title": "FastAPI", "version": "0.1.0"}, - "paths": { - "/users/": { - "get": { - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - }, - "post": { - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - }, - }, - "/users/{user_id}": { - "get": { - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - }, - "/users/{user_id}/items/": { - "post": { - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - "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" - } - } - }, - }, - }, - } - }, - "/items/": { - "get": { - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - }, - "/slowusers/": { - "get": { - "summary": "Read Slow Users", - "operationId": "read_slow_users_slowusers__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Slow Users Slowusers Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - } - }, - }, - "components": { - "schemas": { - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": {"title": "Description", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": {"title": "Description", "type": "string"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "type": "string"}, - }, - }, - "ValidationError": { - "title": "ValidationError", - "required": ["loc", "msg", "type"], - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": { - "anyOf": [{"type": "string"}, {"type": "integer"}] - }, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - }, - } - }, - }