Skip to content

Commit b16d8eb

Browse files
authored
Add the ability to customize the title and description of the api version parameter (#274)
Fix #272
1 parent a831cf5 commit b16d8eb

10 files changed

+52
-16
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ Please follow [the Keep a Changelog standard](https://keepachangelog.com/en/1.0.
55

66
## [Unreleased]
77

8+
## [5.3.0]
9+
10+
### Added
11+
12+
* `api_version_title` and `api_version_description` arguments to `Cadwyn` to allow customizing the API version parameter in the OpenAPI schema
13+
814
## [5.2.2]
915

1016
### Fixed

cadwyn/applications.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from datetime import date
55
from logging import getLogger
66
from pathlib import Path
7-
from typing import TYPE_CHECKING, Annotated, Any, Union, cast
7+
from typing import TYPE_CHECKING, Annotated, Any, Optional, Union, cast
88

99
import fastapi
1010
from fastapi import APIRouter, FastAPI, HTTPException, routing
@@ -71,6 +71,8 @@ def __init__(
7171
api_version_format: APIVersionFormat = "date",
7272
api_version_parameter_name: str = "x-api-version",
7373
api_version_default_value: Union[str, None, Callable[[Request], Awaitable[str]]] = None,
74+
api_version_title: Optional[str] = None,
75+
api_version_description: Optional[str] = None,
7476
versioning_middleware_class: type[VersionPickingMiddleware] = VersionPickingMiddleware,
7577
changelog_url: Union[str, None] = "/changelog",
7678
include_changelog_url_in_schema: bool = True,
@@ -207,6 +209,8 @@ def __init__(
207209
self.api_version_format = api_version_format
208210
self.api_version_parameter_name = api_version_parameter_name
209211
self.api_version_pythonic_parameter_name = api_version_parameter_name.replace("-", "_")
212+
self.api_version_title = api_version_title
213+
self.api_version_description = api_version_description
210214
if api_version_location == "custom_header":
211215
self._api_version_manager = HeaderVersionManager(api_version_parameter_name=api_version_parameter_name)
212216
self._api_version_fastapi_depends_class = fastapi.Header
@@ -465,6 +469,8 @@ def _add_versioned_routers(
465469
default_value=version,
466470
fastapi_depends_class=self._api_version_fastapi_depends_class,
467471
validation_data_type=self.api_version_validation_data_type,
472+
title=self.api_version_title,
473+
description=self.api_version_description,
468474
)
469475
)
470476
],

cadwyn/middleware.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import re
66
from collections.abc import Awaitable, Callable
77
from contextvars import ContextVar
8-
from typing import Annotated, Any, Literal, Protocol, Union
8+
from typing import Annotated, Any, Literal, Optional, Protocol, Union
99

1010
import fastapi
1111
from fastapi import Request
@@ -58,6 +58,8 @@ def _generate_api_version_dependency(
5858
default_value: str,
5959
fastapi_depends_class: Callable[..., Any],
6060
validation_data_type: Any,
61+
title: Optional[str] = None,
62+
description: Optional[str] = None,
6163
):
6264
def api_version_dependency(**kwargs: Any):
6365
# TODO: What do I return?
@@ -69,7 +71,12 @@ def api_version_dependency(**kwargs: Any):
6971
api_version_pythonic_parameter_name,
7072
inspect.Parameter.KEYWORD_ONLY,
7173
annotation=Annotated[
72-
validation_data_type, fastapi_depends_class(openapi_examples={"default": {"value": default_value}})
74+
validation_data_type,
75+
fastapi_depends_class(
76+
openapi_examples={"default": {"value": default_value}},
77+
title=title,
78+
description=description,
79+
),
7380
],
7481
# Path-based parameters do not support a default value in FastAPI :(
7582
default=default_value if fastapi_depends_class != fastapi.Path else inspect.Signature.empty,

docs/concepts/where_to_put_the_version_and_how_to_format_it.md renamed to docs/concepts/api_version_parameter.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Where to put the version and how to format it
1+
# API Version Parameter
22

33
Cadwyn adds another routing layer to FastAPI by default: by version parameter. This means that before FastAPI tries to route us to the correct route, Cadwyn will first decide on which version of the route to use based on a version parameter. Feel free to look at the example app with URL path version prefixes and arbitrary strings as versions [here](../how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md).
44

@@ -91,3 +91,21 @@ If the app has two versions: 2022-01-02 and 2022-01-05, and the request date par
9191
Exact match is always preferred over partial match and a request will never be matched to the higher versioned route.
9292

9393
We implement routing like this because Cadwyn was born in a microservice architecture and it is extremely convenient to have waterfalling there. For example, imagine that you have two Cadwyn services: Payables and Receivables, each defining its own API versions. Payables service might contain 10 versions while receivables service might contain only 2 versions because it didn't need as many breaking changes. If a client requests a version that does not exist in receivables -- we will just waterfall to some earlier version, making receivables behavior consistent even if API keeps getting new versions.
94+
95+
## API Version Parameter Title and Description
96+
97+
You can pass a title and/or a description to `Cadwyn` constructor. They are equivalent to passing `title` and `description` to `fastapi.Path` or `fastapi.Header` constructors.
98+
99+
```python
100+
app = Cadwyn(
101+
...,
102+
api_version_title="My Great API version parameter",
103+
api_version_description="Description of my great API version parameter",
104+
)
105+
```
106+
107+
## API Version Context Variables
108+
109+
Cadwyn automatically converts your data to a correct version and has "version checks" when dealing with side effects as described in [the section above](./version_changes.md#version-changes-with-side-effects). It can only do so using a special [context variable](https://docs.python.org/3/library/contextvars.html) that stores the current API version.
110+
111+
You can also pass a different compatible contextvar to your `cadwyn.VersionBundle` constructor.

docs/concepts/api_version_parameter_and_context_variables.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

docs/how_to/version_with_paths_and_numbers_instead_of_headers_and_dates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Cadwyn uses version headers with ISO dates by default for versioning. However, you can use any strings instead of ISO dates and/or you can use path version prefixes instead of version headers. Here's our quickstart tutorial example but using version numbers and path prefixes:
44

55
Feel free to mix and match the API version formats and version locations as you see fit.
6-
But beware that Cadwyn does not support [version waterfalling](../concepts/where_to_put_the_version_and_how_to_format_it.md#api-version-waterfalling) for arbitrary strings as versions.
6+
But beware that Cadwyn does not support [version waterfalling](../concepts/api_version_parameter.md#api-version-waterfalling) for arbitrary strings as versions.
77

88
```python
99
{! ./docs_src/how_to/version_with_path_and_numbers_instead_of_headers_and_dates/block001.py !}

mkdocs.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@ nav:
119119
- "Endpoint migrations": "concepts/endpoint_migrations.md"
120120
- "Enum migrations": "concepts/enum_migrations.md"
121121
- "Schema migrations": "concepts/schema_migrations.md"
122-
- "API Version parameter and context variables": "concepts/api_version_parameter_and_context_variables.md"
123-
- "Where to put the version and how to format it": "concepts/where_to_put_the_version_and_how_to_format_it.md"
122+
- "API Version parameter": "concepts/api_version_parameter.md"
124123
- "Changelogs": "concepts/changelogs.md"
125124
- "Testing": concepts/testing.md
126125
- "Contributing": "home/CONTRIBUTING.md"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cadwyn"
3-
version = "5.2.2"
3+
version = "5.3.0"
44
description = "Production-ready community-driven modern Stripe-like API versioning in FastAPI"
55
authors = [{ name = "Stanislav Zmiev", email = "zmievsa@gmail.com" }]
66
license = "MIT"

tests/tutorial/main.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,14 @@ async def get_user_addresses(user_id: uuid.UUID):
123123
return {"data": database_parody[f"addr_{user_id}"]}
124124

125125

126-
app = Cadwyn(versions=version_bundle, title="My amazing API")
126+
app = Cadwyn(
127+
versions=version_bundle,
128+
title="My amazing API",
129+
api_version_title="My Great API version parameter",
130+
api_version_description="Description of my great API version parameter",
131+
)
127132
app.generate_and_include_versioned_routers(router)
128133

129134

130135
if __name__ == "__main__":
131-
uvicorn.run(app)
136+
uvicorn.run(app, port=8011)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)