From 75688bac93d4a146d538b96a79c84fafff96e58b Mon Sep 17 00:00:00 2001 From: Kacper Date: Fri, 19 Jul 2019 10:21:49 +0200 Subject: [PATCH 1/9] Added scroll for companies --- intercom/service/company.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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): From 15d66743be0fdcef9aab08a4f0f784a120e1e1d3 Mon Sep 17 00:00:00 2001 From: Ryan Stocker Date: Fri, 28 Feb 2020 12:21:44 -0800 Subject: [PATCH 2/9] wip --- intercom/__init__.py | 2 +- intercom/api_operations/all.py | 4 +- intercom/client.py | 5 +++ intercom/collection_proxy.py | 9 +++- intercom/contact.py | 14 ++++++ intercom/cursor_collection_proxy.py | 69 +++++++++++++++++++++++++++++ intercom/service/contact.py | 21 +++++++++ 7 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 intercom/contact.py create mode 100644 intercom/cursor_collection_proxy.py create mode 100644 intercom/service/contact.py diff --git a/intercom/__init__.py b/intercom/__init__.py index 23824dc6..fb79e7b8 100644 --- a/intercom/__init__.py +++ b/intercom/__init__.py @@ -6,7 +6,7 @@ MultipleMatchingUsersError, RateLimitExceeded, ResourceNotFound, ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError) -__version__ = '3.1.0' +__version__ = '3.1.0.RYAN' 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..4a34075d --- /dev/null +++ b/intercom/cursor_collection_proxy.py @@ -0,0 +1,69 @@ +# -*- 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 + + import pdb; pdb.set_trace() + 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/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 From d9964bddae8e98de18364b17282e860267bfcc4e Mon Sep 17 00:00:00 2001 From: Ryan Stocker Date: Fri, 28 Feb 2020 17:31:04 -0800 Subject: [PATCH 3/9] wip --- intercom/cursor_collection_proxy.py | 1 - intercom/scroll_collection_proxy.py | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/intercom/cursor_collection_proxy.py b/intercom/cursor_collection_proxy.py index 4a34075d..808c39cb 100644 --- a/intercom/cursor_collection_proxy.py +++ b/intercom/cursor_collection_proxy.py @@ -18,7 +18,6 @@ def get_page(self, url, params={}): if url is None: raise StopIteration - import pdb; pdb.set_trace() if "sort" in params or "query" in params: url = f"{url}/search" if self.paging_info and "next" in self.paging_info: 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.""" From 0e1c4c4fbc7857b5339cbb432f6e39aaaa54452c Mon Sep 17 00:00:00 2001 From: Eugene Prikazchikov Date: Tue, 4 Jun 2024 11:20:41 +0300 Subject: [PATCH 4/9] Do not attempt to JSON deseralize objects of type None --- intercom/traits/api_resource.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/intercom/traits/api_resource.py b/intercom/traits/api_resource.py index af929846..88a1f693 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 = value.get('type') is None + return has_type and not is_type_none def datetime_value(value): From ed9bf129c30b23e4c9bbc299d93fa24f20fa6dbe Mon Sep 17 00:00:00 2001 From: rstocker99 Date: Wed, 5 Jun 2024 11:09:24 -0700 Subject: [PATCH 5/9] Make version pip 24.1 compatible --- intercom/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/intercom/__init__.py b/intercom/__init__.py index fb79e7b8..c90cb872 100644 --- a/intercom/__init__.py +++ b/intercom/__init__.py @@ -6,6 +6,7 @@ MultipleMatchingUsersError, RateLimitExceeded, ResourceNotFound, ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError) +# Was __version__ = '3.1.0.RYAN' but pip doens't like that __version__ = '3.1.0.RYAN' From fd0ed0d155b6eda37ef07cc85cb016637b355319 Mon Sep 17 00:00:00 2001 From: rstocker99 Date: Wed, 5 Jun 2024 11:11:50 -0700 Subject: [PATCH 6/9] Make version number compatible with pip 24.1 --- intercom/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intercom/__init__.py b/intercom/__init__.py index c90cb872..b1edec4f 100644 --- a/intercom/__init__.py +++ b/intercom/__init__.py @@ -7,7 +7,7 @@ ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError) # Was __version__ = '3.1.0.RYAN' but pip doens't like that -__version__ = '3.1.0.RYAN' +__version__ = '3.1.1.1' RELATED_DOCS_TEXT = "See https://github.com/jkeyes/python-intercom \ From 3fd4268fd6331b2e6b1f958ea629766ef4464bd9 Mon Sep 17 00:00:00 2001 From: rstocker99 Date: Wed, 5 Jun 2024 11:22:17 -0700 Subject: [PATCH 7/9] Wheel building does like having __version__ even in a comment --- intercom/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intercom/__init__.py b/intercom/__init__.py index b1edec4f..a4da5eef 100644 --- a/intercom/__init__.py +++ b/intercom/__init__.py @@ -6,7 +6,7 @@ MultipleMatchingUsersError, RateLimitExceeded, ResourceNotFound, ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError) -# Was __version__ = '3.1.0.RYAN' but pip doens't like that +# Version was '3.1.0.RYAN' but pip doens't like that __version__ = '3.1.1.1' From bc97d5db8e55da33283dc1f6aec61158cb48d311 Mon Sep 17 00:00:00 2001 From: rstocker99 Date: Wed, 5 Jun 2024 11:43:15 -0700 Subject: [PATCH 8/9] Fix AttributeError: 'str' object has no attribute 'get' --- intercom/traits/api_resource.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intercom/traits/api_resource.py b/intercom/traits/api_resource.py index 88a1f693..6271fcc3 100644 --- a/intercom/traits/api_resource.py +++ b/intercom/traits/api_resource.py @@ -23,7 +23,7 @@ def custom_attribute_field(attribute): def typed_value(value): has_type = hasattr(value, 'keys') and 'type' in value - is_type_none = value.get('type') is None + is_type_none = has_type and value.get('type') is None return has_type and not is_type_none From c6263cd2b7b0b2272283d896225bc9695eeaeb29 Mon Sep 17 00:00:00 2001 From: rstocker99 Date: Wed, 5 Jun 2024 11:47:54 -0700 Subject: [PATCH 9/9] Bump version for bug fix in typed_value --- intercom/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intercom/__init__.py b/intercom/__init__.py index a4da5eef..eb0e5dc0 100644 --- a/intercom/__init__.py +++ b/intercom/__init__.py @@ -7,7 +7,7 @@ ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError) # Version was '3.1.0.RYAN' but pip doens't like that -__version__ = '3.1.1.1' +__version__ = '3.1.1.2' RELATED_DOCS_TEXT = "See https://github.com/jkeyes/python-intercom \