Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add contacts from v2.0 #223

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
dist
venv
env
.venv
.coverage
.env
Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ sudo: false
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
install:
Expand Down
20 changes: 20 additions & 0 deletions intercom/api_operations/search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
"""Operation to search through contacts."""

from intercom import utils


class Search(object):
"""A mixin that provides `search` functionality."""

def search(self, query, **params):
"""Find all instances of the resource based on the supplied parameters."""
collection_name = utils.resource_class_to_collection_name(
self.collection_class)
finder_url = "/{}/scroll".format(collection_name)

response = self.client.post("/{}/search".format(collection_name), query)

collection_data = response['data']

return map(lambda item: self.collection_class(**item), collection_data)
5 changes: 5 additions & 0 deletions intercom/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ def tags(self):
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):
Expand Down
21 changes: 16 additions & 5 deletions intercom/collection_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_first_page(self):

def get_next_page(self):
# get the next page of results
return self.get_page(self.next_page)
return self.get_page(self.next_page, self.finder_params)

def get_page(self, url, params={}):
# get a page of results
Expand All @@ -82,22 +82,33 @@ def get_page(self, url, params={}):
if response is None:
raise HttpError('Http Error - No response entity returned')

collection = response[self.collection]
if self.collection in response:
collection = response[self.collection]
else:
collection = response['data']
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


# 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)
self.next_page = self.extract_next_link(url, response)

def paging_info_present(self, response):
return 'pages' in response and 'type' in response['pages']

def extract_next_link(self, response):
def extract_next_link(self, url, response):
if self.paging_info_present(response):
paging_info = response["pages"]
if paging_info["next"]:
if 'next' not in paging_info or paging_info['next'] == None:
return None
elif paging_info["next"] and (isinstance(paging_info['next'], six.text_type) or isinstance(paging_info['next'], str)):
next_parsed = six.moves.urllib.parse.urlparse(paging_info["next"])
return '{}?{}'.format(next_parsed.path, next_parsed.query)
else:
#cursor based pagination
return '{}?starting_after={}'.format(six.moves.urllib.parse.urlparse(url).path, paging_info['next']['starting_after'])


14 changes: 14 additions & 0 deletions intercom/contact.py
Original file line number Diff line number Diff line change
@@ -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', 'role']

@property
def flat_store_attributes(self):
return ['custom_attributes']
20 changes: 20 additions & 0 deletions intercom/service/contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- 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.search import Search
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


class Contact(BaseService, All, Find, FindAll, Delete, Save, Load, Submit, Tags, Search):

@property
def collection_class(self):
return contact.Contact
14 changes: 14 additions & 0 deletions tests/integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ def get_timestamp():
now = datetime.utcnow()
return int(time.mktime(now.timetuple()))

def get_or_create_contact(client, timestamp):
# get user
email = '%[email protected]' % (timestamp)
try:
user = client.contacts.find(email=email)
except ResourceNotFound:
# Create a user
contact = client.contacts.create(
email=email,
user_id=timestamp,
name="Ada %s" % (timestamp))
time.sleep(5)
return contact


def get_or_create_user(client, timestamp):
# get user
Expand Down
28 changes: 28 additions & 0 deletions tests/integration/test_contacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-

import os
import unittest
from intercom.client import Client
from . import get_timestamp
from . import get_or_create_contact

intercom = Client(
os.environ.get('INTERCOM_PERSONAL_ACCESS_TOKEN'))


class ContactsTest(unittest.TestCase):

@classmethod
def setup_class(cls):
nowstamp = get_timestamp()
cls.user = get_or_create_contact(intercom, nowstamp)
cls.email = cls.user.email

@classmethod
def teardown_class(cls):
delete_user(intercom, cls.user)

def test_find_by_email(self):
# Find user by email
contact = intercom.contacts.find(email=self.email)
self.assertEqual(self.email, contact.email)
143 changes: 142 additions & 1 deletion tests/unit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,128 @@ def get_user(email="[email protected]", name="Joe Schmoe"):
}
}

def get_contact(email="[email protected]", name="Joe Schmoe"):
return {
"type": "contact",
"id": "id-from-customers-app",
"workspace_id": "ecahpwf5",
"external_id": "the-app-id",
"role": "user",
"email": email,
"phone": "+1123456789",
"name": name,
"avatar": "https://example.org/128Wash.jpg",
"owner_id": 127,
"social_profiles": {
"type": "list",
"data": [
{
"type": "social_profile",
"name": "Twitter",
"url": "http://twitter.com/th1sland"
}
]
},
"has_hard_bounced": False,
"marked_email_as_spam": False,
"unsubscribed_from_emails": False,
"created_at": 1571672154,
"updated_at": 1571672158,
"signed_up_at": 1571069751,
"last_seen_at": 1571069751,
"last_replied_at": 1571672158,
"last_contacted_at": 1571672158,
"last_email_opened_at": 1571673478,
"last_email_clicked_at": 1571676789,
"language_override": None,
"browser": "chrome",
"browser_version": "77.0.3865.90",
"browser_language": "en",
"os": "OS X 10.14.6",
"location": {
"type": "location",
"country": "Ireland",
"region": "Dublin",
"city": "Dublin"
},
"android_app_name": None,
"android_app_version": None,
"android_device": None,
"android_os_version": None,
"android_sdk_version": None,
"android_last_seen_at": None,
"ios_app_name": None,
"ios_app_version": None,
"ios_device": None,
"ios_os_version": None,
"ios_sdk_version": None,
"ios_last_seen_at": None,
"custom_attributes": {
"paid_subscriber": True,
"monthly_spend": 155.5,
"team_mates": 1
},
"tags": {
"type": "list",
"data": [
{
"type": "tag",
"id": "2",
"url": "/tags/2"
},
{
"type": "tag",
"id": "4",
"url": "/tags/4"
},
{
"type": "tag",
"id": "5",
"url": "/tags/5"
}
],
"url": "/contacts/5ba682d23d7cf92bef87bfd4/tags",
"total_count": 3,
"has_more": False
},
"notes": {
"type": "list",
"data": [
{
"type": "note",
"id": "20114858",
"url": "/notes/20114858"
}
],
"url": "/contacts/5ba682d23d7cf92bef87bfd4/notes",
"total_count": 1,
"has_more": False
},
"companies": {
"type": "list",
"data": [
{
"type": "company",
"id": "5ba686093d7cf93552a3dc99",
"url": "/companies/5ba686093d7cf93552a3dc99"

},
{
"type": "company",
"id": "5cee64a03d7cf90c51b36f19",
"url": "/companies/5cee64a03d7cf90c51b36f19"
},
{
"type": "company",
"id": "5d7668883d7cf944dbc5c791",
"url": "/companies/5d7668883d7cf944dbc5c791"
}
],
"url": "/contacts/5ba682d23d7cf92bef87bfd4/companies",
"total_count": 3,
"has_more": False
}
}

def get_company(name):
return {
Expand All @@ -160,7 +282,6 @@ def get_company(name):
}
}


def get_event(name="the-event-name"):
return {
"type": "event",
Expand Down Expand Up @@ -195,6 +316,26 @@ def page_of_users(include_next_link=False):
page["pages"]["next"] = "https://api.intercom.io/users?per_page=50&page=2"
return page

def page_of_contacts(include_next_link=False):
page = {
"type": "contacts.list",
"pages": {
"type": "pages",
"page": 1,
"next": None,
"per_page": 50,
"total_pages": 7
},
"users": [
get_contact("[email protected]"),
get_contact("[email protected]"),
get_contact("[email protected]")],
"total_count": 314
}
if include_next_link:
page["pages"]["next"] = "https://api.intercom.io/contacts?per_page=50&page=2"
return page


def users_scroll(include_users=False): # noqa
# a "page" of results from the Scroll API
Expand Down
Loading