Skip to content

Commit 30422aa

Browse files
committed
API: accept X-RTD-Hosting-Integrations-API-Version
This new header will force the API endpoint to return a specific JSON version schema without taking into consideration the any other header. The idea behind this is to allow theme authors guarrantees about the JSON scheme they are expecting; without stopping us to move forward with potential changes required to the API response when adding new features/addons/etc. Related readthedocs/addons#64
1 parent f3479bb commit 30422aa

File tree

4 files changed

+52
-4
lines changed

4 files changed

+52
-4
lines changed

readthedocs/proxito/tests/responses/v0.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"api_version": 0,
23
"comment": "THIS RESPONSE IS IN ALPHA FOR TEST PURPOSES ONLY AND IT'S GOING TO CHANGE COMPLETELY -- DO NOT USE IT!",
34
"projects": {
45
"current": {
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2+
"api_version": "1",
23
"comment": "Undefined yet. Use v0 for now"
34
}

readthedocs/proxito/tests/test_hosting.py

+12
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,15 @@ def test_get_config_unsupported_version(self):
111111
)
112112
assert r.status_code == 400
113113
assert r.json() == self._get_response_dict("v2")
114+
115+
def test_get_config_explicit_api_version(self):
116+
r = self.client.get(
117+
reverse("proxito_readthedocs_config_json"),
118+
{"url": "https://project.dev.readthedocs.io/en/latest/"},
119+
secure=True,
120+
HTTP_HOST="project.dev.readthedocs.io",
121+
HTTP_X_RTD_HOSTING_INTEGRATIONS_VERSION="0.1.0",
122+
HTTP_X_RTD_HOSTING_INTEGRATIONS_API_VERSION="1",
123+
)
124+
assert r.status_code == 200
125+
assert r.json() == self._get_response_dict("v1")

readthedocs/proxito/views/hosting.py

+38-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@
2323

2424
class ClientError(Exception):
2525
VERSION_NOT_CURRENTLY_SUPPORTED = (
26-
"The version specified in 'X-RTD-Hosting-Integrations-Version'"
27-
" is currently not supported"
26+
"The version specified in 'X-RTD-Hosting-Integrations-Version' "
27+
"or 'X-RTD-Hosting-Integrations-API-Version' "
28+
"is currently not supported"
29+
)
30+
VERSION_INVALID = (
31+
"'X-RTD-Hosting-Integrations-Version' or 'X-RTD-Hosting-Integrations-API-Version' "
32+
"header version is invalid"
2833
)
29-
VERSION_INVALID = "'X-RTD-Hosting-Integrations-Version' header version is invalid"
3034
VERSION_HEADER_MISSING = (
3135
"'X-RTD-Hosting-Integrations-Version' header attribute is required"
3236
)
@@ -44,6 +48,16 @@ class ReadTheDocsConfigJson(CDNCacheControlMixin, View):
4448
4549
url (required): absolute URL from where the request is performed
4650
(e.g. ``window.location.href``)
51+
52+
Headers:
53+
54+
X-RTD-Hosting-Integrations-Version (required): javascript client version.
55+
It's used by the endpoint to decide what's the JSON structure compatible with the client
56+
(e.g. ``1.0.2``)
57+
58+
X-RTD-Hosting-Integrations-API-Version (optional): force the endpoint to return a specific
59+
JSON structure in particular. Used by theme authors to keep compatibility with
60+
their integration (e.g. ``1``)
4761
"""
4862

4963
def get(self, request):
@@ -63,10 +77,22 @@ def get(self, request):
6377
},
6478
status=400,
6579
)
80+
81+
addons_version_explicit = request.headers.get(
82+
"X-RTD-Hosting-Integrations-API-Version"
83+
)
6684
try:
6785
addons_version = packaging.version.parse(addons_version)
6886
if addons_version.major not in ADDONS_VERSIONS_SUPPORTED:
6987
raise ClientError
88+
89+
if addons_version_explicit:
90+
addons_version_explicit = packaging.version.parse(
91+
addons_version_explicit
92+
)
93+
if addons_version_explicit.major not in ADDONS_VERSIONS_SUPPORTED:
94+
raise ClientError
95+
7096
except packaging.version.InvalidVersion:
7197
return JsonResponse(
7298
{
@@ -90,7 +116,13 @@ def get(self, request):
90116
project.get_default_version()
91117
build = version.builds.last()
92118

93-
data = AddonsResponse().get(addons_version, project, version, build, filename)
119+
data = AddonsResponse().get(
120+
addons_version_explicit or addons_version,
121+
project,
122+
version,
123+
build,
124+
filename,
125+
)
94126
return JsonResponse(data, json_dumps_params=dict(indent=4))
95127

96128

@@ -160,6 +192,7 @@ def _v0(self, project, version, build, filename):
160192
"""
161193

162194
data = {
195+
"api_version": "0",
163196
"comment": (
164197
"THIS RESPONSE IS IN ALPHA FOR TEST PURPOSES ONLY"
165198
" AND IT'S GOING TO CHANGE COMPLETELY -- DO NOT USE IT!"
@@ -270,5 +303,6 @@ def _v0(self, project, version, build, filename):
270303

271304
def _v1(self, project, version, build, filename):
272305
return {
306+
"api_version": "1",
273307
"comment": "Undefined yet. Use v0 for now",
274308
}

0 commit comments

Comments
 (0)