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

Intercom API v2.5 #240

Open
wants to merge 8 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
13 changes: 6 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
sudo: false
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
install:
- 3.7
- 3.8
- 3.9
- 3.10
install:
- pip install -r requirements.txt
- pip install -r dev-requirements.txt
script:
- nosetests --with-coverag tests/unit
after_success:
coveralls --verbose
after_success: coveralls --verbose
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,7 @@ or the more specific error subclass:
BadRequestError
RateLimitExceeded
MultipleMatchingUsersError
MultipleMatchingContactsError
HttpError
UnexpectedError

Expand Down
35 changes: 26 additions & 9 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
#
# Development dependencies.
#
nose==1.3.4
mock==1.0.1
coveralls==0.5
coverage==3.7.1
sphinx==1.4.8
sphinx-rtd-theme==0.1.9
alabaster==0.7.12; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
babel==2.10.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
colorama==0.4.5; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6") or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.5.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
coverage==6.5.0; python_version >= "3.7"
coveralls==3.3.1; python_version >= "3.5"
docopt==0.6.2; python_version >= "3.5"
docutils==0.17.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
imagesize==1.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
importlib-metadata==4.12.0; python_version < "3.10" and python_version >= "3.7" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
jinja2==3.1.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
markupsafe==2.1.1; python_version >= "3.7"
mock==4.0.3; python_version >= "3.6"
nose==1.3.7
packaging==21.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pygments==2.13.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pyparsing==3.0.9; python_full_version >= "3.6.8" and python_version >= "3.6"
snowballstemmer==2.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
sphinx-rtd-theme==1.0.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
sphinx==5.2.2; python_version >= "3.6"
sphinxcontrib-applehelp==1.0.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
sphinxcontrib-devhelp==1.0.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
sphinxcontrib-htmlhelp==2.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
sphinxcontrib-jsmath==1.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
sphinxcontrib-qthelp==1.0.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
sphinxcontrib-serializinghtml==1.1.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
zipp==3.8.1; python_version < "3.10" and python_version >= "3.7"
22 changes: 13 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import os
import sys

import sphinx_rtd_theme
import sys, os

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
Expand Down Expand Up @@ -44,10 +46,11 @@
master_doc = 'index'

# General information about the project.
project = u'python-intercom'
project = 'python-intercom'
from datetime import datetime

now = datetime.now()
copyright = u'%s, John Keyes' % (now.year)
copyright = f"{now.year}, John Keyes"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand All @@ -56,6 +59,7 @@
# The short X.Y version.

import re

with open(os.path.join(path_dir, 'intercom', '__init__.py')) as init:
source = init.read()
m = re.search("__version__ = '(.*)'", source, re.M)
Expand Down Expand Up @@ -200,8 +204,8 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'python-intercom.tex', u'python-intercom Documentation',
u'John Keyes', 'manual'),
('index', 'python-intercom.tex', 'python-intercom Documentation',
'John Keyes', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -230,8 +234,8 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'python-intercom', u'python-intercom Documentation',
[u'John Keyes'], 1)
('index', 'python-intercom', 'python-intercom Documentation',
['John Keyes'], 1)
]

# If true, show URL addresses after external links.
Expand All @@ -244,8 +248,8 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'python-intercom', u'python-intercom Documentation',
u'John Keyes', 'python-intercom', 'One line description of project.',
('index', 'python-intercom', 'python-intercom Documentation',
'John Keyes', 'python-intercom', 'One line description of project.',
'Miscellaneous'),
]

Expand Down
16 changes: 9 additions & 7 deletions intercom/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
# -*- coding: utf-8 -*-

# from datetime import datetime
from .errors import (ArgumentError, AuthenticationError, # noqa
BadGatewayError, BadRequestError, HttpError, IntercomError,
MultipleMatchingUsersError, RateLimitExceeded, ResourceNotFound,
ServerError, ServiceUnavailableError, UnexpectedError, TokenUnauthorizedError)
from .errors import (ArgumentError, AuthenticationError, BadGatewayError,
BadRequestError, HttpError, IntercomError,
MultipleMatchingContactsError, MultipleMatchingUsersError,
RateLimitExceeded, ResourceNotFound, ServerError,
ServiceUnavailableError, TokenUnauthorizedError,
UnexpectedError)

__version__ = '3.1.0'
__version__ = '4.0.0'


RELATED_DOCS_TEXT = "See https://github.com/jkeyes/python-intercom \
for usage examples."
COMPATIBILITY_WARNING_TEXT = "It looks like you are upgrading from \
COMPATIBILITY_WARNING_TEXT = f"It looks like you are upgrading from \
an older version of python-intercom. Please note that this new version \
(%s) is not backwards compatible." % (__version__)
({__version__}) is not backwards compatible."
COMPATIBILITY_WORKAROUND_TEXT = "To get rid of this error please set \
Intercom.app_api_key and don't set Intercom.api_key."
CONFIGURATION_REQUIRED_TEXT = "You must set both Intercom.app_id and \
Expand Down
2 changes: 1 addition & 1 deletion intercom/api_operations/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ def all(self):
"""Return a CollectionProxy for the resource."""
collection = utils.resource_class_to_collection_name(
self.collection_class)
finder_url = "/%s" % (collection)
finder_url = f"/{collection}"
return CollectionProxy(
self.client, self.collection_class, collection, finder_url)
4 changes: 2 additions & 2 deletions intercom/api_operations/bulk.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def submit_bulk_job(self, create_items=[], delete_items=[], job_id=None):
if job_id:
bulk_request['job'] = {'id': job_id}

response = self.client.post('/bulk/%s' % (collection_name), bulk_request)
response = self.client.post(f"/bulk/{collection_name}", bulk_request)
if not response:
raise HttpError('HTTP Error - No response entity returned.')
return Job().from_response(response)
Expand All @@ -51,7 +51,7 @@ def errors(self, id):
"""Return errors for the Bulk API job specified."""
from intercom.errors import HttpError
from intercom.job import Job
response = self.client.get("/jobs/%s/error" % (id), {})
response = self.client.get(f"/jobs/{id}/error", {})
if not response:
raise HttpError('Http Error - No response entity returned.')
return Job.from_api(response)
2 changes: 1 addition & 1 deletion intercom/api_operations/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ def delete(self, obj):
"""Delete the specified instance of this resource."""
collection = utils.resource_class_to_collection_name(
self.collection_class)
self.client.delete("/%s/%s" % (collection, obj.id), {})
self.client.delete(f"/{collection}/{obj.id}", {})
return obj
8 changes: 3 additions & 5 deletions intercom/api_operations/find.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""Operation to find an instance of a particular resource."""

from intercom import HttpError
from intercom import utils
from intercom import HttpError, utils


class Find(object):
Expand All @@ -13,10 +12,9 @@ def find(self, **params):
collection = utils.resource_class_to_collection_name(
self.collection_class)
if 'id' in params:
response = self.client.get(
"/%s/%s" % (collection, params['id']), {})
response = self.client.get(f"/{collection}/{params['id']}", {})
else:
response = self.client.get("/%s" % (collection), params)
response = self.client.get(f"/{collection}", params)

if response is None:
raise HttpError('Http Error - No response entity returned')
Expand Down
4 changes: 2 additions & 2 deletions intercom/api_operations/find_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ def find_all(self, **params):
collection = utils.resource_class_to_collection_name(
self.collection_class)
if 'id' in params and 'type' not in params:
finder_url = "/%s/%s" % (collection, params['id'])
finder_url = f"/{collection}/{params['id']}"
else:
finder_url = "/%s" % (collection)
finder_url = f"/{collection}"
finder_params = params
return self.proxy_class(
self.client, self.collection_class, collection,
Expand Down
9 changes: 4 additions & 5 deletions intercom/api_operations/load.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""Operation to load an instance of a particular resource."""

from intercom import HttpError
from intercom import utils
from intercom import HttpError, utils


class Load(object):
Expand All @@ -13,11 +12,11 @@ def load(self, resource):
collection = utils.resource_class_to_collection_name(
self.collection_class)
if hasattr(resource, 'id'):
response = self.client.get("/%s/%s" % (collection, resource.id), {}) # noqa
response = self.client.get(f"/{collection}/{resource.id}", {}) # noqa
else:
raise Exception(
"Cannot load %s as it does not have a valid id." % (
self.collection_class))
f"Cannot load {self.collection_class} as it does not have a valid id."
)

if response is None:
raise HttpError('Http Error - No response entity returned')
Expand Down
15 changes: 10 additions & 5 deletions intercom/api_operations/save.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
"""Operation to create or save an instance of a particular resource."""

from tkinter import E

from intercom import utils


Expand All @@ -11,23 +13,26 @@ def create(self, **params):
"""Create an instance of the resource from the supplied parameters."""
collection = utils.resource_class_to_collection_name(
self.collection_class)
response = self.client.post("/%s/" % (collection), params)
response = self.client.post(f"/{collection}/", params)
if response: # may be empty if we received a 202
return self.collection_class(**response)

def save(self, obj):
"""Save the instance of the resource."""
collection = utils.resource_class_to_collection_name(
obj.__class__)
obj.__class__
)
params = obj.attributes
if self.id_present(obj) and not self.posted_updates(obj):
# update
response = self.client.put('/%s/%s' % (collection, obj.id), params)
response = self.client.put(f"/{collection}/{obj.id}", params)
else:
# create
params.update(self.identity_hash(obj))
response = self.client.post('/%s' % (collection), params)
if response:
response = self.client.post(f"/{collection}", params)
if obj.__class__ == response.__class__:
return response
else:
return obj.from_response(response)

def id_present(self, obj):
Expand Down
5 changes: 5 additions & 0 deletions intercom/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ def companies(self):
from intercom.service import company
return company.Company(self)

@property
def contacts(self):
from intercom.service import contact
return contact.Contact(self)

@property
def conversations(self):
from intercom.service import conversation
Expand Down
15 changes: 15 additions & 0 deletions intercom/contact.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-

from intercom.traits.api_resource import Resource
from intercom.traits.incrementable_attributes import IncrementableAttributes


class Contact(Resource, IncrementableAttributes):

collection_name = 'contacts'
update_verb = 'put'
identity_vars = ['id', 'email', 'workspace_id', 'external_id']

@property
def flat_store_attributes(self):
return ['custom_attributes']
6 changes: 5 additions & 1 deletion intercom/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class MultipleMatchingUsersError(IntercomError):
pass


class MultipleMatchingContactsError(IntercomError):
pass


class UnexpectedError(IntercomError):
pass

Expand Down Expand Up @@ -81,7 +85,7 @@ class TokenNotFoundError(IntercomError):
'rate_limit_exceeded': RateLimitExceeded,
'service_unavailable': ServiceUnavailableError,
'server_error': ServiceUnavailableError,
'conflict': MultipleMatchingUsersError,
'conflict': MultipleMatchingContactsError,
'unique_user_constraint': MultipleMatchingUsersError,
'token_unauthorized': TokenUnauthorizedError,
'token_not_found': TokenNotFoundError,
Expand Down
2 changes: 1 addition & 1 deletion intercom/extended_api_operations/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ def by_tag(self, _id):
"""Return a CollectionProxy to all the tagged resources."""
collection = utils.resource_class_to_collection_name(
self.collection_class)
finder_url = "/%s?tag_id=%s" % (collection, _id)
finder_url = f"/{collection}?tag_id={_id}"
return CollectionProxy(
self.client, self.collection_class, collection, finder_url)
17 changes: 13 additions & 4 deletions intercom/extended_api_operations/users.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# -*- coding: utf-8 -*-
"""Operation to return all users for a particular Company."""

from intercom import utils, user
from deprecated import deprecated

from intercom import user, utils
from intercom.collection_proxy import CollectionProxy


@deprecated(
"""Users is no longer available as a resource.
In order to see information and take action on users,
you should use the Contacts API."""
)
class Users(object):
"""A mixin that provides `users` functionality to Company."""

def users(self, id):
"""Return a CollectionProxy to all the users for the specified Company."""
collection = utils.resource_class_to_collection_name(
self.collection_class)
finder_url = "/%s/%s/users" % (collection, id)
self.collection_class
)
finder_url = f"/{collection}/{id}/users"
return CollectionProxy(
self.client, user.User, "users", finder_url)
self.client, user.User, "users", finder_url
)
Loading