From 3b3104bc4b034e2d6c13902c87bc542480595b9f Mon Sep 17 00:00:00 2001 From: Remi Jannel Date: Fri, 12 Oct 2018 11:06:00 -0700 Subject: [PATCH] Add support for the Person resource --- .travis.yml | 2 +- stripe/api_resources/__init__.py | 1 + stripe/api_resources/account.py | 8 +++ stripe/api_resources/person.py | 30 ++++++++++++ stripe/util.py | 1 + tests/api_resources/test_account.py | 75 ++++++++++++++++++++++++++++- tests/api_resources/test_person.py | 43 +++++++++++++++++ tests/conftest.py | 2 +- 8 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 stripe/api_resources/person.py create mode 100644 tests/api_resources/test_person.py diff --git a/.travis.yml b/.travis.yml index aabecbb66..f14bfd03e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ cache: env: global: # If changing this number, please also change it in `tests/conftest.py`. - - STRIPE_MOCK_VERSION=0.33.0 + - STRIPE_MOCK_VERSION=0.35.0 before_install: # Unpack and start stripe-mock so that the test suite can talk to it diff --git a/stripe/api_resources/__init__.py b/stripe/api_resources/__init__.py index e63c75a9c..d0b755e7f 100644 --- a/stripe/api_resources/__init__.py +++ b/stripe/api_resources/__init__.py @@ -35,6 +35,7 @@ from stripe.api_resources.order_return import OrderReturn from stripe.api_resources.payment_intent import PaymentIntent from stripe.api_resources.payout import Payout +from stripe.api_resources.person import Person from stripe.api_resources.plan import Plan from stripe.api_resources.product import Product from stripe.api_resources.recipient import Recipient diff --git a/stripe/api_resources/account.py b/stripe/api_resources/account.py index b578bff9e..9b0e96354 100644 --- a/stripe/api_resources/account.py +++ b/stripe/api_resources/account.py @@ -15,6 +15,10 @@ operations=['create', 'retrieve', 'update', 'delete', 'list'] ) @nested_resource_class_methods('login_link', operations=['create']) +@nested_resource_class_methods( + 'person', + operations=['create', 'retrieve', 'update', 'delete', 'list'] +) class Account(CreateableAPIResource, ListableAPIResource, UpdateableAPIResource, DeletableAPIResource): OBJECT_NAME = 'account' @@ -41,6 +45,10 @@ def _build_instance_url(cls, sid): def instance_url(self): return self._build_instance_url(self.get('id')) + def persons(self, **params): + return self.request( + 'get', self.instance_url() + '/persons', params) + def reject(self, idempotency_key=None, **params): url = self.instance_url() + '/reject' headers = util.populate_headers(idempotency_key) diff --git a/stripe/api_resources/person.py b/stripe/api_resources/person.py new file mode 100644 index 000000000..a79d650b7 --- /dev/null +++ b/stripe/api_resources/person.py @@ -0,0 +1,30 @@ +from __future__ import absolute_import, division, print_function + +from stripe import util +from stripe.api_resources.account import Account +from stripe.api_resources.abstract import UpdateableAPIResource +from stripe.six.moves.urllib.parse import quote_plus + + +class Person(UpdateableAPIResource): + OBJECT_NAME = 'person' + + def instance_url(self): + token = util.utf8(self.id) + account = util.utf8(self.account) + base = Account.class_url() + acct_extn = quote_plus(account) + extn = quote_plus(token) + return "%s/%s/persons/%s" % (base, acct_extn, extn) + + @classmethod + def modify(cls, sid, **params): + raise NotImplementedError( + "Can't modify a person without an account" + "ID. Call save on account.persons.retrieve('person_id')") + + @classmethod + def retrieve(cls, id, api_key=None, **params): + raise NotImplementedError( + "Can't retrieve a person without an account" + "ID. Use account.persons.retrieve('person_id')") diff --git a/stripe/util.py b/stripe/util.py index 046adb8dd..9a02f1046 100644 --- a/stripe/util.py +++ b/stripe/util.py @@ -187,6 +187,7 @@ def load_object_classes(): api_resources.OrderReturn.OBJECT_NAME: api_resources.OrderReturn, api_resources.PaymentIntent.OBJECT_NAME: api_resources.PaymentIntent, api_resources.Payout.OBJECT_NAME: api_resources.Payout, + api_resources.Person.OBJECT_NAME: api_resources.Person, api_resources.Plan.OBJECT_NAME: api_resources.Plan, api_resources.Product.OBJECT_NAME: api_resources.Product, api_resources.Recipient.OBJECT_NAME: api_resources.Recipient, diff --git a/tests/api_resources/test_account.py b/tests/api_resources/test_account.py index 5374cb782..098bc3893 100644 --- a/tests/api_resources/test_account.py +++ b/tests/api_resources/test_account.py @@ -5,6 +5,7 @@ TEST_RESOURCE_ID = 'acct_123' TEST_EXTERNALACCOUNT_ID = 'ba_123' +TEST_PERSON_ID = 'person_123' class TestAccount(object): @@ -136,6 +137,16 @@ def test_is_deauthorizable(self, request_mock): } ) + def test_can_call_persons(self, request_mock): + account = stripe.Account.retrieve(TEST_RESOURCE_ID) + resources = account.persons() + request_mock.assert_requested( + 'get', + '/v1/accounts/%s/persons' % TEST_RESOURCE_ID + ) + assert isinstance(resources.data, list) + assert isinstance(resources.data[0], stripe.Person) + class TestAccountExternalAccounts(object): def test_is_listable(self, request_mock): @@ -145,7 +156,6 @@ def test_is_listable(self, request_mock): '/v1/accounts/%s/external_accounts' % TEST_RESOURCE_ID ) assert isinstance(resources.data, list) - assert isinstance(resources.data[0], stripe.Card) def test_is_retrievable(self, request_mock): resource = stripe.Account.retrieve_external_account( @@ -204,3 +214,66 @@ def test_is_creatable(self, request_mock): '/v1/accounts/%s/login_links' % TEST_RESOURCE_ID ) assert isinstance(resource, stripe.LoginLink) + + +class TestAccountPersons(object): + def test_is_creatable(self, request_mock): + resource = stripe.Account.create_person( + TEST_RESOURCE_ID, + dob=dict( + day=1, + month=1, + year=1980 + ) + ) + request_mock.assert_requested( + 'post', + '/v1/accounts/%s/persons' % TEST_RESOURCE_ID + ) + assert isinstance(resource, stripe.Person) + + def test_is_deletable(self, request_mock): + resource = stripe.Account.delete_person( + TEST_RESOURCE_ID, + TEST_PERSON_ID + ) + request_mock.assert_requested( + 'delete', + '/v1/accounts/%s/persons/%s' % (TEST_RESOURCE_ID, + TEST_PERSON_ID) + ) + assert resource.deleted is True + + def test_is_listable(self, request_mock): + resources = stripe.Account.list_persons(TEST_RESOURCE_ID) + request_mock.assert_requested( + 'get', + '/v1/accounts/%s/persons' % TEST_RESOURCE_ID + ) + assert isinstance(resources.data, list) + assert isinstance(resources.data[0], stripe.Person) + + def test_is_modifiable(self, request_mock): + resource = stripe.Account.modify_person( + TEST_RESOURCE_ID, + TEST_PERSON_ID, + metadata={'foo': 'bar'} + ) + request_mock.assert_requested( + 'post', + '/v1/accounts/%s/persons/%s' % (TEST_RESOURCE_ID, + TEST_PERSON_ID) + ) + assert isinstance(resource, stripe.Person) + + def test_is_retrievable(self, request_mock): + resource = stripe.Account.retrieve_person( + TEST_RESOURCE_ID, + TEST_PERSON_ID + ) + request_mock.assert_requested( + 'get', + '/v1/accounts/%s/persons/%s' % (TEST_RESOURCE_ID, + TEST_PERSON_ID) + ) + assert isinstance(resource, stripe.Person) diff --git a/tests/api_resources/test_person.py b/tests/api_resources/test_person.py new file mode 100644 index 000000000..02d80e1ab --- /dev/null +++ b/tests/api_resources/test_person.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import, division, print_function + +import pytest + +import stripe + + +TEST_RESOURCE_ID = 'trr_123' + + +class TestPerson(object): + def construct_resource(self): + person_dict = { + 'id': TEST_RESOURCE_ID, + 'object': 'person', + 'account': 'acct_123' + } + return stripe.Person.construct_from(person_dict, stripe.api_key) + + def test_has_instance_url(self, request_mock): + resource = self.construct_resource() + assert resource.instance_url() == \ + '/v1/accounts/acct_123/persons/%s' % TEST_RESOURCE_ID + + def test_is_not_modifiable(self, request_mock): + with pytest.raises(NotImplementedError): + stripe.Person.modify( + TEST_RESOURCE_ID, + first_name='John' + ) + + def test_is_not_retrievable(self, request_mock): + with pytest.raises(NotImplementedError): + stripe.Person.retrieve(TEST_RESOURCE_ID) + + def test_is_saveable(self, request_mock): + resource = self.construct_resource() + resource.first_name = 'John' + resource.save() + request_mock.assert_requested( + 'post', + '/v1/accounts/acct_123/persons/%s' % TEST_RESOURCE_ID + ) diff --git a/tests/conftest.py b/tests/conftest.py index 4a3abf51b..14bc6ca09 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,7 +13,7 @@ from tests.request_mock import RequestMock -MOCK_MINIMUM_VERSION = '0.33.0' +MOCK_MINIMUM_VERSION = '0.35.0' MOCK_PORT = os.environ.get('STRIPE_MOCK_PORT', 12111)