Skip to content

Commit 76165f6

Browse files
committed
Added versioning docs
1 parent c3b5ab7 commit 76165f6

9 files changed

+236
-9
lines changed

docs/basics/versioning.md

Lines changed: 224 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,224 @@
1-
## Coming Soon
1+
Versioning allows for the existence of multiple versions of controllers or individual routes within the same application,
2+
which can be useful when making changes that may break previous versions.
3+
This allows developers to support older versions of the application while still making necessary updates.
4+
5+
There are 4 types of versioning that are supported:
6+
7+
- [`URL Versioning`](#url-versioning): The version will be passed within the **URL** of the request
8+
- [`Header Versioning`](#header-versioning): A custom request **header** will specify the version
9+
- [`Query Versioning`](#query-versioning): A custom request **query** will specify the version
10+
- [`Host Versioning`](#host-versioning): The version will be part of the request **client host**
11+
12+
## **URL Versioning**
13+
This scheme requires the client to specify the version as part of the URL path.
14+
```
15+
GET /v1/receipes/ HTTP/1.1
16+
Host: example.com
17+
Accept: application/json
18+
```
19+
20+
To enable **URL Versioning** for your application, do the following:
21+
```python
22+
# project_name/server.py
23+
import os
24+
from ellar.constants import ELLAR_CONFIG_MODULE
25+
from ellar.core.factory import AppFactory
26+
from ellar.core.versioning import VersioningSchemes
27+
from .root_module import ApplicationModule
28+
29+
application = AppFactory.create_from_app_module(
30+
ApplicationModule,
31+
config_module=os.environ.get(
32+
ELLAR_CONFIG_MODULE, "dialerai.config:DevelopmentConfig"
33+
),
34+
global_guards=[]
35+
)
36+
application.enable_versioning(VersioningSchemes.URL, version_parameter='v', default_version=None)
37+
```
38+
The URL path will be parsed with the provided `version_parameter`, `v`, to determine specified version.
39+
For example, `https://example.com/v1/route`, will resolve to `version='1'` and `https://example.com/v3/route`, will resolve to `version='3'`.
40+
41+
If version is not specified in the URL, the `default_version` will be used. Which in this case is `None`.
42+
43+
## **Header Versioning**
44+
This scheme requires the client to specify the version as part of the media type in the `Accept` header.
45+
The version is included as a media type parameter, that supplements the main media type.
46+
47+
Here's an example HTTP request using accept header versioning style.
48+
```
49+
GET /receipes/ HTTP/1.1
50+
Host: example.com
51+
Accept: application/json; version=1
52+
```
53+
54+
To enable **Header Versioning** for your application, do the following:
55+
```python
56+
# project_name/server.py
57+
import os
58+
from ellar.constants import ELLAR_CONFIG_MODULE
59+
from ellar.core.factory import AppFactory
60+
from ellar.core.versioning import VersioningSchemes
61+
from .root_module import ApplicationModule
62+
63+
application = AppFactory.create_from_app_module(
64+
ApplicationModule,
65+
config_module=os.environ.get(
66+
ELLAR_CONFIG_MODULE, "dialerai.config:DevelopmentConfig"
67+
),
68+
global_guards=[]
69+
)
70+
application.enable_versioning(
71+
VersioningSchemes.HEADER,
72+
header_parameter='accept',
73+
version_parameter='version',
74+
default_version=None
75+
)
76+
```
77+
During request handling, request header `accept` value will be parsed to read the version value. A header `accept: application/json; version=2` will resolve to `version='2'`
78+
79+
#### **Using Custom Header**
80+
We can also use a custom header asides `accept`. for example:
81+
```
82+
GET /receipes/ HTTP/1.1
83+
Host: example.com
84+
X-Custom-Header: version=2
85+
```
86+
87+
And then we enable it with the code below:
88+
```python
89+
# project_name/server.py
90+
...
91+
application.enable_versioning(
92+
VersioningSchemes.HEADER,
93+
header_parameter='x-custom-header',
94+
version_parameter='version_header',
95+
default_version=None
96+
)
97+
```
98+
The header property, `x-custom-header`, will be the name of the header that will contain the version of the request.
99+
And the value follow the format `[version_parameter]=version-number;`, for example: `headers={'x-custom-header': 'version_header=3'}` will resolve to `version='3'`.
100+
101+
## **Query Versioning**
102+
This scheme is a simple style that includes the version as a query parameter in the URL. For example:
103+
```
104+
GET /receipes?version=2 HTTP/1.1
105+
Host: example.com
106+
Accept: application/json
107+
```
108+
109+
To enable **Query Versioning** for your application, do the following:
110+
```python
111+
# project_name/server.py
112+
import os
113+
from ellar.constants import ELLAR_CONFIG_MODULE
114+
from ellar.core.factory import AppFactory
115+
from ellar.core.versioning import VersioningSchemes
116+
from .root_module import ApplicationModule
117+
118+
application = AppFactory.create_from_app_module(
119+
ApplicationModule,
120+
config_module=os.environ.get(
121+
ELLAR_CONFIG_MODULE, "dialerai.config:DevelopmentConfig"
122+
),
123+
global_guards=[]
124+
)
125+
application.enable_versioning(
126+
VersioningSchemes.QUERY,
127+
version_parameter='version',
128+
default_version=None
129+
)
130+
```
131+
132+
## **Host Versioning**
133+
The hostname versioning scheme requires the client to specify the requested version as part of the hostname in the URL.
134+
135+
For example the following is an HTTP request to the `http://v1.example.com/receipes/` URL:
136+
```
137+
GET /receipes/ HTTP/1.1
138+
Host: v1.example.com
139+
Accept: application/json
140+
```
141+
142+
To enable **Host Versioning** for your application, do the following:
143+
```python
144+
# project_name/server.py
145+
import os
146+
from ellar.constants import ELLAR_CONFIG_MODULE
147+
from ellar.core.factory import AppFactory
148+
from ellar.core.versioning import VersioningSchemes
149+
from .root_module import ApplicationModule
150+
151+
application = AppFactory.create_from_app_module(
152+
ApplicationModule,
153+
config_module=os.environ.get(
154+
ELLAR_CONFIG_MODULE, "dialerai.config:DevelopmentConfig"
155+
),
156+
global_guards=[]
157+
)
158+
application.enable_versioning(
159+
VersioningSchemes.HOST,
160+
version_parameter='v',
161+
default_version=None
162+
)
163+
```
164+
By default, this implementation expects the hostname to match this simple regular expression:
165+
```regexp
166+
^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$
167+
```
168+
169+
Note that the first group is enclosed in brackets, indicating that this is the matched portion of the hostname.
170+
171+
The `HostNameVersioning` scheme can be awkward to use in debug mode as you will typically be accessing a raw IP address such as 127.0.0.1.
172+
There are various online tutorials on how to [access localhost with a custom subdomain](https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains) which you may find helpful in this case.
173+
174+
Hostname based versioning can be particularly useful if you have requirements to route incoming requests to different servers based on the version, as you can configure different DNS records for different API versions.
175+
176+
## **Controller Versions**
177+
A version can be applied to a controller by using `Version` decorator from `ellar.common` package.
178+
179+
To add a version to a controller do the following:
180+
```python
181+
from ellar.common import Controller, Version
182+
183+
@Controller('/example')
184+
@Version('1')
185+
class ExampleControllerV1:
186+
pass
187+
188+
```
189+
## **Route Versions**
190+
A version can be applied to an individual route. This version will override any other version that would effect the route, such as the Controller Version.
191+
192+
To add a version to an individual route do the following:
193+
194+
```python
195+
from ellar.common import Controller, Version, get
196+
197+
@Controller('/example')
198+
class ExampleController:
199+
@Version('1')
200+
@get('/items')
201+
async def get_items_v1(self):
202+
return 'This action returns all items for version 1'
203+
204+
@get('/items')
205+
@Version('2')
206+
async def get_items_v2(self):
207+
return 'This action returns all items for version 2'
208+
209+
```
210+
## **Multiple Versions**
211+
Multiple versions can be applied to a controller or route. To use multiple versions, you would set the version to be an Array.
212+
213+
To add multiple versions do the following:
214+
```python
215+
from ellar.common import Controller, Version, get
216+
217+
@Controller('/example')
218+
@Version('1', '2')
219+
class ExampleControllerV1AndV2:
220+
@get('/items')
221+
async def get_items(self):
222+
return 'This action returns all items for version 1 & 2'
223+
224+
```

tests/test_application/test_application_functions.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
from ellar.core.services import CoreServiceRegistration
2222
from ellar.core.staticfiles import StaticFiles
2323
from ellar.core.templating import Environment
24-
from ellar.core.versioning import VERSIONING, DefaultAPIVersioning, UrlPathAPIVersioning
24+
from ellar.core.versioning import (
25+
DefaultAPIVersioning,
26+
UrlPathAPIVersioning,
27+
VersioningSchemes as VERSIONING,
28+
)
2529
from ellar.di import EllarInjector
2630
from ellar.helper.importer import get_class_import
2731
from ellar.openapi import OpenAPIDocumentModule

tests/test_versioning/test_default_versioning.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33
from ellar.core import TestClientFactory
4-
from ellar.core.versioning import VERSIONING
4+
from ellar.core.versioning import VersioningSchemes as VERSIONING
55

66
from .operations import (
77
ControllerIndividualVersioning,

tests/test_versioning/test_default_versioning_for_controllers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33
from ellar.core import TestClientFactory
4-
from ellar.core.versioning import VERSIONING
4+
from ellar.core.versioning import VersioningSchemes as VERSIONING
55

66
from .operations import (
77
ControllerIndividualVersioning,

tests/test_versioning/test_header_versioning.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ellar.constants import NOT_SET
44
from ellar.core import TestClientFactory
5-
from ellar.core.versioning import VERSIONING
5+
from ellar.core.versioning import VersioningSchemes as VERSIONING
66

77
from .operations import mr
88

tests/test_versioning/test_header_versioning_controller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ellar.constants import NOT_SET
44
from ellar.core import TestClientFactory
5-
from ellar.core.versioning import VERSIONING
5+
from ellar.core.versioning import VersioningSchemes as VERSIONING
66

77
from .operations import (
88
ControllerIndividualVersioning,

tests/test_versioning/test_host_versioning.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ellar.constants import NOT_SET
44
from ellar.core import TestClientFactory
5-
from ellar.core.versioning import VERSIONING
5+
from ellar.core.versioning import VersioningSchemes as VERSIONING
66

77
from .operations import mr
88

tests/test_versioning/test_query_versioning.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ellar.constants import NOT_SET
44
from ellar.core import TestClientFactory
5-
from ellar.core.versioning import VERSIONING
5+
from ellar.core.versioning import VersioningSchemes as VERSIONING
66

77
from .operations import mr
88

tests/test_versioning/test_url_versioning.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ellar.constants import NOT_SET
44
from ellar.core import TestClientFactory
5-
from ellar.core.versioning import VERSIONING, UrlPathAPIVersioning
5+
from ellar.core.versioning import UrlPathAPIVersioning, VersioningSchemes as VERSIONING
66

77
from .operations import mr
88

0 commit comments

Comments
 (0)