From a305a542a14ea16cd2145f427ea4d667b86545b5 Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Mon, 2 Jan 2017 22:26:32 +0100 Subject: [PATCH 1/8] Add support for user= parameter for most URLs --- imboclient/url/image.py | 6 +++--- imboclient/url/images.py | 6 +++--- imboclient/url/metadata.py | 6 +++--- imboclient/url/url.py | 10 ++++++++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/imboclient/url/image.py b/imboclient/url/image.py index d04295d..0fc2e37 100644 --- a/imboclient/url/image.py +++ b/imboclient/url/image.py @@ -3,12 +3,12 @@ class UrlImage (url.Url): - def __init__(self, base_url, public_key, private_key, image_identifier): - url.Url.__init__(self, base_url, public_key, private_key) + def __init__(self, base_url, public_key, private_key, image_identifier, user=None): + url.Url.__init__(self, base_url, public_key, private_key, user=user) self._image_identifier = image_identifier def resource_url(self): - return self._base_url + '/users/' + self._public_key + '/images/' + self._image_identifier + return self.user_url('images/' + self._image_identifier) def border(self, color='000000', width=1, height=1): self.add_query_param('t[]', "border:color={},width={},height={}".format(color, width, height)) diff --git a/imboclient/url/images.py b/imboclient/url/images.py index aa6a1f2..27c7135 100644 --- a/imboclient/url/images.py +++ b/imboclient/url/images.py @@ -3,8 +3,8 @@ class UrlImages (url.Url): - def __init__(self, base_url, public_key, private_key): - super(UrlImages, self).__init__(base_url, public_key, private_key) + def __init__(self, base_url, public_key, private_key, user=None): + super(UrlImages, self).__init__(base_url, public_key, private_key, user=user) def resource_url(self): - return self._base_url + '/users/' + self._public_key + '/images.json' + return self.user_url('images.json') diff --git a/imboclient/url/metadata.py b/imboclient/url/metadata.py index fb0067e..08ac530 100644 --- a/imboclient/url/metadata.py +++ b/imboclient/url/metadata.py @@ -3,9 +3,9 @@ class UrlMetadata (url.Url): - def __init__(self, base_url, public_key, private_key, image_identifier): - url.Url.__init__(self, base_url, public_key, private_key) + def __init__(self, base_url, public_key, private_key, image_identifier, user=None): + url.Url.__init__(self, base_url, public_key, private_key, user=user) self._image_identifier = image_identifier def resource_url(self): - return self._base_url + '/users/' + self._public_key + '/images/' + self._image_identifier + '/' + 'metadata' + return self.user_url('images/' + self._image_identifier + '/' + 'metadata') diff --git a/imboclient/url/url.py b/imboclient/url/url.py index 5f03838..9ccd56a 100644 --- a/imboclient/url/url.py +++ b/imboclient/url/url.py @@ -8,15 +8,22 @@ class Url(object): - def __init__(self, base_url, public_key, private_key): + def __init__(self, base_url, public_key, private_key, user=None): self._base_url = base_url self._public_key = public_key self._private_key = private_key + self._user = user self._query_params = None + self.access_token = accesstoken.AccessToken() + def __str__(self): return self.url() + def user_url(self, resource): + u = self._user if self._user else self._public_key + return self._base_url + '/users/' + u + '/' + resource + def url(self): url = self.resource_url() query_string = self.query_string() @@ -27,7 +34,6 @@ def url(self): if self._public_key is None or self._private_key is None: return url - self.access_token = accesstoken.AccessToken() generated_token = self.access_token.generate_token(url, self._private_key) if self._query_params is None: From 3c2fa047f0839b570678588c0816315904388df6 Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Mon, 2 Jan 2017 22:26:48 +0100 Subject: [PATCH 2/8] Allow user= to be supplied when creating the client --- imboclient/client.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/imboclient/client.py b/imboclient/client.py index 729399e..4bbc6be 100644 --- a/imboclient/client.py +++ b/imboclient/client.py @@ -20,14 +20,15 @@ from urlparse import urlparse class Client: - def __init__(self, server_urls, public_key, private_key, version=None): + def __init__(self, server_urls, public_key, private_key, version=None, user=None): self.server_urls = self._parse_urls(server_urls) self._public_key = public_key self._private_key = private_key self._version = version + self._user = user def metadata_url(self, image_identifier): - return metadata.UrlMetadata(self.server_urls[0], self._public_key, self._private_key, image_identifier) + return metadata.UrlMetadata(self.server_urls[0], self._public_key, self._private_key, image_identifier, user=self._user) def status_url(self): return status.UrlStatus(self.server_urls[0], self._public_key, self._private_key) @@ -36,10 +37,10 @@ def user_url(self): return user.UrlUser(self.server_urls[0], self._public_key, self._private_key) def images_url(self): - return images.UrlImages(self.server_urls[0], self._public_key, self._private_key) + return images.UrlImages(self.server_urls[0], self._public_key, self._private_key, user=self._user) def image_url(self, image_identifier): - return image.UrlImage(self.server_urls[0], self._public_key, self._private_key, image_identifier) + return image.UrlImage(self.server_urls[0], self._public_key, self._private_key, image_identifier, user=self._user) def add_image(self, path): image_file_data = self._image_file_data(path) From cdf554368db373b7fb340fcfe7063ccd9bebc48e Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Mon, 2 Jan 2017 22:27:51 +0100 Subject: [PATCH 3/8] Update mocks to expect the user= parameter, although empty --- imboclient/test/unit/test_client.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/imboclient/test/unit/test_client.py b/imboclient/test/unit/test_client.py index 33e09b4..1067f84 100644 --- a/imboclient/test/unit/test_client.py +++ b/imboclient/test/unit/test_client.py @@ -64,19 +64,19 @@ def test_user_url(self, mocked_url_user): @patch('imboclient.url.images.UrlImages') def test_images_url(self, mocked_url_images): images_url = self._client.images_url() - mocked_url_images.assert_called_once_with('http://imbo.local', 'public', 'private') + mocked_url_images.assert_called_once_with('http://imbo.local', 'public', 'private', user=None) assert images_url == mocked_url_images() @patch('imboclient.url.image.UrlImage') def test_image_url(self, mocked_url_image): image_url = self._client.image_url('ff') - mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff') + mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user=None) assert image_url == mocked_url_image() @patch('imboclient.url.metadata.UrlMetadata') def test_metadata_url(self, mocked_url_metadata): metadata_url = self._client.metadata_url('ff') - mocked_url_metadata.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff') + mocked_url_metadata.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user=None) assert metadata_url == mocked_url_metadata() @patch('imboclient.header.authenticate.Authenticate.headers') @@ -178,7 +178,6 @@ def test_image_identifier_exists_true(self, mocked_url_image, mocked_requests_he assert self._client.image_identifier_exists('ff') is True mocked_requests_head.assert_called_once_with('http://imbo.local/users/public/ff?accessToken=aa') - mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff') @patch('requests.head') @patch('imboclient.url.image.UrlImage') @@ -189,7 +188,7 @@ def test_image_identifier_exists_false(self, mocked_url_image, mocked_requests_g assert self._client.image_identifier_exists('ffa') is False mocked_requests_get.assert_called_once_with('http://imbo.local/users/public/ffa?accessToken=aaf') - mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ffa') + mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ffa', user=None) @patch('imboclient.url.image.UrlImage.url') @patch('requests.head') From 99cf7b8ab21bf8e91b148c87eced6c323f2e4595 Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Mon, 2 Jan 2017 22:33:45 +0100 Subject: [PATCH 4/8] Add tests for urls with user and a _client_with_user to test against --- imboclient/test/unit/test_client.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/imboclient/test/unit/test_client.py b/imboclient/test/unit/test_client.py index 1067f84..3a55e61 100644 --- a/imboclient/test/unit/test_client.py +++ b/imboclient/test/unit/test_client.py @@ -17,9 +17,11 @@ class TestClient: def setup(self): self._client = imbo.Client(['http://imbo.local'], 'public', 'private'); + self._client_with_user = imbo.Client(['http://imbo.local'], 'public', 'private', user='foo'); def teardown(self): self._client = None + self._client_with_user = None def test_server_urls_generic(self): self._client = imbo.Client(['imbo.local'], 'public', 'private'); @@ -67,18 +69,36 @@ def test_images_url(self, mocked_url_images): mocked_url_images.assert_called_once_with('http://imbo.local', 'public', 'private', user=None) assert images_url == mocked_url_images() + @patch('imboclient.url.images.UrlImages') + def test_images_url_with_user(self, mocked_url_images): + images_url = self._client_with_user.images_url() + mocked_url_images.assert_called_once_with('http://imbo.local', 'public', 'private', user='foo') + assert images_url == mocked_url_images() + @patch('imboclient.url.image.UrlImage') def test_image_url(self, mocked_url_image): image_url = self._client.image_url('ff') mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user=None) assert image_url == mocked_url_image() + @patch('imboclient.url.image.UrlImage') + def test_image_url_with_user(self, mocked_url_image): + image_url = self._client_with_user.image_url('ff') + mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user='foo') + assert image_url == mocked_url_image() + @patch('imboclient.url.metadata.UrlMetadata') def test_metadata_url(self, mocked_url_metadata): metadata_url = self._client.metadata_url('ff') mocked_url_metadata.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user=None) assert metadata_url == mocked_url_metadata() + @patch('imboclient.url.metadata.UrlMetadata') + def test_metadata_url_with_user(self, mocked_url_metadata): + metadata_url = self._client_with_user.metadata_url('ff') + mocked_url_metadata.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user='foo') + assert metadata_url == mocked_url_metadata() + @patch('imboclient.header.authenticate.Authenticate.headers') @patch('imboclient.url.images.UrlImages.url') @patch('requests.post') @@ -188,7 +208,6 @@ def test_image_identifier_exists_false(self, mocked_url_image, mocked_requests_g assert self._client.image_identifier_exists('ffa') is False mocked_requests_get.assert_called_once_with('http://imbo.local/users/public/ffa?accessToken=aaf') - mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ffa', user=None) @patch('imboclient.url.image.UrlImage.url') @patch('requests.head') From 1a9089e2f4b4368d2fcd7708d9af24bc724dc213 Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Mon, 2 Jan 2017 22:34:00 +0100 Subject: [PATCH 5/8] Remove stray semicolons --- imboclient/test/unit/test_client.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/imboclient/test/unit/test_client.py b/imboclient/test/unit/test_client.py index 3a55e61..6c4fdca 100644 --- a/imboclient/test/unit/test_client.py +++ b/imboclient/test/unit/test_client.py @@ -16,39 +16,39 @@ class TestClient: def setup(self): - self._client = imbo.Client(['http://imbo.local'], 'public', 'private'); - self._client_with_user = imbo.Client(['http://imbo.local'], 'public', 'private', user='foo'); + self._client = imbo.Client(['http://imbo.local'], 'public', 'private') + self._client_with_user = imbo.Client(['http://imbo.local'], 'public', 'private', user='foo') def teardown(self): self._client = None self._client_with_user = None def test_server_urls_generic(self): - self._client = imbo.Client(['imbo.local'], 'public', 'private'); + self._client = imbo.Client(['imbo.local'], 'public', 'private') assert self._client.server_urls[0] == 'http://imbo.local' def test_server_urls_http(self): - self._client = imbo.Client(['http://imbo.local'], 'public', 'private'); + self._client = imbo.Client(['http://imbo.local'], 'public', 'private') assert self._client.server_urls[0] == 'http://imbo.local' def test_server_urls_https(self): - self._client = imbo.Client(['https://imbo.local'], 'public', 'private'); + self._client = imbo.Client(['https://imbo.local'], 'public', 'private') assert self._client.server_urls[0] == 'https://imbo.local' def test_server_urls_port_normal(self): - self._client = imbo.Client(['http://imbo.local'], 'public', 'private'); + self._client = imbo.Client(['http://imbo.local'], 'public', 'private') assert self._client.server_urls[0] == 'http://imbo.local' def test_server_urls_port_normal_explicit(self): - self._client = imbo.Client(['http://imbo.local:80'], 'public', 'private'); + self._client = imbo.Client(['http://imbo.local:80'], 'public', 'private') assert self._client.server_urls[0] == 'http://imbo.local' def test_server_urls_port_ssl(self): - self._client = imbo.Client(['https://imbo.local:443'], 'public', 'private'); + self._client = imbo.Client(['https://imbo.local:443'], 'public', 'private') assert self._client.server_urls[0] == 'https://imbo.local' def test_server_urls_port_explicit_without_protocol(self): - self._client = imbo.Client(['imbo.local:8000'], 'public', 'private'); + self._client = imbo.Client(['imbo.local:8000'], 'public', 'private') assert self._client.server_urls[0] == 'http://imbo.local:8000' @patch('imboclient.url.status.UrlStatus') From 5905abbb6dd59a87c68c51ba4bf230e9280db13e Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Mon, 2 Jan 2017 22:37:31 +0100 Subject: [PATCH 6/8] Add tests to check actual usage of 'user' in URL --- imboclient/test/unit/test_client.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/imboclient/test/unit/test_client.py b/imboclient/test/unit/test_client.py index 6c4fdca..5055c41 100644 --- a/imboclient/test/unit/test_client.py +++ b/imboclient/test/unit/test_client.py @@ -75,6 +75,10 @@ def test_images_url_with_user(self, mocked_url_images): mocked_url_images.assert_called_once_with('http://imbo.local', 'public', 'private', user='foo') assert images_url == mocked_url_images() + def test_images_url_with_user_used(self): + images_url = self._client_with_user.images_url() + assert images_url.url().startswith('http://imbo.local/users/foo/images') + @patch('imboclient.url.image.UrlImage') def test_image_url(self, mocked_url_image): image_url = self._client.image_url('ff') @@ -87,6 +91,10 @@ def test_image_url_with_user(self, mocked_url_image): mocked_url_image.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user='foo') assert image_url == mocked_url_image() + def test_image_url_with_user_used(self): + image_url = self._client_with_user.image_url('ff') + assert image_url.url().startswith('http://imbo.local/users/foo/images/ff?') + @patch('imboclient.url.metadata.UrlMetadata') def test_metadata_url(self, mocked_url_metadata): metadata_url = self._client.metadata_url('ff') @@ -99,6 +107,10 @@ def test_metadata_url_with_user(self, mocked_url_metadata): mocked_url_metadata.assert_called_once_with('http://imbo.local', 'public', 'private', 'ff', user='foo') assert metadata_url == mocked_url_metadata() + def test_metadata_url_with_user_used(self): + metadata_url = self._client_with_user.metadata_url('ff') + assert metadata_url.url().startswith('http://imbo.local/users/foo/images/ff/metadata?') + @patch('imboclient.header.authenticate.Authenticate.headers') @patch('imboclient.url.images.UrlImages.url') @patch('requests.post') From 50ccc212319ba9752939255c40bfcf849cd7ea77 Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Tue, 3 Jan 2017 11:44:02 +0100 Subject: [PATCH 7/8] Whitespace fix --- imboclient/url/url.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/imboclient/url/url.py b/imboclient/url/url.py index 9ccd56a..7097b24 100644 --- a/imboclient/url/url.py +++ b/imboclient/url/url.py @@ -22,6 +22,7 @@ def __str__(self): def user_url(self, resource): u = self._user if self._user else self._public_key + return self._base_url + '/users/' + u + '/' + resource def url(self): @@ -46,6 +47,7 @@ def add_query_param(self, key, value): self._query_params = [] self._query_params.append((key, value)) + return self def add_query(self, query): @@ -54,6 +56,7 @@ def add_query(self, query): self.add_query_param('limit', query.limit()) self.add_query_param('from', query.q_from()) self.add_query_param('to', query.q_to()) + if query.metadata: self.add_query_param('query', json.dumps(query.query())) @@ -62,6 +65,7 @@ def add_query(self, query): def query_string(self): if not self._query_params: return '' + return urlencode(self._query_params) def resource_url(self): @@ -69,4 +73,5 @@ def resource_url(self): def reset(self): self._query_params = [] + return self From 34ec3adb929e2e3d92e5a9b3032153bcfbf9133f Mon Sep 17 00:00:00 2001 From: Mats Lindh Date: Tue, 3 Jan 2017 11:57:21 +0100 Subject: [PATCH 8/8] Add publicKey URL argument and tests --- imboclient/test/unit/test_client.py | 3 +++ imboclient/url/url.py | 34 +++++++++++++++++++---------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/imboclient/test/unit/test_client.py b/imboclient/test/unit/test_client.py index 5055c41..a1949bd 100644 --- a/imboclient/test/unit/test_client.py +++ b/imboclient/test/unit/test_client.py @@ -78,6 +78,7 @@ def test_images_url_with_user(self, mocked_url_images): def test_images_url_with_user_used(self): images_url = self._client_with_user.images_url() assert images_url.url().startswith('http://imbo.local/users/foo/images') + assert 'publicKey=public' in images_url.url() @patch('imboclient.url.image.UrlImage') def test_image_url(self, mocked_url_image): @@ -94,6 +95,7 @@ def test_image_url_with_user(self, mocked_url_image): def test_image_url_with_user_used(self): image_url = self._client_with_user.image_url('ff') assert image_url.url().startswith('http://imbo.local/users/foo/images/ff?') + assert 'publicKey=public' in image_url.url() @patch('imboclient.url.metadata.UrlMetadata') def test_metadata_url(self, mocked_url_metadata): @@ -110,6 +112,7 @@ def test_metadata_url_with_user(self, mocked_url_metadata): def test_metadata_url_with_user_used(self): metadata_url = self._client_with_user.metadata_url('ff') assert metadata_url.url().startswith('http://imbo.local/users/foo/images/ff/metadata?') + assert 'publicKey=public' in metadata_url.url() @patch('imboclient.header.authenticate.Authenticate.headers') @patch('imboclient.url.images.UrlImages.url') diff --git a/imboclient/url/url.py b/imboclient/url/url.py index 7097b24..1f7143d 100644 --- a/imboclient/url/url.py +++ b/imboclient/url/url.py @@ -13,7 +13,7 @@ def __init__(self, base_url, public_key, private_key, user=None): self._public_key = public_key self._private_key = private_key self._user = user - self._query_params = None + self._query_params = [] self.access_token = accesstoken.AccessToken() @@ -27,20 +27,26 @@ def user_url(self, resource): def url(self): url = self.resource_url() - query_string = self.query_string() - if self._query_params and len(self._query_params) > 0: - url = url + '?' + query_string + # create copy of list + params = list(self._query_params) + + # if we have a user, we'll have to supply the public key as a GET argument + if self._user: + params.append(('publicKey', self._public_key)) + + query_string = self.query_stringify(params) + + if query_string: + url += '?' + query_string if self._public_key is None or self._private_key is None: return url generated_token = self.access_token.generate_token(url, self._private_key) + sep = '?' if not query_string else '&' - if self._query_params is None: - return url + '?accessToken=' + generated_token - - return url + '&accessToken=' + generated_token + return url + sep + 'accessToken=' + generated_token def add_query_param(self, key, value): if self._query_params is None: @@ -63,10 +69,7 @@ def add_query(self, query): return self def query_string(self): - if not self._query_params: - return '' - - return urlencode(self._query_params) + return self.query_stringify(self._query_params) def resource_url(self): raise NotImplementedError("Missing implementation. You may want to use a Url implementation instead.") @@ -75,3 +78,10 @@ def reset(self): self._query_params = [] return self + + @classmethod + def query_stringify(cls, parameters): + if not parameters: + return '' + + return urlencode(parameters) \ No newline at end of file