Skip to content

Commit

Permalink
Add support for the Person resource
Browse files Browse the repository at this point in the history
  • Loading branch information
remi-stripe committed Oct 27, 2018
1 parent a9a8d75 commit 3b3104b
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions stripe/api_resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions stripe/api_resources/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions stripe/api_resources/person.py
Original file line number Diff line number Diff line change
@@ -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')")
1 change: 1 addition & 0 deletions stripe/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
75 changes: 74 additions & 1 deletion tests/api_resources/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

TEST_RESOURCE_ID = 'acct_123'
TEST_EXTERNALACCOUNT_ID = 'ba_123'
TEST_PERSON_ID = 'person_123'


class TestAccount(object):
Expand Down Expand Up @@ -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):
Expand All @@ -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(
Expand Down Expand Up @@ -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)
43 changes: 43 additions & 0 deletions tests/api_resources/test_person.py
Original file line number Diff line number Diff line change
@@ -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
)
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down

0 comments on commit 3b3104b

Please sign in to comment.