Add API versioning support, including tests
This commit is contained in:
parent
677a7cf3bd
commit
afde3b2231
3 changed files with 81 additions and 10 deletions
|
@ -4,14 +4,24 @@ import api_photo
|
||||||
import api_tag
|
import api_tag
|
||||||
import api_album
|
import api_album
|
||||||
|
|
||||||
|
LATEST_API_VERSION = 2
|
||||||
|
|
||||||
class OpenPhoto(OpenPhotoHttp):
|
class OpenPhoto(OpenPhotoHttp):
|
||||||
""" Client library for OpenPhoto """
|
"""
|
||||||
|
Python client library for the specified OpenPhoto host.
|
||||||
|
OAuth tokens (consumer*, token*) can optionally be specified.
|
||||||
|
|
||||||
|
All requests will include the api_version path, if specified.
|
||||||
|
This should be used to ensure that your application will continue to work
|
||||||
|
even if the OpenPhoto API is updated to a new revision.
|
||||||
|
"""
|
||||||
def __init__(self, host,
|
def __init__(self, host,
|
||||||
consumer_key='', consumer_secret='',
|
consumer_key='', consumer_secret='',
|
||||||
token='', token_secret=''):
|
token='', token_secret='',
|
||||||
|
api_version=None):
|
||||||
OpenPhotoHttp.__init__(self, host,
|
OpenPhotoHttp.__init__(self, host,
|
||||||
consumer_key, consumer_secret,
|
consumer_key, consumer_secret,
|
||||||
token, token_secret)
|
token, token_secret, api_version)
|
||||||
|
|
||||||
self.photos = api_photo.ApiPhotos(self)
|
self.photos = api_photo.ApiPhotos(self)
|
||||||
self.photo = api_photo.ApiPhoto(self)
|
self.photo = api_photo.ApiPhoto(self)
|
||||||
|
|
|
@ -19,12 +19,13 @@ DUPLICATE_RESPONSE = {"code": 409,
|
||||||
class OpenPhotoHttp:
|
class OpenPhotoHttp:
|
||||||
""" Base class to handle HTTP requests to an OpenPhoto server """
|
""" Base class to handle HTTP requests to an OpenPhoto server """
|
||||||
def __init__(self, host, consumer_key='', consumer_secret='',
|
def __init__(self, host, consumer_key='', consumer_secret='',
|
||||||
token='', token_secret=''):
|
token='', token_secret='', api_version=None):
|
||||||
self._host = host
|
self._host = host
|
||||||
self._consumer_key = consumer_key
|
self._consumer_key = consumer_key
|
||||||
self._consumer_secret = consumer_secret
|
self._consumer_secret = consumer_secret
|
||||||
self._token = token
|
self._token = token
|
||||||
self._token_secret = token_secret
|
self._token_secret = token_secret
|
||||||
|
self._api_version = api_version
|
||||||
|
|
||||||
self._logger = logging.getLogger("openphoto")
|
self._logger = logging.getLogger("openphoto")
|
||||||
|
|
||||||
|
@ -37,11 +38,18 @@ class OpenPhotoHttp:
|
||||||
"""
|
"""
|
||||||
Performs an HTTP GET from the specified endpoint (API path),
|
Performs an HTTP GET from the specified endpoint (API path),
|
||||||
passing parameters if given.
|
passing parameters if given.
|
||||||
|
The api_version is prepended to the endpoint,
|
||||||
|
if it was specified when the OpenPhoto object was created.
|
||||||
|
|
||||||
Returns the decoded JSON dictionary, and raises exceptions if an
|
Returns the decoded JSON dictionary, and raises exceptions if an
|
||||||
error code is received.
|
error code is received.
|
||||||
Returns the raw response if process_response=False
|
Returns the raw response if process_response=False
|
||||||
"""
|
"""
|
||||||
params = self._process_params(params)
|
params = self._process_params(params)
|
||||||
|
if not endpoint.startswith("/"):
|
||||||
|
endpoint = "/" + endpoint
|
||||||
|
if self._api_version is not None:
|
||||||
|
endpoint = "/v%d%s" % (self._api_version, endpoint)
|
||||||
url = urlparse.urlunparse(('http', self._host, endpoint, '',
|
url = urlparse.urlunparse(('http', self._host, endpoint, '',
|
||||||
urllib.urlencode(params), ''))
|
urllib.urlencode(params), ''))
|
||||||
if self._consumer_key:
|
if self._consumer_key:
|
||||||
|
@ -72,11 +80,18 @@ class OpenPhotoHttp:
|
||||||
"""
|
"""
|
||||||
Performs an HTTP POST to the specified endpoint (API path),
|
Performs an HTTP POST to the specified endpoint (API path),
|
||||||
passing parameters if given.
|
passing parameters if given.
|
||||||
|
The api_version is prepended to the endpoint,
|
||||||
|
if it was specified when the OpenPhoto object was created.
|
||||||
|
|
||||||
Returns the decoded JSON dictionary, and raises exceptions if an
|
Returns the decoded JSON dictionary, and raises exceptions if an
|
||||||
error code is received.
|
error code is received.
|
||||||
Returns the raw response if process_response=False
|
Returns the raw response if process_response=False
|
||||||
"""
|
"""
|
||||||
params = self._process_params(params)
|
params = self._process_params(params)
|
||||||
|
if not endpoint.startswith("/"):
|
||||||
|
endpoint = "/" + endpoint
|
||||||
|
if self._api_version is not None:
|
||||||
|
endpoint = "/v%d%s" % (self._api_version, endpoint)
|
||||||
url = urlparse.urlunparse(('http', self._host, endpoint, '', '', ''))
|
url = urlparse.urlunparse(('http', self._host, endpoint, '', '', ''))
|
||||||
|
|
||||||
if not self._consumer_key:
|
if not self._consumer_key:
|
||||||
|
|
46
tests/test_framework.py
Normal file
46
tests/test_framework.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import unittest
|
||||||
|
import logging
|
||||||
|
import openphoto
|
||||||
|
import test_base
|
||||||
|
|
||||||
|
class TestFramework(test_base.TestBase):
|
||||||
|
def setUp(self):
|
||||||
|
"""Override the default setUp, since we don't need a populated database"""
|
||||||
|
logging.info("\nRunning %s..." % self.id())
|
||||||
|
|
||||||
|
def create_client_from_base(self, api_version):
|
||||||
|
return openphoto.OpenPhoto(self.client._host,
|
||||||
|
self.client._consumer_key,
|
||||||
|
self.client._consumer_secret,
|
||||||
|
self.client._token,
|
||||||
|
self.client._token_secret,
|
||||||
|
api_version=api_version)
|
||||||
|
|
||||||
|
def test_api_version_zero(self):
|
||||||
|
# API v0 has a special hello world message
|
||||||
|
client = self.create_client_from_base(api_version=0)
|
||||||
|
result = client.get("hello.json")
|
||||||
|
self.assertEqual(result['message'], "Hello, world! This is version zero of the API!")
|
||||||
|
self.assertEqual(result['result']['__route__'], "/v0/hello.json")
|
||||||
|
|
||||||
|
def test_specified_api_version(self):
|
||||||
|
# For all API versions >0, we get a generic hello world message
|
||||||
|
for api_version in range(1, openphoto.LATEST_API_VERSION + 1):
|
||||||
|
client = self.create_client_from_base(api_version=api_version)
|
||||||
|
result = client.get("hello.json")
|
||||||
|
self.assertEqual(result['message'], "Hello, world!")
|
||||||
|
self.assertEqual(result['result']['__route__'], "/v%d/hello.json" % api_version)
|
||||||
|
|
||||||
|
def test_unspecified_api_version(self):
|
||||||
|
# If the API version is unspecified, we get a generic hello world message
|
||||||
|
client = self.create_client_from_base(api_version=None)
|
||||||
|
result = client.get("hello.json")
|
||||||
|
self.assertEqual(result['message'], "Hello, world!")
|
||||||
|
self.assertEqual(result['result']['__route__'], "/hello.json")
|
||||||
|
|
||||||
|
def test_future_api_version(self):
|
||||||
|
# If the API version is unsupported, we should get an error
|
||||||
|
# (it's a ValueError, since the returned 404 HTML page is not valid JSON)
|
||||||
|
client = self.create_client_from_base(api_version=openphoto.LATEST_API_VERSION + 1)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
client.get("hello.json")
|
Loading…
Add table
Add a link
Reference in a new issue