diff --git a/intercom/__init__.py b/intercom/__init__.py index 23824dc6..eb0e5dc0 100644 --- a/intercom/__init__.py +++ b/intercom/__init__.py @@ -6,7 +6,8 @@ MultipleMatchingUsersError, RateLimitExceeded, ResourceNotFound, ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError) -__version__ = '3.1.0' +# Version was '3.1.0.RYAN' but pip doens't like that +__version__ = '3.1.1.2' RELATED_DOCS_TEXT = "See https://github.com/jkeyes/python-intercom \ diff --git a/intercom/api_operations/all.py b/intercom/api_operations/all.py index ec571385..437e4957 100644 --- a/intercom/api_operations/all.py +++ b/intercom/api_operations/all.py @@ -8,10 +8,12 @@ class All(object): """A mixin that provides `all` functionality.""" + proxy_class = CollectionProxy + def all(self): """Return a CollectionProxy for the resource.""" collection = utils.resource_class_to_collection_name( self.collection_class) finder_url = "/%s" % (collection) - return CollectionProxy( + return self.proxy_class( self.client, self.collection_class, collection, finder_url) diff --git a/intercom/client.py b/intercom/client.py index ce37617b..7134102d 100644 --- a/intercom/client.py +++ b/intercom/client.py @@ -70,6 +70,11 @@ def users(self): from intercom.service import user return user.User(self) + @property + def contacts(self): + from intercom.service import contact + return contact.Contact(self) + @property def leads(self): from intercom.service import lead diff --git a/intercom/collection_proxy.py b/intercom/collection_proxy.py index 2b419f30..89b7079b 100644 --- a/intercom/collection_proxy.py +++ b/intercom/collection_proxy.py @@ -79,10 +79,17 @@ def get_page(self, url, params={}): raise StopIteration response = self.client.get(url, params) + if response is None: raise HttpError('Http Error - No response entity returned') - collection = response[self.collection] + # HACK: dealing with the fact that in unstable /tags is returning + # {'type': 'list', 'data': []} instead of + # {'type': 'tags.list', 'tags': []} + if response['type'] == 'list': + collection = response['data'] + else: + collection = response[self.collection] # if there are no resources in the response stop iterating if collection is None: raise StopIteration diff --git a/intercom/contact.py b/intercom/contact.py new file mode 100644 index 00000000..154eaad8 --- /dev/null +++ b/intercom/contact.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +from intercom.traits.api_resource import Resource +from intercom.traits.incrementable_attributes import IncrementableAttributes + + +class Contact(Resource, IncrementableAttributes): + + update_verb = 'post' + identity_vars = ['id', 'email', 'user_id'] + + @property + def flat_store_attributes(self): + return ['custom_attributes'] diff --git a/intercom/cursor_collection_proxy.py b/intercom/cursor_collection_proxy.py new file mode 100644 index 00000000..808c39cb --- /dev/null +++ b/intercom/cursor_collection_proxy.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +import six +from intercom import HttpError +from intercom import utils +from intercom.collection_proxy import CollectionProxy + +class CursorCollectionProxy(CollectionProxy): + def __init__(self, client, collection_cls, collection, finder_url, finder_params={}): + super(CursorCollectionProxy, self).__init__(client, collection_cls, collection,finder_url, finder_params=finder_params) + self.paging_info = None + + def get_page(self, url, params={}): + # get a page of results + # from intercom import Intercom + + # if there is no url stop iterating + if url is None: + raise StopIteration + + if "sort" in params or "query" in params: + url = f"{url}/search" + if self.paging_info and "next" in self.paging_info: + params["pagination"] = { + "per_page": self.paging_info["per_page"], + "starting_after": self.paging_info["next"]["starting_after"] + } + response = self.client.post(url, params) + else: + response = self.client.get(url, params) + + if response is None: + raise HttpError('Http Error - No response entity returned') + + # HACK: dealing with the fact that in unstable /tags is returning + # {'type': 'list', 'data': []} instead of + # {'type': 'tags.list', 'tags': []} + if response['type'] == 'list': + collection = response['data'] + else: + collection = response[self.collection] + # if there are no resources in the response stop iterating + if collection is None: + raise StopIteration + + # create the resource iterator + self.resources = iter(collection) + # grab the next page URL if one exists + self.next_page = self.extract_next_link(response) + + def get_next_page(self): + # get the next page of results + return self.get_page(self.next_page, self.finder_params) + + def extract_next_link(self, response): + if self.paging_info_present(response): + self.paging_info = response["pages"] + print(self.paging_info) + if "next" in self.paging_info: + # '/users?per_page=50&page=2' + # {'page': 2, 'starting_after': 'WzE1NzkxODQzNDEwMDAsIjVlMTZmOTg3MWEzNmFiMTFjMDY2YmMzZSIsMl0='} + if self.finder_params: + return '/{}'.format(self.collection) + else: + return '/{}?page={}&starting_after={}'.format( + self.collection, + self.paging_info["next"]["page"], + self.paging_info["next"]["starting_after"]) diff --git a/intercom/scroll_collection_proxy.py b/intercom/scroll_collection_proxy.py index c789f835..cdd05e0f 100644 --- a/intercom/scroll_collection_proxy.py +++ b/intercom/scroll_collection_proxy.py @@ -76,14 +76,26 @@ def get_page(self, scroll_param=None): raise HttpError('Http Error - No response entity returned') # create the resource iterator - collection = response[self.resource_name] + + # HACK: dealing with the fact that in unstable /tags is returning + # {'type': 'list', 'data': []} instead of + # {'type': 'tags.list', 'tags': []} + if response['type'] == 'list': + collection = response['data'] + else: + collection = response[self.resource_name] + self.resources = iter(collection) # grab the next page URL if one exists self.scroll_param = self.extract_scroll_param(response) def records_present(self, response): """Return whether there are resources in the response.""" - return len(response.get(self.resource_name)) > 0 + if response['type'] == 'list': + collection = response['data'] + else: + collection = response[self.resource_name] + return len(collection) > 0 def extract_scroll_param(self, response): """Extract the scroll_param from the response.""" diff --git a/intercom/service/company.py b/intercom/service/company.py index 446062ad..7b911898 100644 --- a/intercom/service/company.py +++ b/intercom/service/company.py @@ -7,12 +7,13 @@ from intercom.api_operations.find_all import FindAll from intercom.api_operations.save import Save from intercom.api_operations.load import Load +from intercom.api_operations.scroll import Scroll from intercom.extended_api_operations.users import Users from intercom.extended_api_operations.tags import Tags from intercom.service.base_service import BaseService -class Company(BaseService, All, Delete, Find, FindAll, Save, Load, Users, Tags): +class Company(BaseService, All, Delete, Find, FindAll, Save, Load, Users, Tags, Scroll): @property def collection_class(self): diff --git a/intercom/service/contact.py b/intercom/service/contact.py new file mode 100644 index 00000000..9fa081e5 --- /dev/null +++ b/intercom/service/contact.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from intercom import contact +from intercom.api_operations.all import All +from intercom.api_operations.bulk import Submit +from intercom.api_operations.find import Find +from intercom.api_operations.find_all import FindAll +from intercom.api_operations.delete import Delete +from intercom.api_operations.save import Save +from intercom.api_operations.load import Load +from intercom.extended_api_operations.tags import Tags +from intercom.service.base_service import BaseService +from intercom.cursor_collection_proxy import CursorCollectionProxy + +class Contact(BaseService, All, Find, FindAll, Delete, Save, Load, Submit, Tags): + + proxy_class = CursorCollectionProxy + + @property + def collection_class(self): + return contact.Contact diff --git a/intercom/traits/api_resource.py b/intercom/traits/api_resource.py index af929846..6271fcc3 100644 --- a/intercom/traits/api_resource.py +++ b/intercom/traits/api_resource.py @@ -22,7 +22,9 @@ def custom_attribute_field(attribute): def typed_value(value): - return hasattr(value, 'keys') and 'type' in value + has_type = hasattr(value, 'keys') and 'type' in value + is_type_none = has_type and value.get('type') is None + return has_type and not is_type_none def datetime_value(value):