diff --git a/.coveragerc b/.coveragerc index 7972544..05910d7 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,4 +1,5 @@ [run] source = hyperwallet -omit = venv/* -omit = hyperwallet/tests/* +omit = + venv/* + hyperwallet/tests/* diff --git a/.gitignore b/.gitignore index b2c3f32..ae34f4d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ htmlcov # Other .DS_Store .coverage +.idea/ \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d5bc367..43d6536 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +1.1.2 (current) +------------------ + +- Added bank card endpoint + 1.1.1 (2017-10-11) ------------------ diff --git a/hyperwallet/__init__.py b/hyperwallet/__init__.py index 035fc8f..8dd1f18 100644 --- a/hyperwallet/__init__.py +++ b/hyperwallet/__init__.py @@ -6,7 +6,7 @@ __email__ = 'devsupport@hyperwallet.com' __copyright__ = 'Copyright (c) 2017 Hyperwallet' __license__ = 'MIT' -__version__ = '1.1.1' +__version__ = '1.1.2' __url__ = 'https://github.com/hyperwallet/python-sdk' __download_url__ = 'https://pypi.python.org/pypi/hyperwallet-sdk' __description__ = 'A Python wrapper around the Hyperwallet API' diff --git a/hyperwallet/api.py b/hyperwallet/api.py index 30c2048..ae8bd40 100644 --- a/hyperwallet/api.py +++ b/hyperwallet/api.py @@ -2,9 +2,9 @@ import os -from config import SERVER -from exceptions import HyperwalletException -from utils import ApiClient +from .config import SERVER +from .exceptions import HyperwalletException +from .utils import ApiClient from hyperwallet import ( User, @@ -155,37 +155,6 @@ def listUsers(self, return [User(x) for x in response.get('data', [])] - def createUserStatusTransition(self, - userToken=None, - data=None): - ''' - Create a User Status Transition. - - :param userToken: - A token identifying the User. **REQUIRED** - :param data: - A dictionary containing User Status Transition information. **REQUIRED** - :returns: - A User Status Transition. - ''' - - if not userToken: - raise HyperwalletException('userToken is required') - - if not data: - raise HyperwalletException('data is required') - - response = self.apiClient.doPost( - os.path.join( - 'users', - userToken, - 'status-transitions' - ), - data - ) - - return StatusTransition(response) - def getUserStatusTransition(self, userToken=None, statusTransitionToken=None): @@ -821,6 +790,44 @@ def createPrepaidCard(self, return PrepaidCard(response) + def updatePrepaidCard(self, + userToken=None, + prepaidCardToken=None, + data=None): + ''' + Update a Prepaid Card. + + :param userToken: + A token identifying the User. **REQUIRED** + :param prepaidCardToken: + A token identifying the Prepaid Card. **REQUIRED** + :param data: + A dictionary containing Prepaid Card information. **REQUIRED** + :returns: + A Prepaid Card. + ''' + + if not userToken: + raise HyperwalletException('userToken is required') + + if not prepaidCardToken: + raise HyperwalletException('prepaidCardToken is required') + + if not data: + raise HyperwalletException('data is required') + + response = self.apiClient.doPut( + os.path.join( + 'users', + userToken, + 'prepaid-cards', + prepaidCardToken + ), + data + ) + + return PrepaidCard(response) + def getPrepaidCard(self, userToken=None, prepaidCardToken=None): @@ -1560,6 +1567,37 @@ def listPaymentStatusTransitions(self, return [StatusTransition(x) for x in response.get('data', [])] + def createPaymentStatusTransition(self, + paymentToken=None, + data=None): + ''' + Create Payment Status Transition. + + :param paymentToken: + A token identifying the Payment. **REQUIRED** + :param data: + A dictionary containing User Status Transition information. **REQUIRED** + :returns: + A Payment Status Transition. + ''' + + if not paymentToken: + raise HyperwalletException('paymentToken is required') + + if not data: + raise HyperwalletException('data is required') + + response = self.apiClient.doPost( + os.path.join( + 'payments', + paymentToken, + 'status-transitions' + ), + data + ) + + return StatusTransition(response) + ''' Balances diff --git a/hyperwallet/exceptions.py b/hyperwallet/exceptions.py index 82f8fcc..6cef772 100644 --- a/hyperwallet/exceptions.py +++ b/hyperwallet/exceptions.py @@ -6,8 +6,16 @@ class HyperwalletException(Exception): An Exception raised when the SDK is used incorrectly. ''' + @property + def message(self): + return self.__dict__.get('message', None) or getattr(self, 'args')[0] + class HyperwalletAPIException(Exception): ''' An Exception raised when the API response is an error. ''' + + @property + def message(self): + return self.__dict__.get('message', None) or getattr(self, 'args')[0] diff --git a/hyperwallet/tests/test_api.py b/hyperwallet/tests/test_api.py index 1033d05..d3765d7 100644 --- a/hyperwallet/tests/test_api.py +++ b/hyperwallet/tests/test_api.py @@ -131,28 +131,6 @@ def test_list_users_success(self, mock_get): self.assertEqual(response[0].token, self.data.get('token')) - def test_create_user_status_transition_fail_need_user_token(self): - - with self.assertRaises(HyperwalletException) as exc: - self.api.createUserStatusTransition() - - self.assertEqual(exc.exception.message, 'userToken is required') - - def test_create_user_status_transition_fail_need_data(self): - - with self.assertRaises(HyperwalletException) as exc: - self.api.createUserStatusTransition('token') - - self.assertEqual(exc.exception.message, 'data is required') - - @mock.patch('hyperwallet.utils.ApiClient._makeRequest') - def test_create_user_status_transition_success(self, mock_post): - - mock_post.return_value = self.data - response = self.api.createUserStatusTransition('token', self.data) - - self.assertTrue(response.token, self.data.get('token')) - def test_get_user_status_transition_fail_need_user_token(self): with self.assertRaises(HyperwalletException) as exc: @@ -632,6 +610,35 @@ def test_create_prepaid_card_success(self, mock_post): self.assertTrue(response.token, self.data.get('token')) + def test_update_prepaid_card_fail_need_user_token(self): + + with self.assertRaises(HyperwalletException) as exc: + self.api.updatePrepaidCard() + + self.assertEqual(exc.exception.message, 'userToken is required') + + def test_update_prepaid_card_fail_need_card_token(self): + + with self.assertRaises(HyperwalletException) as exc: + self.api.updatePrepaidCard('token') + + self.assertEqual(exc.exception.message, 'prepaidCardToken is required') + + def test_update_prepaid_card_fail_need_data(self): + + with self.assertRaises(HyperwalletException) as exc: + self.api.updatePrepaidCard('token', 'token') + + self.assertEqual(exc.exception.message, 'data is required') + + @mock.patch('hyperwallet.utils.ApiClient._makeRequest') + def test_update_prepaid_card_success(self, mock_put): + + mock_put.return_value = self.data + response = self.api.updatePrepaidCard('token', 'token', self.data) + + self.assertTrue(response.token, self.data.get('token')) + def test_get_prepaid_card_fail_need_user_token(self): with self.assertRaises(HyperwalletException) as exc: @@ -1235,6 +1242,28 @@ def test_list_payment_status_transitions_success(self, mock_get): self.assertTrue(response[0].token, self.data.get('token')) + def test_create_payment_status_transition_fail_need_payment_token(self): + + with self.assertRaises(HyperwalletException) as exc: + self.api.createPaymentStatusTransition() + + self.assertEqual(exc.exception.message, 'paymentToken is required') + + def test_create_payment_status_transition_fail_need_data(self): + + with self.assertRaises(HyperwalletException) as exc: + self.api.createPaymentStatusTransition('token') + + self.assertEqual(exc.exception.message, 'data is required') + + @mock.patch('hyperwallet.utils.ApiClient._makeRequest') + def test_create_payment_status_transition_success(self, mock_post): + + mock_post.return_value = self.data + response = self.api.createPaymentStatusTransition('token', self.data) + + self.assertTrue(response.token, self.data.get('token')) + ''' Balances diff --git a/hyperwallet/tests/test_client.py b/hyperwallet/tests/test_client.py index 2757e76..54dd470 100644 --- a/hyperwallet/tests/test_client.py +++ b/hyperwallet/tests/test_client.py @@ -91,9 +91,13 @@ def test_receive_valid_json_response(self, session_mock): content=json.dumps(data) ) + encoded = json.dumps(data) + if hasattr(encoded, 'decode'): # Python 2 + encoded = encoded.decode('utf-8') + self.assertEqual( self.client._makeRequest(), - json.loads(json.dumps(data).decode('utf-8')) + json.loads(encoded) ) diff --git a/hyperwallet/tests/test_models.py b/hyperwallet/tests/test_models.py index 474b4e4..08ceb9b 100644 --- a/hyperwallet/tests/test_models.py +++ b/hyperwallet/tests/test_models.py @@ -2,9 +2,6 @@ import json import unittest -import hyperwallet - -from hyperwallet.exceptions import HyperwalletException from hyperwallet import ( HyperwalletModel, @@ -374,7 +371,7 @@ def test_transfer_method_configuration_model(self): transfer_method_configuration_data ) - print transfer_method_configuration_data.get('countries') + print(transfer_method_configuration_data.get('countries')) self.assertEqual( test_transfer_method_configuration.__repr__(), 'TransferMethodConfiguration({country}, {type})'.format( diff --git a/hyperwallet/utils/apiclient.py b/hyperwallet/utils/apiclient.py index 7a88438..9b2461c 100644 --- a/hyperwallet/utils/apiclient.py +++ b/hyperwallet/utils/apiclient.py @@ -1,14 +1,16 @@ #!/usr/bin/env python -import sys import ssl import json -import urlparse import requests from hyperwallet.exceptions import HyperwalletAPIException from requests_toolbelt.adapters.ssl import SSLAdapter from hyperwallet import __version__ +try: + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin # Python 2 class ApiClient(object): @@ -42,7 +44,7 @@ def __init__(self, username, password, server): self.server = server # The complete base URL of the API. - self.baseUrl = urlparse.urljoin(self.server, '/rest/v3/') + self.baseUrl = urljoin(self.server, '/rest/v3/') # The default connection to persist authentication and SSL settings. defaultSession = requests.Session() @@ -81,7 +83,7 @@ def _makeRequest(self, try: response = self.session.request( method=method, - url=urlparse.urljoin(self.baseUrl, url), + url=urljoin(self.baseUrl, url), data=data, headers=headers, params=params @@ -93,7 +95,7 @@ def _makeRequest(self, 'code': 'COMMUNICATION_ERROR', 'message': 'Connection to {} failed: {}'.format( self.server, - e.message + e.args[0] ) }] }) @@ -101,7 +103,9 @@ def _makeRequest(self, if response.status_code is 204: return {} - content = response.content.decode('utf-8') + content = response.content + if hasattr(content, 'decode'): # Python 2 + content = content.decode('utf-8') try: json_body = json.loads(content) @@ -110,7 +114,7 @@ def _makeRequest(self, raise HyperwalletAPIException({ 'errors': [{ 'code': 'GARBAGE_RESPONSE', - 'message': 'Invalid response: {}'.format(e.message) + 'message': 'Invalid response: {}'.format(e.args[0]) }] })