diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ca6ff24 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: CI + +on: + push: + branches: + - master + + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-18.04 + + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7, 3.8, pypy2, pypy3] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Test with tox + run: | + pip install "pytest" "tox>=1.9" "tox-gh-actions" + tox diff --git a/.python-version b/.python-version index d4722a7..3364f6d 100755 --- a/.python-version +++ b/.python-version @@ -1,3 +1,5 @@ +3.8.2 +3.7.7 3.5.1 2.7.11 3.4.3 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 21a2de5..0000000 --- a/.travis.yml +++ /dev/null @@ -1,54 +0,0 @@ -language: python - -python: - - "2.6" - - "2.7" - - "3.3" - - "3.4" - - "3.5" - - "pypy" - - "pypy3" - -env: - - REQUESTS=1.0 - - REQUESTS=1.1 - - REQUESTS=1.2 - - REQUESTS=2.0 - - REQUESTS=2.1 - - REQUESTS=2.2 - - REQUESTS=2.3 - - REQUESTS=2.4 - - REQUESTS=2.5 - - REQUESTS=2.6 - - REQUESTS=2.7 - - REQUESTS=2.8 - - REQUESTS=2.9 - - REQUESTS=2.10 - - REQUESTS=2.11 - - REQUESTS=dev - -cache: pip - -matrix: - fast_finish: true - exclude: - # No support for Python 3.4+ in requests 1.x - - python: "3.4" - env: REQUESTS=1.0 - - python: "3.4" - env: REQUESTS=1.1 - - python: "3.4" - env: REQUESTS=1.2 - - python: "3.5" - env: REQUESTS=1.0 - - python: "3.5" - env: REQUESTS=1.1 - - python: "3.5" - env: REQUESTS=1.2 - -# No support for Python 3.2- in virtualenv 14+ -install: - - travis_retry pip install "virtualenv<14.0.0" "tox>=1.9" - -script: - - tox -e py$(echo $(echo $TRAVIS_PYTHON_VERSION | sed -e 's/pypy/py/')-requests$REQUESTS | tr -d .)-travis -- --cov=payplug diff --git a/CHANGELOG.md b/CHANGELOG.md index 61af1ba..0cfade0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +1.2.2 +----- +- Add API version setting +- Fix tests +- Move CI to Github Actions + 1.2.1 ----- - Require pyOpenSSL>=0.15 to prevent random failures. diff --git a/README.rst b/README.rst index 07555e7..18e1d35 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,8 @@ Python library for the PayPlug API ================================== -.. image:: https://travis-ci.org/payplug/payplug-python.svg?branch=master - :target: https://travis-ci.org/payplug/payplug-python +.. image:: https://github.com/payplug/payplug-python/workflows/CI/badge.svg + :target: https://github.com/payplug/payplug-python/actions :alt: CI Status .. image:: https://img.shields.io/pypi/v/payplug.svg?maxAge=2592000 @@ -61,7 +61,7 @@ To get started, add the following piece of code to the header of your Python pro import payplug -If everything run without error, congratulations. You installed PayPlug python library! You're ready to create your +If everything runs without errors, congratulations. You installed PayPlug python library! You're ready to create your first payment. Usage diff --git a/payplug/__init__.py b/payplug/__init__.py index 0ec8430..4c9bfd9 100644 --- a/payplug/__init__.py +++ b/payplug/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from datetime import datetime from six import string_types from payplug import config, exceptions, network, notifications, resources, routes from payplug.network import HttpClient, UrllibRequest @@ -28,6 +29,24 @@ def set_secret_key(token): config.secret_key = token +def set_api_version(version): + """ + Specify the PayPlug API version to use. + + :Example + + >>> import payplug + >>> payplug.set_api_version("2019-08-06") + + :param version: the desired version, as an ISO-8601 date + :type version: string + """ + + if not isinstance(version, string_types) and version is not None: + raise exceptions.ConfigurationError('Expected string value for API version.') + + config.api_version = version + class Payment(object): """ diff --git a/payplug/__version__.py b/payplug/__version__.py index 67a20ab..495a173 100644 --- a/payplug/__version__.py +++ b/payplug/__version__.py @@ -1,2 +1,2 @@ # -*- coding: utf-8 -*- -__version__ = '1.2.1' +__version__ = '1.2.2' diff --git a/payplug/config.py b/payplug/config.py index b683307..010d788 100644 --- a/payplug/config.py +++ b/payplug/config.py @@ -16,3 +16,4 @@ CACERT_PATH = os.path.join(os.path.dirname(__file__), 'certs', 'cacert.pem') secret_key = None +api_version = None diff --git a/payplug/network.py b/payplug/network.py index 348a99a..e351c07 100644 --- a/payplug/network.py +++ b/payplug/network.py @@ -158,7 +158,7 @@ class HttpClient(object): """ HTTP Client that relies on HttpRequest to perform requests. """ - def __init__(self, token=None, request_handler=None): + def __init__(self, token=None, request_handler=None, api_version=None): """ :param token: the secret key that will be used to authenticate API requests :type token: string @@ -173,6 +173,7 @@ def __init__(self, token=None, request_handler=None): raise exceptions.SecretKeyNotSet('You must set your secret key using payplug.set_secret_key() function.') self._secret_key = token or config.secret_key + self._api_version = api_version or config.api_version self._request_handler = request_handler or available_clients[0] def post(self, url, data=None): @@ -280,6 +281,9 @@ def _request(self, http_verb, url, data=None, authenticated=True): if authenticated: headers['Authorization'] = 'Bearer ' + self._secret_key + if self._api_version: + headers['PayPlug-Version'] = self._api_version + requestor = self._request_handler() response, status, _ = requestor.do_request(http_verb, url, headers, data) diff --git a/payplug/test/__init__.py b/payplug/test/__init__.py index de064dd..f7a43a8 100644 --- a/payplug/test/__init__.py +++ b/payplug/test/__init__.py @@ -5,3 +5,4 @@ class TestBase: def setup_method(self, method): payplug.secret_key = None + payplug.api_version = None diff --git a/payplug/test/test_init/test_configuration.py b/payplug/test/test_init/test_configuration.py index aec10d1..ddd4aa5 100644 --- a/payplug/test/test_init/test_configuration.py +++ b/payplug/test/test_init/test_configuration.py @@ -17,3 +17,23 @@ def test_secret_key_set(self): assert config.secret_key is None payplug.set_secret_key('a_secret_key') assert config.secret_key == 'a_secret_key' + +class TestApiVersionKey(TestBase): + def test_no_api_version(self): + import payplug + assert config.api_version is None + payplug.set_api_version(None) + assert config.api_version == None + + def test_invalid_api_version(self): + import payplug + with py.test.raises(exceptions.ConfigurationError) as excinfo: + payplug.set_api_version(42) + assert 'Expected string value for API version.' == str(excinfo.value) + assert config.api_version is None + + def test_secret_api_version(self): + import payplug + assert config.api_version is None + payplug.set_api_version('2019-08-06') + assert config.api_version == '2019-08-06' diff --git a/payplug/test/test_init/test_dao_card.py b/payplug/test/test_init/test_dao_card.py index fc44de3..267c3b3 100644 --- a/payplug/test/test_init/test_dao_card.py +++ b/payplug/test/test_init/test_dao_card.py @@ -57,7 +57,6 @@ def test_delete_with_card_object(self): assert res is None -@pytest.fixture def cards_list_fixture(): return { "type": "list", diff --git a/payplug/test/test_init/test_dao_customer.py b/payplug/test/test_init/test_dao_customer.py index e21b719..3b4424b 100644 --- a/payplug/test/test_init/test_dao_customer.py +++ b/payplug/test/test_init/test_dao_customer.py @@ -49,7 +49,6 @@ def test_delete_with_customer_object(self): assert res is None -@pytest.fixture def customers_list_fixture(): return { "type": "list", diff --git a/payplug/test/test_init/test_dao_payment.py b/payplug/test/test_init/test_dao_payment.py index 8a5e0e5..06275fd 100644 --- a/payplug/test/test_init/test_dao_payment.py +++ b/payplug/test/test_init/test_dao_payment.py @@ -37,7 +37,6 @@ def test_create(self): assert payment.id == 'pay_payment_id' -@pytest.fixture def get_payments_fixture(): return { "type": "list", diff --git a/payplug/test/test_init/test_dao_refund.py b/payplug/test/test_init/test_dao_refund.py index 8bb1d1a..cd6bc29 100644 --- a/payplug/test/test_init/test_dao_refund.py +++ b/payplug/test/test_init/test_dao_refund.py @@ -47,7 +47,6 @@ def test_create_with_payment_object(self, url_mock): assert isinstance(refund, resources.Refund) -@pytest.fixture def get_refunds_fixture(): return { "type": "list", diff --git a/payplug/test/test_network/test_http_client.py b/payplug/test/test_network/test_http_client.py index ce87eb5..e0e13a5 100644 --- a/payplug/test/test_network/test_http_client.py +++ b/payplug/test/test_network/test_http_client.py @@ -30,6 +30,18 @@ def test_default_no_secret_key(self): with pytest.raises(exceptions.SecretKeyNotSet): HttpClient() + @patch('payplug.network.available_clients', [network.RequestsRequest]) + @patch('payplug.config.api_version', 'an_api_version') + def test_default_api_version(self): + http_client = HttpClient() + assert http_client._api_version == 'an_api_version' + + @patch('payplug.network.available_clients', [network.RequestsRequest]) + @patch('payplug.config.api_version', 'an_api_version') + def test_api_version(self): + http_client = HttpClient(api_version='another_api_version') + assert http_client._api_version == 'another_api_version' + @patch('payplug.network.available_clients', [network.RequestsRequest, network.UrllibRequest]) @patch('payplug.config.secret_key', 'a_secret_key') def test_default_request_handler(self): @@ -71,7 +83,7 @@ def test_request_ok(self): requestor.do_request.return_value = '"a valid json response"', 201, {} request_handler = MagicMock(return_value=requestor) - http_client = HttpClient('a_secret_key', request_handler) + http_client = HttpClient('a_secret_key', request_handler, api_version='an_api_version') response, status = http_client._request('POST', 'this_is_an_url', {'some': 'data'}) assert requestor.do_request.call_count == 1 @@ -79,6 +91,7 @@ def test_request_ok(self): assert do_request_args[0] == 'POST' assert do_request_args[1] == 'this_is_an_url' assert do_request_args[2]['Authorization'] == 'Bearer a_secret_key' + assert do_request_args[2]['PayPlug-Version'] == 'an_api_version' assert do_request_args[3] == {'some': 'data'} assert response == 'a valid json response' diff --git a/payplug/test/test_real_http_query.py b/payplug/test/test_real_http_query.py index 835aa6b..005da45 100644 --- a/payplug/test/test_real_http_query.py +++ b/payplug/test/test_real_http_query.py @@ -7,13 +7,15 @@ class TestRealHttpQuery(TestBase): - def test_http_query_requests(self): - http_client = HttpClient(token='a_secret_key', request_handler=RequestsRequest) + @pytest.mark.parametrize("api_version", [None, '2019-08-06']) + def test_http_query_requests(self, api_version): + http_client = HttpClient(token='a_secret_key', api_version=api_version, request_handler=RequestsRequest) _, status = http_client._request('GET', routes.API_BASE_URL + '/test', authenticated=False) assert status == 200 @pytest.mark.xfail(sys.version_info < (2, 7, 9), reason="Can't set ca_file easily with urllib.") - def test_http_query_urllib(self): - http_client = HttpClient(token='a_secret_key', request_handler=UrllibRequest) + @pytest.mark.parametrize("api_version", [None, '2019-08-06']) + def test_http_query_urllib(self, api_version): + http_client = HttpClient(token='a_secret_key', api_version=api_version, request_handler=UrllibRequest) _, status = http_client._request('GET', routes.API_BASE_URL + '/test', authenticated=False) assert status == 200 diff --git a/payplug/test/test_resources/test_payment.py b/payplug/test/test_resources/test_payment.py index dd3d401..14aa849 100644 --- a/payplug/test/test_resources/test_payment.py +++ b/payplug/test/test_resources/test_payment.py @@ -124,7 +124,6 @@ def test_abort_payment(self, payment_abort_mock): payment_abort_mock.assert_called_once_with(payment) -@pytest.fixture def payment_fixture(): return { "id": "pay_5iHMDxy4ABR4YBVW4UscIn", diff --git a/payplug/test/test_resources/test_refund.py b/payplug/test/test_resources/test_refund.py index 8235018..fbeb508 100644 --- a/payplug/test/test_resources/test_refund.py +++ b/payplug/test/test_resources/test_refund.py @@ -37,7 +37,6 @@ def test_initialize_refund(self): assert refund_object.metadata['reason'] == "The delivery was delayed" -@pytest.fixture def refund_fixture(): return { "id": "re_5iHMDxy4ABR4YBVW4UscIn", diff --git a/tox.ini b/tox.ini index bdad4a7..9ae3c39 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,19 @@ [tox] ; No support for Python 3.4+ in requests 1.x envlist = - py{26,27,33,py,py3}-requests{10,11,12,20,21,22,23,24,25,26,27,28,29,210,211,dev}, - py{34,35}-requests{20,21,22,23,24,25,26,27,28,29,210,211,dev} + py{27,py}-requests{10,11,12}, + py{27,py,34,35,36,37,38,py3}-requests{20,21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221,222,223,dev} + +[gh-actions] +python = + 2.7: py27 + 3.4: py34 + 3.5: py35 + 3.6: py36 + 3.7: py37 + 3.8: py38 + pypy2: pypy + pypy3: pypy3 [testenv] deps = @@ -28,12 +39,20 @@ deps = requests29: requests>=2.9,<2.10 requests210: requests>=2.10,<2.11 requests211: requests>=2.11,<2.12 + requests212: requests>=2.12,<2.13 + requests213: requests>=2.13,<2.14 + requests214: requests>=2.14,<2.15 + requests215: requests>=2.15,<2.16 + requests216: requests>=2.16,<2.17 + requests217: requests>=2.17,<2.18 + requests218: requests>=2.18,<2.19 + requests219: requests>=2.19,<2.20 + requests220: requests>=2.20,<2.21 + requests221: requests>=2.21,<2.22 + requests222: requests>=2.22,<2.23 + requests223: requests>=2.23,<2.24 requestsdev: https://github.com/kennethreitz/requests/tarball/master - ; Travis CI runs on Debian Jessie which provides outdated Pypy versions (<2.6) that are incompatible with - ; cryptography >= 1.0. - ; We have to force cryptography < 1.0 in this case. - ; We also force pyOpenSSL < 16.0 because newer versions requires cryptography>=1.3. - ; See https://github.com/travis-ci/travis-ci/issues/4756 - py{py,py3}-travis: pyOpenSSL<16.0 - py{py,py3}-travis: cryptography<1.0 -commands=py.test {posargs} \ No newline at end of file + + requests213,requests214,requests215: idna>=1.0 + +commands=py.test {posargs}