From 714de5a97b03a204f9768c04a9fb8efd7bfcc7ca Mon Sep 17 00:00:00 2001 From: John Keyes Date: Tue, 31 Mar 2015 23:10:11 +0100 Subject: [PATCH 1/5] Modifying parse_body so local var body is always in scope. #72 --- intercom/request.py | 6 +++--- tests/integration/issues/__init__.py | 1 + tests/integration/issues/test_72.py | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 tests/integration/issues/__init__.py create mode 100644 tests/integration/issues/test_72.py diff --git a/intercom/request.py b/intercom/request.py index 6302c39c..b6fdd26e 100644 --- a/intercom/request.py +++ b/intercom/request.py @@ -43,11 +43,11 @@ def send_request_to_path(cls, method, url, auth, params=None): def parse_body(cls, resp): try: body = json.loads(resp.content.decode()) + if body.get('type') == 'error.list': + cls.raise_application_errors_on_failure(body, resp.status_code) + return body except ValueError: cls.raise_errors_on_failure(resp) - if body.get('type') == 'error.list': - cls.raise_application_errors_on_failure(body, resp.status_code) - return body @classmethod def set_rate_limit_details(cls, resp): diff --git a/tests/integration/issues/__init__.py b/tests/integration/issues/__init__.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/tests/integration/issues/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/integration/issues/test_72.py b/tests/integration/issues/test_72.py new file mode 100644 index 00000000..cd6c43f7 --- /dev/null +++ b/tests/integration/issues/test_72.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +import os +import unittest +from intercom import Intercom +from intercom import User + +Intercom.app_id = os.environ.get('INTERCOM_APP_ID') +Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') + + +class Issue72Test(unittest.TestCase): + + def test(self): + User.create(email='me@example.com') From e5bfa3453099d8a8e4ad3173f86e29c5f56e0a48 Mon Sep 17 00:00:00 2001 From: John Keyes Date: Tue, 31 Mar 2015 23:40:23 +0100 Subject: [PATCH 2/5] Recording fix for UnboundLocal. --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e83f17f7..15358022 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,8 @@ Changelog ========= +* 2.0.beta (not yet released) + * Fixed `UnboundLocalError` in `Request.parse_body`. * 2.0.alpha * support for Intercom API v2 * support for Python 3 From d98f10aafc380de33bcb713c801435558a012e09 Mon Sep 17 00:00:00 2001 From: John Keyes Date: Wed, 1 Apr 2015 17:04:51 +0100 Subject: [PATCH 3/5] Handling 202 empty body responses gracefully. --- intercom/api_operations/save.py | 3 ++- intercom/request.py | 5 ++++- tests/integration/issues/test_72.py | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/intercom/api_operations/save.py b/intercom/api_operations/save.py index ba00181a..33d6bdd7 100644 --- a/intercom/api_operations/save.py +++ b/intercom/api_operations/save.py @@ -10,7 +10,8 @@ def create(cls, **params): from intercom import Intercom collection = utils.resource_class_to_collection_name(cls) response = Intercom.post("/%s/" % (collection), **params) - return cls(**response) + if response: # may be empty if we received a 202 + return cls(**response) def from_dict(self, pdict): for key, value in list(pdict.items()): diff --git a/intercom/request.py b/intercom/request.py index b6fdd26e..cd15e942 100644 --- a/intercom/request.py +++ b/intercom/request.py @@ -42,7 +42,10 @@ def send_request_to_path(cls, method, url, auth, params=None): @classmethod def parse_body(cls, resp): try: - body = json.loads(resp.content.decode()) + decoded_body = resp.content.decode() + if not decoded_body: # return early for empty responses (issue-72) + return + body = json.loads(decoded_body) if body.get('type') == 'error.list': cls.raise_application_errors_on_failure(body, resp.status_code) return body diff --git a/tests/integration/issues/test_72.py b/tests/integration/issues/test_72.py index cd6c43f7..c70bb6b0 100644 --- a/tests/integration/issues/test_72.py +++ b/tests/integration/issues/test_72.py @@ -2,7 +2,9 @@ import os import unittest +import time from intercom import Intercom +from intercom import Event from intercom import User Intercom.app_id = os.environ.get('INTERCOM_APP_ID') @@ -13,3 +15,11 @@ class Issue72Test(unittest.TestCase): def test(self): User.create(email='me@example.com') + # no exception here as empty response expected + data = { + 'event_name': 'Eventful 1', + 'created_at': int(time.time()), + 'email': 'me@example.com' + } + print data + Event.create(**data) From bc385570d07eb79f03ceee413a5f5eabbb464671 Mon Sep 17 00:00:00 2001 From: John Keyes Date: Wed, 1 Apr 2015 22:50:10 +0100 Subject: [PATCH 4/5] Mocking out the event tests to check the call parameters. --- tests/unit/test_event.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/tests/unit/test_event.py b/tests/unit/test_event.py index 60046bb7..88ae1ed3 100644 --- a/tests/unit/test_event.py +++ b/tests/unit/test_event.py @@ -1,21 +1,16 @@ # -*- coding: utf-8 -*- import httpretty -import json -import re import time import unittest from datetime import datetime from intercom import User +from intercom import Intercom from intercom import Event -from nose.tools import eq_ -from nose.tools import ok_ +from mock import patch from nose.tools import istest -post = httpretty.POST -r = re.compile - class EventTest(unittest.TestCase): @@ -29,7 +24,6 @@ def setUp(self): # noqa self.created_time = now - 300 @istest - @httpretty.activate def it_creates_an_event_with_metadata(self): data = { 'event_name': 'Eventful 1', @@ -41,13 +35,10 @@ def it_creates_an_event_with_metadata(self): 'found_date': 12909364407 } } - httpretty.register_uri( - post, r(r'/events/$'), body=json.dumps(data), status=202) - event = Event.create(**data) - eq_('Eventful 1', event.event_name) - ok_(hasattr(event, 'metadata')) - eq_('pi@example.com', event.metadata['invitee_email']) + with patch.object(Intercom, 'post', return_value=data) as mock_method: + Event.create(**data) + mock_method.assert_called_once_with('/events/', **data) @istest @httpretty.activate @@ -56,9 +47,6 @@ def it_creates_an_event_without_metadata(self): 'event_name': 'sale of item', 'email': 'joe@example.com', } - httpretty.register_uri( - post, r(r'/events/$'), body=json.dumps(data), status=202) - event = Event.create(**data) - - eq_('sale of item', event.event_name) - ok_(not hasattr(event, 'metadata')) + with patch.object(Intercom, 'post', return_value=data) as mock_method: + Event.create(**data) + mock_method.assert_called_once_with('/events/', **data) From 18a312adf5a05e63552c0d1ef7d97577500d8116 Mon Sep 17 00:00:00 2001 From: John Keyes Date: Wed, 1 Apr 2015 23:03:13 +0100 Subject: [PATCH 5/5] Adding entry for empty body support to CHANGES. --- CHANGES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 15358022..1a18e801 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,7 +3,8 @@ Changelog ========= * 2.0.beta (not yet released) - * Fixed `UnboundLocalError` in `Request.parse_body`. + * fixed `UnboundLocalError` in `Request.parse_body`. + * added support for replies with an empty body. * 2.0.alpha * support for Intercom API v2 * support for Python 3