Skip to content

Commit

Permalink
Merge pull request #183 from stripe/auto-pagination
Browse files Browse the repository at this point in the history
Auto pagination
  • Loading branch information
kyleconroy committed Oct 12, 2015
2 parents 6f6f71a + fe16f49 commit ad79162
Show file tree
Hide file tree
Showing 32 changed files with 2,301 additions and 2,139 deletions.
44 changes: 41 additions & 3 deletions stripe/resource.py
Expand Up @@ -312,9 +312,32 @@ def instance_url(self):

class ListObject(StripeObject):

def all(self, **params):
def list(self, **params):
return self.request('get', self['url'], params)

def all(self, **params):
warnings.warn("The `all` method is deprecated and will"
"be removed in future versions. Please use the "
"`list` method instead",
DeprecationWarning)
return self.list(**params)

def auto_paging_iter(self):
page = self
params = dict(self._retrieve_params)

while True:
item_id = None
for item in page:
item_id = item.get('id', None)
yield item

if not getattr(page, 'has_more', False) or item_id is None:
return

params['starting_after'] = item_id
page = self.list(**params)

def create(self, idempotency_key=None, **params):
headers = populate_headers(idempotency_key)
return self.request('post', self['url'], params, headers)
Expand All @@ -327,6 +350,9 @@ def retrieve(self, id, **params):

return self.request('get', url, params)

def __iter__(self):
return getattr(self, 'data', []).__iter__()


class SingletonAPIResource(APIResource):

Expand All @@ -349,8 +375,20 @@ def instance_url(self):
class ListableAPIResource(APIResource):

@classmethod
def all(cls, api_key=None, idempotency_key=None,
stripe_account=None, **params):
def all(cls, *args, **params):
warnings.warn("The `all` class method is deprecated and will"
"be removed in future versions. Please use the "
"`list` class method instead",
DeprecationWarning)
return cls.list(*args, **params)

@classmethod
def auto_paging_iter(self, *args, **params):
return self.list(*args, **params).auto_paging_iter()

@classmethod
def list(cls, api_key=None, idempotency_key=None,
stripe_account=None, **params):
requestor = api_requestor.APIRequestor(api_key, account=stripe_account)
url = cls.class_url()
response, api_key = requestor.request('get', url, params)
Expand Down
18 changes: 7 additions & 11 deletions stripe/test/__init__.py
@@ -1,17 +1,13 @@
import pkgutil
import os
import unittest2


def all_names():
for _, modname, _ in pkgutil.iter_modules(__path__):
if modname.startswith('test_'):
yield 'stripe.test.' + modname


def all():
return unittest2.defaultTestLoader.loadTestsFromNames(all_names())
path = os.path.dirname(os.path.realpath(__file__))
return unittest2.defaultTestLoader.discover(path)


def unit():
unit_names = [name for name in all_names() if 'integration' not in name]
return unittest2.defaultTestLoader.loadTestsFromNames(unit_names)
def resources():
path = os.path.dirname(os.path.realpath(__file__))
return unittest2.defaultTestLoader.discover(
os.path.join(path, "resources"))
9 changes: 8 additions & 1 deletion stripe/test/helper.py
Expand Up @@ -142,7 +142,7 @@ def tearDown(self):
def assertRaisesRegexp(self, exception, regexp, callable, *args, **kwargs):
try:
callable(*args, **kwargs)
except exception, err:
except exception as err:
if regexp is None:
return True

Expand Down Expand Up @@ -200,6 +200,13 @@ def mock_response(self, res):
self.requestor_mock.request = Mock(return_value=(res, 'reskey'))


class StripeResourceTest(StripeApiTestCase):

def setUp(self):
super(StripeResourceTest, self).setUp()
self.mock_response({})


class MyResource(stripe.resource.APIResource):
pass

Expand Down
Empty file.
108 changes: 108 additions & 0 deletions stripe/test/resources/test_accounts.py
@@ -0,0 +1,108 @@
import stripe
from stripe.test.helper import StripeResourceTest


class AccountTest(StripeResourceTest):

def test_retrieve_account_deprecated(self):
stripe.Account.retrieve()

self.requestor_mock.request.assert_called_with(
'get',
'/v1/account',
{},
None
)

def test_retrieve_account(self):
stripe.Account.retrieve('acct_foo')
self.requestor_mock.request.assert_called_with(
'get',
'/v1/accounts/acct_foo',
{},
None
)

def test_list_accounts(self):
stripe.Account.all()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/accounts',
{}
)

def test_create_account(self):
pii = {
'type': 'individual',
'first_name': 'Joe',
'last_name': 'Smith',
}
stripe.Account.create(legal_entity=pii)
self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts',
{
'legal_entity': pii,
},
None,
)

def test_update_account(self):
acct = stripe.Account.construct_from({
'id': 'acct_update',
'legal_entity': {'first_name': 'Joe'},
}, 'api_key')
acct.legal_entity['first_name'] = 'Bob'
acct.save()

self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts/acct_update',
{
'legal_entity': {
'first_name': 'Bob',
},
},
None,
)

def test_account_delete_bank_account(self):
source = stripe.BankAccount.construct_from({
'account': 'acc_delete_ba',
'id': 'ba_delete_ba',
}, 'api_key')
source.delete()

self.requestor_mock.request.assert_called_with(
'delete',
'/v1/accounts/acc_delete_ba/external_accounts/ba_delete_ba',
{},
None
)

def test_verify_additional_owner(self):
acct = stripe.Account.construct_from({
'id': 'acct_update',
'additional_owners': [{
'first_name': 'Alice',
'verification': {},
}]
}, 'api_key')
owner = acct.additional_owners[0]
owner.verification.document = 'file_foo'
acct.save()

self.requestor_mock.request.assert_called_with(
'post',
'/v1/accounts/acct_update',
{
'additional_owners': {
'0': {
'verification': {
'document': 'file_foo',
},
},
},
},
None,
)
86 changes: 86 additions & 0 deletions stripe/test/resources/test_api_resource.py
@@ -0,0 +1,86 @@
import stripe
from stripe.test.helper import (
StripeApiTestCase, MyResource, MySingleton
)


class APIResourceTests(StripeApiTestCase):

def test_retrieve_and_refresh(self):
self.mock_response({
'id': 'foo2',
'bobble': 'scrobble',
})

res = MyResource.retrieve('foo*', myparam=5)

url = '/v1/myresources/foo%2A'
self.requestor_mock.request.assert_called_with(
'get', url, {'myparam': 5}, None
)

self.assertEqual('scrobble', res.bobble)
self.assertEqual('foo2', res.id)
self.assertEqual('reskey', res.api_key)

self.mock_response({
'frobble': 5,
})

res = res.refresh()

url = '/v1/myresources/foo2'
self.requestor_mock.request.assert_called_with(
'get', url, {'myparam': 5}, None
)

self.assertEqual(5, res.frobble)
self.assertRaises(KeyError, res.__getitem__, 'bobble')

def test_convert_to_stripe_object(self):
sample = {
'foo': 'bar',
'adict': {
'object': 'charge',
'id': 42,
'amount': 7,
},
'alist': [
{
'object': 'customer',
'name': 'chilango'
}
]
}

converted = stripe.resource.convert_to_stripe_object(
sample, 'akey', None)

# Types
self.assertTrue(isinstance(converted, stripe.resource.StripeObject))
self.assertTrue(isinstance(converted.adict, stripe.Charge))
self.assertEqual(1, len(converted.alist))
self.assertTrue(isinstance(converted.alist[0], stripe.Customer))

# Values
self.assertEqual('bar', converted.foo)
self.assertEqual(42, converted.adict.id)
self.assertEqual('chilango', converted.alist[0].name)

# Stripping
# TODO: We should probably be stripping out this property
# self.assertRaises(AttributeError, getattr, converted.adict, 'object')


class SingletonAPIResourceTests(StripeApiTestCase):

def test_retrieve(self):
self.mock_response({
'single': 'ton'
})
res = MySingleton.retrieve()

self.requestor_mock.request.assert_called_with(
'get', '/v1/mysingleton', {}, None)

self.assertEqual('ton', res.single)
72 changes: 72 additions & 0 deletions stripe/test/resources/test_application_fees.py
@@ -0,0 +1,72 @@
import stripe
from stripe.test.helper import StripeResourceTest


class ApplicationFeeTest(StripeResourceTest):

def test_list_application_fees(self):
stripe.ApplicationFee.all()
self.requestor_mock.request.assert_called_with(
'get',
'/v1/application_fees',
{}
)


class ApplicationFeeRefundTest(StripeResourceTest):

def test_fetch_refund(self):
fee = stripe.ApplicationFee.construct_from({
'id': 'fee_get_refund',
'refunds': {
'object': 'list',
'url': '/v1/application_fees/fee_get_refund/refunds',
}
}, 'api_key')

fee.refunds.retrieve("ref_get")

self.requestor_mock.request.assert_called_with(
'get',
'/v1/application_fees/fee_get_refund/refunds/ref_get',
{},
None
)

def test_list_refunds(self):
fee = stripe.ApplicationFee.construct_from({
'id': 'fee_get_refund',
'refunds': {
'object': 'list',
'url': '/v1/application_fees/fee_get_refund/refunds',
}
}, 'api_key')

fee.refunds.all()

self.requestor_mock.request.assert_called_with(
'get',
'/v1/application_fees/fee_get_refund/refunds',
{},
None
)

def test_update_refund(self):
refund = stripe.resource.ApplicationFeeRefund.construct_from({
'id': "ref_update",
'fee': "fee_update",
'metadata': {},
}, 'api_key')
refund.metadata["key"] = "value"
refund.save()

self.requestor_mock.request.assert_called_with(
'post',
'/v1/application_fees/fee_update/refunds/ref_update',
{
'metadata': {
'key': 'value',
}
},
None
)

0 comments on commit ad79162

Please sign in to comment.