Skip to content
This repository was archived by the owner on Mar 15, 2018. It is now read-only.

Commit 81a0955

Browse files
author
Andy McKay
committed
version the api (bug 852233)
1 parent 864b21c commit 81a0955

File tree

5 files changed

+92
-12
lines changed

5 files changed

+92
-12
lines changed

docs/topics/api.rst

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@ This API is for Apps. There is a separate set of `APIs for Add-ons`_.
1717
api/reviewers.rst
1818
api/ratings.rst
1919

20+
Versioning
21+
==========
22+
23+
This API is versioned and we are currently moving towards version 1 of the API.
24+
The API will be versioned by the URL, so that version 1 APIs will all be at::
25+
26+
/api/v1/...
27+
28+
If you are not using the most recent version of the API then you will get
29+
a header in the response::
30+
31+
X-API-Status: Deprecated
32+
33+
The current policy for how long deprecated APIs will exist has not been
34+
defined, but it would include time for any clients to upgrade before versions
35+
are turned off.
36+
37+
We will also return the version of the API we think you are using::
38+
39+
X-API-Version: 1
40+
41+
.. note: Before v1 is released, the API was unversioned at `/api/`, because of
42+
the small number of clients using that URL, we hope all users are able to
43+
update to `/api/v1/` quickly so we can remove that unversioned URL.
44+
2045
Authentication
2146
==============
2247

@@ -135,7 +160,6 @@ Verbs
135160
This follows the order of the `django-tastypie`_ REST verbs, a PUT for an
136161
update and POST for create.
137162

138-
139163
Responses
140164
=========
141165

mkt/api/middleware.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import re
2+
13
from django.conf import settings
24
from django.middleware.transaction import TransactionMiddleware
35

@@ -51,12 +53,12 @@ def process_response(self, request, response):
5153
# hook for figuring out if a response should have the CORS headers on
5254
# it. That's because it will often error out with immediate HTTP
5355
# responses.
54-
55-
fireplacey = request.META.get('HTTP_ORIGIN') == settings.FIREPLACE_URL
56+
fireplace_url = settings.FIREPLACE_URL
57+
fireplacey = request.META.get('HTTP_ORIGIN') == fireplace_url
5658
if fireplacey or getattr(request, 'CORS', None):
5759
# If this is a request from our hosted frontend, allow cookies.
5860
if fireplacey:
59-
response['Access-Control-Allow-Origin'] = settings.FIREPLACE_URL
61+
response['Access-Control-Allow-Origin'] = fireplace_url
6062
response['Access-Control-Allow-Credentials'] = 'true'
6163
else:
6264
response['Access-Control-Allow-Origin'] = '*'
@@ -69,3 +71,32 @@ def process_response(self, request, response):
6971
response['Access-Control-Allow-Headers'] = 'Content-Type'
7072
response['Access-Control-Allow-Methods'] = ', '.join(options)
7173
return response
74+
75+
v_re = re.compile('^/api/v(?P<version>\d+)/|^/api/')
76+
77+
78+
class APIVersionMiddleware(object):
79+
"""
80+
Figures out what version of the API they are on. Maybe adds in a
81+
deprecation notice.
82+
"""
83+
84+
def process_request(self, request):
85+
try:
86+
version = v_re.match(request.META['PATH_INFO']).group('version')
87+
except AttributeError:
88+
# Not in the API.
89+
return
90+
91+
# If you are in the API, but don't have a version, this will be None.
92+
request.API_VERSION = version
93+
94+
def process_response(self, request, response):
95+
# Not in the API.
96+
if not hasattr(request, 'API_VERSION'):
97+
return response
98+
99+
response['X-API-Version'] = request.API_VERSION
100+
if not request.API_VERSION:
101+
response['X-API-Status'] = 'Deprecated'
102+
return response

mkt/api/tests/test_middleware.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import amo.tests
1010
from mkt.api.middleware import (APIPinningMiddleware, APITransactionMiddleware,
11-
CORSMiddleware)
11+
APIVersionMiddleware, CORSMiddleware)
1212
from mkt.site.middleware import RedirectPrefixedURIMiddleware
1313

1414
fireplace_url = 'http://firepla.ce:1234'
@@ -94,3 +94,22 @@ def test_not_pinned(self):
9494
self.req.amo_user.is_anonymous.return_value = True
9595
self.pin.process_request(self.req)
9696
ok_(not this_thread_is_pinned())
97+
98+
99+
class TestAPIVersionMiddleware(amo.tests.TestCase):
100+
101+
def setUp(self):
102+
self.dep = APIVersionMiddleware()
103+
104+
def req(self, url, header):
105+
req = RequestFactory().get(url)
106+
self.dep.process_request(req)
107+
res = self.dep.process_response(req, HttpResponse())
108+
return res.get(header, None)
109+
110+
def test_notice(self):
111+
eq_(self.req('/foo/', 'X-API-Status'), None)
112+
eq_(self.req('/foo/api/', 'X-API-Status'), None)
113+
eq_(self.req('/api/', 'X-API-Status'), 'Deprecated')
114+
eq_(self.req('/api/v1/', 'X-API-Status'), None)
115+
eq_(self.req('/api/v1/', 'X-API-Version'), '1')

mkt/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
'mkt.fragments.middleware.VaryOnAJAXMiddleware',
9898
'mkt.site.middleware.DeviceDetectionMiddleware',
9999
'mkt.fragments.middleware.HijackRedirectMiddleware',
100+
'mkt.api.middleware.APIVersionMiddleware',
100101
'mkt.api.middleware.CORSMiddleware',
101102
'mkt.api.middleware.APITransactionMiddleware',
102103
'mkt.api.middleware.APIPinningMiddleware'

mkt/urls.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@
2828
handler404 = 'mkt.site.views.handler404'
2929
handler500 = 'mkt.site.views.handler500'
3030

31+
api_patterns = patterns('',
32+
url('', include(home_api_patterns)),
33+
url('', include('mkt.api.urls')),
34+
url('', include(account_api_patterns)),
35+
url('', include(reviewer_api_patterns)),
36+
url('', include('mkt.webpay.urls')),
37+
url('', include(receipt_api_patterns)),
38+
url('', include('mkt.monolith.urls')),
39+
)
3140

3241
urlpatterns = patterns('',
3342
# Home.
@@ -118,13 +127,9 @@
118127
url('^login$', login, name='users.login'),
119128
url('^logout$', logout, name='users.logout'),
120129

121-
url('^api/', include(home_api_patterns)),
122-
url('^api/', include('mkt.api.urls')),
123-
url('^api/', include(account_api_patterns)),
124-
url('^api/', include(reviewer_api_patterns)),
125-
url('^api/', include('mkt.webpay.urls')),
126-
url('^api/', include(receipt_api_patterns)),
127-
url('^api/', include('mkt.monolith.urls')),
130+
# Version the API here.
131+
url('^api/v1/', include(api_patterns)),
132+
url('^api/', include(api_patterns)),
128133

129134
url('^appcache/', include('django_appcache.urls')),
130135

0 commit comments

Comments
 (0)