Skip to content

Commit

Permalink
Merge pull request #205 from recurly/api_version_2_6
Browse files Browse the repository at this point in the history
Bump version 2.5.0
  • Loading branch information
bhelx committed May 19, 2017
2 parents fabd88e + 9e96f18 commit d77ed48
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 12 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
## Unreleased

## Version 2.5.0 April 17, 2017

- Remove parsing of X-Records header
- Cardless Free Trial changes for 2.6

### Upgrade Notes

This release will upgrade us to API version 2.6. There are two breaking changes:

1. Since the X-Records header was removed in the pagination endpoint, you can no longer call `len()` on a Page and expect it to return a cached response.
From now on you need to explicitly call the `count()` class method on a Page. See [PR #202](https://github.com/recurly/recurly-client-python/pull/202) for more information.
2. For `POST /v2/subscriptions` Sending `None` for `total_billing_cycles` attribute will now override plan `total_billing_cycles` setting and will make subscription renew forever.
Omitting the attribute will cause the setting to default to the value of plan `total_billing_cycles`.

## Version 2.4.4 March 23, 2017

- Add API version 2.5 changes
Expand Down
6 changes: 4 additions & 2 deletions recurly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"""

__version__ = '2.4.4'
__version__ = '2.5.0'
__python_version__ = '.'.join(map(str, sys.version_info[:3]))

cached_rate_limits = {
Expand All @@ -42,7 +42,7 @@
API_KEY = None
"""The API key to use when authenticating API requests."""

API_VERSION = '2.5'
API_VERSION = '2.6'
"""The API version to use when making API requests."""

CA_CERTS_FILE = None
Expand Down Expand Up @@ -859,6 +859,7 @@ class Subscription(Resource):
'shipping_address_id',
'started_with_gift',
'converted_at',
'no_billing_info_reason',
)
sensitive_attributes = ('number', 'verification_value', 'bulk')

Expand Down Expand Up @@ -1094,6 +1095,7 @@ class Plan(Resource):
'total_billing_cycles',
'revenue_schedule_type',
'setup_fee_revenue_schedule_type',
'trial_requires_billing_info',
)

def get_add_on(self, add_on_code):
Expand Down
39 changes: 29 additions & 10 deletions recurly/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,6 @@ def __iter__(self):
pass
raise StopIteration

def __len__(self):
try:
if not self.record_size:
return 0
else:
return int(self.record_size)
except AttributeError:
return 0

def next_page(self):
"""Return the next `Page` after this one in the result sequence
it's from.
Expand Down Expand Up @@ -142,6 +133,12 @@ def page_for_url(cls, url):

return cls.page_for_value(resp, value)

@classmethod
def count_for_url(cls, url):
"""Return the count of server side resources given a url"""
headers = Resource.headers_for_url(url)
return int(headers['X-Records'])

@classmethod
def page_for_value(cls, resp, value):
"""Return a new `Page` representing the given resource `value`
Expand All @@ -153,7 +150,6 @@ def page_for_value(cls, resp, value):
"""
page = cls(value)
page.record_size = resp.getheader('X-Records')
links = parse_link_value(resp.getheader('Link'))
for url, data in six.iteritems(links):
if data.get('rel') == 'start':
Expand Down Expand Up @@ -346,6 +342,15 @@ def get(cls, uuid):
resp, elem = cls.element_for_url(url)
return cls.from_element(elem)

@classmethod
def headers_for_url(cls, url):
"""Return the headers only for the given URL as a dict"""
response = cls.http_request(url, method='HEAD')
if response.status != 200:
cls.raise_http_error(response)

return Resource.headers_as_dict(response)

@classmethod
def element_for_url(cls, url):
"""Return the resource at the given URL, as a
Expand Down Expand Up @@ -400,6 +405,10 @@ def value_for_element(cls, elem):

attr_type = elem.attrib.get('type')
log.debug("Converting %r element with type %r", elem.tag, attr_type)
# TODO this trial_requires_billing_info check can be removed when
# the server starts sending the correct type
if elem.tag == 'trial_requires_billing_info':
return elem.text.strip() == 'true'
if attr_type == 'integer':
return int(elem.text.strip())
if attr_type == 'float':
Expand Down Expand Up @@ -606,6 +615,16 @@ def all(cls, **kwargs):
url = '%s?%s' % (url, urlencode(kwargs))
return Page.page_for_url(url)

@classmethod
def count(cls, **kwargs):
"""Return a count of server side resources given
filtering arguments in kwargs.
"""
url = urljoin(recurly.base_uri(), cls.collection_path)
if kwargs:
url = '%s?%s' % (url, urlencode(kwargs))
return Page.count_for_url(url)

def save(self):
"""Save this `Resource` instance to the service.
Expand Down
9 changes: 9 additions & 0 deletions tests/fixtures/pages/count.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
HEAD https://api.recurly.com/v2/accounts?begin_time=2017-05-01T10%3A30%3A01-06%3A00 HTTP/1.1
X-Api-Version: {api-version}
Accept: application/xml
Authorization: Basic YXBpa2V5Og==
User-Agent: {user-agent}


HTTP/1.1 200 OK
X-Records: 23
1 change: 1 addition & 0 deletions tests/fixtures/plan/exists.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Content-Type: application/xml; charset=utf-8
<trial_interval_unit>days</trial_interval_unit>
<total_billing_cycles type="integer">10</total_billing_cycles>
<created_at type="datetime">2011-10-03T22:23:12Z</created_at>
<trial_requires_billing_info type="boolean">false</trial_requires_billing_info>
<unit_amount_in_cents>
</unit_amount_in_cents>
<setup_fee_in_cents>
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/subscription/show.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Content-Type: application/xml; charset=utf-8
<trial_ends_at nil="nil"></trial_ends_at>
<tax_in_cents type="integer">0</tax_in_cents>
<tax_type>usst</tax_type>
<no_billing_info_reason>plan_free_trial</no_billing_info_reason>
<subscription_add_ons type="array">
<subscription_add_on>
<add_on_type>usage</add_on_type>
Expand Down
9 changes: 9 additions & 0 deletions tests/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,15 @@ def test_build_invoice(self):
with self.mock_request('invoice/account-deleted.xml'):
account.delete()

def test_count(self):
try:
with self.mock_request('pages/count.xml'):
num_accounts = Account.count(begin_time='2017-05-01T10:30:01-06:00')

self.assertTrue(num_accounts, 23)
finally:
pass

def test_pages(self):
account_code = 'pages-%s-%%d' % self.test_id
all_test_accounts = list()
Expand Down

0 comments on commit d77ed48

Please sign in to comment.