Skip to content

Commit

Permalink
Adding support for Leads (previously known as Contacts).
Browse files Browse the repository at this point in the history
  • Loading branch information
jkeyes committed Mar 21, 2016
1 parent 26abaf0 commit e9a30ea
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 3 deletions.
13 changes: 13 additions & 0 deletions intercom/api_operations/convert.py
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-


class Convert(object):

def convert(self, contact, user):
self.client.post(
'/contacts/convert',
{
'contact': {'user_id': contact.user_id},
'user': self.identity_hash(user)
}
)
2 changes: 1 addition & 1 deletion intercom/api_operations/delete.py
Expand Up @@ -8,5 +8,5 @@ class Delete(object):
def delete(self, obj):
collection = utils.resource_class_to_collection_name(
self.collection_class)
self.client.delete("/%s/%s/" % (collection, obj.id), {})
self.client.delete("/%s/%s" % (collection, obj.id), {})
return obj
5 changes: 5 additions & 0 deletions intercom/client.py
Expand Up @@ -68,6 +68,11 @@ def users(self):
from intercom.service import user
return user.User(self)

@property
def leads(self):
from intercom.service import lead
return lead.Lead(self)

@property
def jobs(self):
from intercom.service import job
Expand Down
7 changes: 7 additions & 0 deletions intercom/collection_proxy.py
Expand Up @@ -2,6 +2,7 @@

import six
from intercom import HttpError
from intercom import utils


class CollectionProxy(six.Iterator):
Expand All @@ -12,6 +13,12 @@ def __init__(

self.client = client

# resource name
self.resource_name = utils.resource_class_to_collection_name(collection_cls)

# resource class
self.resource_class = collection_cls

# needed to create class instances of the resource
self.collection_cls = collection_cls

Expand Down
14 changes: 14 additions & 0 deletions intercom/lead.py
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-

from intercom.traits.api_resource import Resource


class Lead(Resource):

update_verb = 'put'
identity_vars = ['email', 'user_id']
collection_name = 'contacts'

@property
def flat_store_attributes(self):
return ['custom_attributes']
23 changes: 23 additions & 0 deletions intercom/service/lead.py
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*- # noqa

from intercom import lead
from intercom.api_operations.all import All
from intercom.api_operations.convert import Convert
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.service.base_service import BaseService


class Lead(BaseService, All, Find, FindAll, Delete, Save, Load, Convert):
"""Leads are useful for representing logged-out users of your application.
Ref: https://developers.intercom.io/reference#leads
"""

@property
def collection_class(self):
"""The collection class that represents this resource."""
return lead.Lead
2 changes: 1 addition & 1 deletion intercom/user.py
Expand Up @@ -7,7 +7,7 @@
class User(Resource, IncrementableAttributes):

update_verb = 'post'
identity_vars = ['email', 'user_id']
identity_vars = ['id', 'email', 'user_id']

@property
def flat_store_attributes(self):
Expand Down
2 changes: 2 additions & 0 deletions intercom/utils.py
Expand Up @@ -25,6 +25,8 @@ def constantize_singular_resource_name(resource_name):


def resource_class_to_collection_name(cls):
if hasattr(cls, 'collection_name'):
return cls.collection_name
return pluralize(cls.__name__.lower())


Expand Down
65 changes: 65 additions & 0 deletions tests/unit/test_lead.py
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*- # noqa

import mock
import unittest

from intercom.collection_proxy import CollectionProxy
from intercom.client import Client
from intercom.lead import Lead
from intercom.user import User
from mock import patch
from nose.tools import istest
from tests.unit import get_user


class LeadTest(unittest.TestCase): # noqa

def setUp(self): # noqa
self.client = Client()

@istest
def it_should_be_listable(self): # noqa
proxy = self.client.leads.all()
self.assertEquals('contacts', proxy.resource_name)
self.assertEquals('/contacts', proxy.finder_url)
self.assertEquals(Lead, proxy.resource_class)

@istest
def it_should_not_throw_errors_when_there_are_no_parameters(self): # noqa
with patch.object(Client, 'post') as mock_method: # noqa
self.client.leads.create()

@istest
def it_can_update_a_lead_with_an_id(self): # noqa
lead = Lead(id="de45ae78gae1289cb")
with patch.object(Client, 'put') as mock_method: # noqa
self.client.leads.save(lead)
mock_method.assert_called_once_with(
'/contacts/de45ae78gae1289cb', {'custom_attributes': {}})

@istest
def it_can_convert(self): # noqa
lead = Lead.from_api({'user_id': 'contact_id'})
user = User.from_api({'id': 'user_id'})

with patch.object(Client, 'post', returns=get_user()) as mock_method: # noqa
self.client.leads.convert(lead, user)
mock_method.assert_called_once_with(
'/contacts/convert',
{
'contact': {'user_id': lead.user_id},
'user': {'id': user.id}
})

@istest
def it_returns_a_collectionproxy_for_all_without_making_any_requests(self): # noqa
with mock.patch('intercom.request.Request.send_request_to_path', new_callable=mock.NonCallableMock): # noqa
res = self.client.leads.all()
self.assertIsInstance(res, CollectionProxy)

@istest
def it_deletes_a_contact(self): # noqa
lead = Lead(id="1")
with patch.object(Client, 'delete') as mock_method: # noqa
self.client.leads.delete(lead)
mock_method.assert_called_once_with('/contacts/1', {})
2 changes: 1 addition & 1 deletion tests/unit/test_user.py
Expand Up @@ -273,7 +273,7 @@ def it_deletes_a_user(self):
with patch.object(Client, 'delete', return_value={}) as mock_method:
user = self.client.users.delete(user)
eq_(user.id, "1")
mock_method.assert_called_once_with('/users/1/', {})
mock_method.assert_called_once_with('/users/1', {})

@istest
def it_can_use_user_create_for_convenience(self):
Expand Down

0 comments on commit e9a30ea

Please sign in to comment.