diff --git a/freshdesk/__init__.py b/freshdesk/__init__.py index 19b4f1d..67bc602 100644 --- a/freshdesk/__init__.py +++ b/freshdesk/__init__.py @@ -1 +1 @@ -__version__ = '1.3.0' +__version__ = "1.3.0" diff --git a/freshdesk/api.py b/freshdesk/api.py index c738790..e6e561a 100644 --- a/freshdesk/api.py +++ b/freshdesk/api.py @@ -2,8 +2,7 @@ from freshdesk.v2 import api as v2_api -_VERSIONS = {1: v1_api.API, - 2: v2_api.API} +_VERSIONS = {1: v1_api.API, 2: v2_api.API} class FreshdeskAPI(object): @@ -21,7 +20,7 @@ def API(domain, api_key, version=2, **kwargs): # trim the v from a 'v1' or similar try: - version = version.lstrip('v') + version = version.lstrip("v") except AttributeError: pass diff --git a/freshdesk/v1/api.py b/freshdesk/v1/api.py index 1492668..22d5282 100644 --- a/freshdesk/v1/api.py +++ b/freshdesk/v1/api.py @@ -9,27 +9,27 @@ def __init__(self, api): self._api = api def create_ticket(self, subject, **kwargs): - url = 'helpdesk/tickets.json' - status = kwargs.get('status', 2) - priority = kwargs.get('priority', 1) - cc_emails = ','.join(kwargs.get('cc_emails', [])) + url = "helpdesk/tickets.json" + status = kwargs.get("status", 2) + priority = kwargs.get("priority", 1) + cc_emails = ",".join(kwargs.get("cc_emails", [])) ticket_data = { - 'subject': subject, - 'status': status, - 'priority': priority, + "subject": subject, + "status": status, + "priority": priority, } ticket_data.update(kwargs) data = { - 'helpdesk_ticket': ticket_data, - 'cc_emails': cc_emails, + "helpdesk_ticket": ticket_data, + "cc_emails": cc_emails, } - - return Ticket(**self._api._post(url, data=data)['helpdesk_ticket']) + + return Ticket(**self._api._post(url, data=data)["helpdesk_ticket"]) def get_ticket(self, ticket_id): """Fetches the ticket for the given ticket ID""" - url = 'helpdesk/tickets/%d.json' % ticket_id - return Ticket(**self._api._get(url)['helpdesk_ticket']) + url = "helpdesk/tickets/%d.json" % ticket_id + return Ticket(**self._api._get(url)["helpdesk_ticket"]) def list_tickets(self, **kwargs): """List all tickets, optionally filtered by a view. Specify filters as @@ -42,36 +42,36 @@ def list_tickets(self, **kwargs): Multiple filters are AND'd together. """ - filter_name = 'all_tickets' - if 'filter_name' in kwargs and kwargs['filter_name'] is not None: - filter_name = kwargs['filter_name'] - del kwargs['filter_name'] + filter_name = "all_tickets" + if "filter_name" in kwargs and kwargs["filter_name"] is not None: + filter_name = kwargs["filter_name"] + del kwargs["filter_name"] - url = 'helpdesk/tickets/filter/%s?format=json' % filter_name + url = "helpdesk/tickets/filter/%s?format=json" % filter_name page = 1 tickets = [] # Skip pagination by looping over each page and adding tickets while True: - this_page = self._api._get(url + '&page=%d' % page, kwargs) + this_page = self._api._get(url + "&page=%d" % page, kwargs) if len(this_page) == 0: break tickets += this_page page += 1 - return [self.get_ticket(t['display_id']) for t in tickets] + return [self.get_ticket(t["display_id"]) for t in tickets] def list_all_tickets(self): """List all tickets, closed or open.""" - return self.list_tickets(filter_name='all_tickets') + return self.list_tickets(filter_name="all_tickets") def list_open_tickets(self): """List all new and open tickets.""" - return self.list_tickets(filter_name='new_my_open') + return self.list_tickets(filter_name="new_my_open") def list_deleted_tickets(self): """Lists all deleted tickets.""" - return self.list_tickets(filter_name='deleted') + return self.list_tickets(filter_name="deleted") class ContactAPI(object): @@ -96,48 +96,42 @@ def list_contacts(self, **kwargs): """ - url = 'contacts.json?' - if 'query' in kwargs.keys(): - filter_query = kwargs.pop('query') + url = "contacts.json?" + if "query" in kwargs.keys(): + filter_query = kwargs.pop("query") url = url + "query={}".format(filter_query) - if 'state' in kwargs.keys(): - state_query = kwargs.pop('state') - url = url + "state={}".format(state_query) + if "state" in kwargs.keys(): + state_query = kwargs.pop("state") + url = url + "state={}".format(state_query) - if 'letter' in kwargs.keys(): - name_query = kwargs.pop('letter') - url = url + "letter={}".format(name_query) - - contacts = self._api._get(url) - return [Contact(**c['user']) for c in contacts] + if "letter" in kwargs.keys(): + name_query = kwargs.pop("letter") + url = url + "letter={}".format(name_query) + + contacts = self._api._get(url) + return [Contact(**c["user"]) for c in contacts] def create_contact(self, *args, **kwargs): """Creates a contact""" - url = 'contacts.json' - contact_data = { - 'active': True, - 'helpdesk_agent': False, - 'description': 'Freshdesk Contact' - } + url = "contacts.json" + contact_data = {"active": True, "helpdesk_agent": False, "description": "Freshdesk Contact"} contact_data.update(kwargs) - payload = { - 'user': contact_data - } + payload = {"user": contact_data} + + return Contact(**self._api._post(url, data=payload)["user"]) - return Contact(**self._api._post(url, data=payload)['user']) - def make_agent(self, contact_id): - url = 'contacts/%d/make_agent.json' % contact_id - agent = self._api._put(url)['agent'] - return self._api.agents.get_agent(agent['id']) + url = "contacts/%d/make_agent.json" % contact_id + agent = self._api._put(url)["agent"] + return self._api.agents.get_agent(agent["id"]) def get_contact(self, contact_id): - url = 'contacts/%d.json' % contact_id - return Contact(**self._api._get(url)['user']) + url = "contacts/%d.json" % contact_id + return Contact(**self._api._get(url)["user"]) def delete_contact(self, contact_id): - url = 'contacts/%d.json' % contact_id + url = "contacts/%d.json" % contact_id self._api._delete(url) @@ -162,32 +156,32 @@ def list_agents(self, **kwargs): """ - url = 'agents.json?' - if 'query' in kwargs.keys(): - filter_query = kwargs.pop('query') + url = "agents.json?" + if "query" in kwargs.keys(): + filter_query = kwargs.pop("query") url = url + "query={}".format(filter_query) - if 'state' in kwargs.keys(): - state_query = kwargs.pop('state') - url = url + "state={}".format(state_query) - - agents = self._api._get(url) - return [Agent(**a['agent']) for a in agents] - + if "state" in kwargs.keys(): + state_query = kwargs.pop("state") + url = url + "state={}".format(state_query) + + agents = self._api._get(url) + return [Agent(**a["agent"]) for a in agents] + def get_agent(self, agent_id): - """Fetches the agent for the given agent ID""" - url = 'agents/%s.json' % agent_id - return Agent(**self._api._get(url)['agent']) - + """Fetches the agent for the given agent ID""" + url = "agents/%s.json" % agent_id + return Agent(**self._api._get(url)["agent"]) + def update_agent(self, agent_id, **kwargs): """Updates an agent""" - url = 'agents/%s.json' % agent_id - agent = self._api._put(url, data=json.dumps(kwargs))['agent'] + url = "agents/%s.json" % agent_id + agent = self._api._put(url, data=json.dumps(kwargs))["agent"] return Agent(**agent) def delete_agent(self, agent_id): """Delete the agent for the given agent ID""" - url = 'agents/%d.json' % agent_id + url = "agents/%d.json" % agent_id self._api._delete(url) @@ -196,8 +190,8 @@ def __init__(self, api): self._api = api def get_customer(self, company_id): - url = 'customers/%s.json' % company_id - return Customer(**self._api._get(url)['customer']) + url = "customers/%s.json" % company_id + return Customer(**self._api._get(url)["customer"]) def get_customer_from_contact(self, contact): return self.get_customer(contact.customer_id) @@ -208,24 +202,24 @@ def __init__(self, api): self._api = api def get_all_timesheets(self, **kwargs): - url = 'helpdesk/time_sheets.json' + url = "helpdesk/time_sheets.json" if "filter_name" in kwargs.keys() and "filter_value" in kwargs.keys(): url = url + "?{}={}".format(kwargs["filter_name"], kwargs["filter_value"]) l = [] timesheet_data = self._api._get(url) i = 0 - while (i < len(timesheet_data)): - l.append(TimeEntry(**timesheet_data[i]['time_entry'])) + while i < len(timesheet_data): + l.append(TimeEntry(**timesheet_data[i]["time_entry"])) i = i + 1 return l def get_timesheet_by_ticket(self, ticket_id): - url = 'helpdesk/tickets/%s/time_sheets.json' % ticket_id + url = "helpdesk/tickets/%s/time_sheets.json" % ticket_id l = [] timesheet_data = self._api._get(url) i = 0 - while (i < len(timesheet_data)): - l.append(TimeEntry(**timesheet_data[i]['time_entry'])) + while i < len(timesheet_data): + l.append(TimeEntry(**timesheet_data[i]["time_entry"])) i = i + 1 return l @@ -242,9 +236,9 @@ def __init__(self, domain, api_key): .tickets: the Ticket API """ - self._api_prefix = 'https://{}/'.format(domain.rstrip('/')) - self.auth = (api_key, 'X') - self.headers = {'Content-Type': 'application/json'} + self._api_prefix = "https://{}/".format(domain.rstrip("/")) + self.auth = (api_key, "X") + self.headers = {"Content-Type": "application/json"} self.tickets = TicketAPI(self) self.contacts = ContactAPI(self) @@ -254,41 +248,27 @@ def __init__(self, domain, api_key): def _get(self, url, params={}): """Wrapper around request.get() to use the API prefix. Returns a JSON response.""" - r = requests.get(self._api_prefix + url, - params=params, - headers=self.headers, - auth=self.auth, - ) + r = requests.get(self._api_prefix + url, params=params, headers=self.headers, auth=self.auth,) return self._action(r) def _post(self, url, data={}): """Wrapper around request.post() to use the API prefix. Returns a JSON response.""" - r = requests.post(self._api_prefix + url, - data=json.dumps(data), - headers=self.headers, - auth=self.auth, - allow_redirects=False, + r = requests.post( + self._api_prefix + url, data=json.dumps(data), headers=self.headers, auth=self.auth, allow_redirects=False, ) - return self._action(r) - + return self._action(r) + def _put(self, url, data={}): """Wrapper around request.put() to use the API prefix. Returns a JSON response.""" - r = requests.put(self._api_prefix + url, - data=json.dumps(data), - headers=self.headers, - auth=self.auth, - allow_redirects=False, + r = requests.put( + self._api_prefix + url, data=json.dumps(data), headers=self.headers, auth=self.auth, allow_redirects=False, ) return self._action(r) def _delete(self, url): """Wrapper around request.delete() to use the API prefix. Returns a JSON response.""" - r = requests.delete(self._api_prefix + url, - headers=self.headers, - auth=self.auth, - allow_redirects=False, - ) - return self._action(r) + r = requests.delete(self._api_prefix + url, headers=self.headers, auth=self.auth, allow_redirects=False,) + return self._action(r) def _action(self, res): """Returns JSON response or raise exception if errors are present""" @@ -298,16 +278,17 @@ def _action(self, res): res.raise_for_status() j = {} - if 'Retry-After' in res.headers: - raise HTTPError('403 Forbidden: API rate-limit has been reached until {}.' - 'See http://freshdesk.com/api#ratelimit'.format(res.headers['Retry-After'])) + if "Retry-After" in res.headers: + raise HTTPError( + "403 Forbidden: API rate-limit has been reached until {}." + "See http://freshdesk.com/api#ratelimit".format(res.headers["Retry-After"]) + ) - if 'require_login' in j: - raise HTTPError('403 Forbidden: API key is incorrect for this domain') + if "require_login" in j: + raise HTTPError("403 Forbidden: API key is incorrect for this domain") - if 'error' in j: - raise HTTPError('{}: {}'.format(j.get('description'), - j.get('errors'))) + if "error" in j: + raise HTTPError("{}: {}".format(j.get("description"), j.get("errors"))) # Catch any other errors try: diff --git a/freshdesk/v1/models.py b/freshdesk/v1/models.py index ea2df76..a76e89c 100644 --- a/freshdesk/v1/models.py +++ b/freshdesk/v1/models.py @@ -11,7 +11,7 @@ def __init__(self, **kwargs): kwargs.update(custom_fields) for k, v in kwargs.items(): if hasattr(Ticket, k): - k = '_' + k + k = "_" + k setattr(self, k, v) self._keys.add(k) @@ -29,28 +29,28 @@ def __str__(self): return self.subject def __repr__(self): - return ''.format(self.subject) + return "".format(self.subject) @property def comments(self): - return [Comment(ticket=self, **c['note']) for c in self.notes] + return [Comment(ticket=self, **c["note"]) for c in self.notes] @property def priority(self): - _p = {1: 'low', 2: 'medium', 3: 'high', 4: 'urgent'} + _p = {1: "low", 2: "medium", 3: "high", 4: "urgent"} return _p[self._priority] @property def status(self): - _s = {2: 'open', 3: 'pending', 4: 'resolved', 5: 'closed'} + _s = {2: "open", 3: "pending", 4: "resolved", 5: "closed"} try: return _s[self._status] except KeyError: - return 'status_{}'.format(self._status) + return "status_{}".format(self._status) @property def source(self): - _s = {1: 'email', 2: 'portal', 3: 'phone', 4: 'forum', 5: 'twitter', 6: 'facebook', 7: 'chat'} + _s = {1: "email", 2: "portal", 3: "phone", 4: "forum", 5: "twitter", 6: "facebook", 7: "chat"} return _s[self._source] @@ -59,7 +59,7 @@ def __str__(self): return self.body def __repr__(self): - return ''.format(repr(self.ticket)) + return "".format(repr(self.ticket)) class Contact(FreshdeskModel): @@ -67,15 +67,15 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) + return "".format(self.name) class Agent(FreshdeskModel): def __str__(self): - return self.user['name'] + return self.user["name"] def __repr__(self): - return ''.format(self.id, self.user['name']) + return "".format(self.id, self.user["name"]) class TimeEntry(FreshdeskModel): @@ -83,7 +83,7 @@ def __str__(self): return str(self.id) def __repr__(self): - return ''.format(self.id) + return "".format(self.id) class Customer(FreshdeskModel): @@ -91,4 +91,4 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) + return "".format(self.name) diff --git a/freshdesk/v1/tests/conftest.py b/freshdesk/v1/tests/conftest.py index 4b6eec4..b82c6de 100644 --- a/freshdesk/v1/tests/conftest.py +++ b/freshdesk/v1/tests/conftest.py @@ -6,90 +6,96 @@ from freshdesk.v1.api import API -DOMAIN = 'pythonfreshdesk.freshdesk.com' -API_KEY = 'MX4CEAw4FogInimEdRW2' +DOMAIN = "pythonfreshdesk.freshdesk.com" +API_KEY = "MX4CEAw4FogInimEdRW2" class MockedAPI(API): def __init__(self, *args): self.resolver = { - 'get': { - re.compile(r'helpdesk/tickets/filter/all_tickets\?format=json&page=1'): self.read_test_file( - 'all_tickets.json'), - re.compile(r'helpdesk/tickets/filter/new_my_open\?format=json&page=1'): self.read_test_file( - 'all_tickets.json'), - re.compile(r'helpdesk/tickets/filter/spam\?format=json&page=1'): [], - re.compile(r'helpdesk/tickets/filter/deleted\?format=json&page=1'): [], - re.compile(r'helpdesk/tickets/1/time_sheets.json'): self.read_test_file('timeentries_ticket_1.json'), - re.compile(r'helpdesk/tickets/1.json'): self.read_test_file('ticket_1.json'), - re.compile(r'.*&page=2'): [], - re.compile(r'contacts.json'): self.read_test_file('contacts.json'), - re.compile(r'contacts/1.json'): self.read_test_file('contact.json'), - re.compile(r'contacts/1.json'): self.read_test_file('contact.json'), - re.compile(r'agents.json\?$'): self.read_test_file('agents.json'), - re.compile(r'agents/1.json$'): self.read_test_file('agent_1.json'), - re.compile(r'customers/1.json'): self.read_test_file('customer.json'), - re.compile(r'helpdesk/time_sheets.json'): self.read_test_file('timeentries_ticket_1.json'), - re.compile(r'helpdesk/time_sheets.json\?agent_id='): self.read_test_file('timeentries_ticket_1.json'), - re.compile(r'helpdesk/time_sheets.json'): self.read_test_file('timeentries_ticket_1.json'), + "get": { + re.compile(r"helpdesk/tickets/filter/all_tickets\?format=json&page=1"): self.read_test_file( + "all_tickets.json" + ), + re.compile(r"helpdesk/tickets/filter/new_my_open\?format=json&page=1"): self.read_test_file( + "all_tickets.json" + ), + re.compile(r"helpdesk/tickets/filter/spam\?format=json&page=1"): [], + re.compile(r"helpdesk/tickets/filter/deleted\?format=json&page=1"): [], + re.compile(r"helpdesk/tickets/1/time_sheets.json"): self.read_test_file("timeentries_ticket_1.json"), + re.compile(r"helpdesk/tickets/1.json"): self.read_test_file("ticket_1.json"), + re.compile(r".*&page=2"): [], + re.compile(r"contacts.json"): self.read_test_file("contacts.json"), + re.compile(r"contacts/1.json"): self.read_test_file("contact.json"), + re.compile(r"contacts/1.json"): self.read_test_file("contact.json"), + re.compile(r"agents.json\?$"): self.read_test_file("agents.json"), + re.compile(r"agents/1.json$"): self.read_test_file("agent_1.json"), + re.compile(r"customers/1.json"): self.read_test_file("customer.json"), + re.compile(r"helpdesk/time_sheets.json"): self.read_test_file("timeentries_ticket_1.json"), + re.compile(r"helpdesk/time_sheets.json\?agent_id="): self.read_test_file("timeentries_ticket_1.json"), + re.compile(r"helpdesk/time_sheets.json"): self.read_test_file("timeentries_ticket_1.json"), }, - 'post': { - re.compile(r'helpdesk/tickets.json'): self.read_test_file('ticket_1.json'), - re.compile(r'contacts.json'): self.read_test_file('contact.json'), - re.compile(r'agents/1.json$'): self.read_test_file('agent_1.json'), + "post": { + re.compile(r"helpdesk/tickets.json"): self.read_test_file("ticket_1.json"), + re.compile(r"contacts.json"): self.read_test_file("contact.json"), + re.compile(r"agents/1.json$"): self.read_test_file("agent_1.json"), }, - 'put': { - re.compile(r'contacts/1/make_agent.json'): self.read_test_file('agent_1.json'), - re.compile(r'agents/1.json$'): self.read_test_file('agent_1_updated.json'), + "put": { + re.compile(r"contacts/1/make_agent.json"): self.read_test_file("agent_1.json"), + re.compile(r"agents/1.json$"): self.read_test_file("agent_1_updated.json"), + }, + "delete": { + re.compile(r"helpdesk/tickets/1.json"): None, + re.compile(r"contacts/1.json"): None, + re.compile(r"agents/1.json$"): None, }, - 'delete': { - re.compile(r'helpdesk/tickets/1.json'): None, - re.compile(r'contacts/1.json'): None, - re.compile(r'agents/1.json$'): None, - } } super(MockedAPI, self).__init__(*args) def read_test_file(self, filename): - path = os.path.join(os.path.dirname(__file__), 'sample_json_data', filename) - return json.loads(open(path, 'r').read()) + path = os.path.join(os.path.dirname(__file__), "sample_json_data", filename) + return json.loads(open(path, "r").read()) def _get(self, url, *args, **kwargs): - for pattern, j in self.resolver['get'].items(): + for pattern, j in self.resolver["get"].items(): if pattern.match(url): return j # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_get() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_get() has no pattern for '{}'".format(url)) def _post(self, url, *args, **kwargs): - for pattern, data in self.resolver['post'].items(): + for pattern, data in self.resolver["post"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_post() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_post() has no pattern for '{}'".format(url)) def _put(self, url, *args, **kwargs): - for pattern, data in self.resolver['put'].items(): + for pattern, data in self.resolver["put"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_put() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_put() has no pattern for '{}'".format(url)) def _delete(self, url, *args, **kwargs): - for pattern, data in self.resolver['delete'].items(): + for pattern, data in self.resolver["delete"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_delete() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_delete() has no pattern for '{}'".format(url)) @pytest.fixture() diff --git a/freshdesk/v1/tests/test_agent.py b/freshdesk/v1/tests/test_agent.py index 4667fa2..9379240 100644 --- a/freshdesk/v1/tests/test_agent.py +++ b/freshdesk/v1/tests/test_agent.py @@ -14,15 +14,15 @@ def agent(api): @pytest.fixture def agent_json(): - return json.loads(open(os.path.join(os.path.dirname(__file__), 'sample_json_data', 'agent_1.json')).read()) + return json.loads(open(os.path.join(os.path.dirname(__file__), "sample_json_data", "agent_1.json")).read()) def test_str(agent): - assert str(agent) == 'Rachel' + assert str(agent) == "Rachel" def test_repr(agent): - assert repr(agent) == '' + assert repr(agent) == "" def test_list_agents(api, agent): @@ -35,24 +35,19 @@ def test_list_agents(api, agent): def test_get_agent(agent): assert isinstance(agent, Agent) assert agent.id == 1 - assert agent.user['name'] == 'Rachel' - assert agent.user['email'] == 'rachel@freshdesk.com' - assert agent.user['mobile'] == 1234 - assert agent.user['phone'] == 5678 + assert agent.user["name"] == "Rachel" + assert agent.user["email"] == "rachel@freshdesk.com" + assert agent.user["mobile"] == 1234 + assert agent.user["phone"] == 5678 assert agent.occasional is False def test_update_agent(api): - values = { - 'occasional': True, - 'contact': { - 'name': 'Updated Name' - } - } + values = {"occasional": True, "contact": {"name": "Updated Name"}} agent = api.agents.update_agent(1, **values) assert agent.occasional is True - assert agent.user['name'] == 'Updated Name' + assert agent.user["name"] == "Updated Name" def test_delete_agent(api): @@ -60,11 +55,11 @@ def test_delete_agent(api): def test_agent_name(agent): - assert agent.user['name'] == 'Rachel' + assert agent.user["name"] == "Rachel" def test_agent_mobile(agent): - assert agent.user['mobile'] == 1234 + assert agent.user["mobile"] == 1234 def test_agent_state(agent): diff --git a/freshdesk/v1/tests/test_api_class.py b/freshdesk/v1/tests/test_api_class.py index 936edee..b95c940 100644 --- a/freshdesk/v1/tests/test_api_class.py +++ b/freshdesk/v1/tests/test_api_class.py @@ -6,32 +6,30 @@ def test_api_prefix(): - api = API('test_domain', 'test_key') - assert api._api_prefix == 'https://test_domain/' - api = API('test_domain/', 'test_key') - assert api._api_prefix == 'https://test_domain/' + api = API("test_domain", "test_key") + assert api._api_prefix == "https://test_domain/" + api = API("test_domain/", "test_key") + assert api._api_prefix == "https://test_domain/" @responses.activate def test_403_error(): - responses.add(responses.GET, - 'https://{}/helpdesk/tickets/1.json'.format(DOMAIN), - status=403) + responses.add(responses.GET, "https://{}/helpdesk/tickets/1.json".format(DOMAIN), status=403) - api = API(DOMAIN, 'invalid_api_key') + api = API(DOMAIN, "invalid_api_key") from requests.exceptions import HTTPError + with pytest.raises(HTTPError): api.tickets.get_ticket(1) @responses.activate def test_404_error(): - DOMAIN_404 = 'google.com' - responses.add(responses.GET, - 'https://{}/helpdesk/tickets/1.json'.format(DOMAIN_404), - status=404) + DOMAIN_404 = "google.com" + responses.add(responses.GET, "https://{}/helpdesk/tickets/1.json".format(DOMAIN_404), status=404) - api = API(DOMAIN_404, 'invalid_api_key') + api = API(DOMAIN_404, "invalid_api_key") from requests.exceptions import HTTPError + with pytest.raises(HTTPError): api.tickets.get_ticket(1) diff --git a/freshdesk/v1/tests/test_comment.py b/freshdesk/v1/tests/test_comment.py index b323b09..fc3febe 100644 --- a/freshdesk/v1/tests/test_comment.py +++ b/freshdesk/v1/tests/test_comment.py @@ -15,8 +15,8 @@ def test_comments_list(ticket): def test_comment_str(ticket): - assert str(ticket.comments[0]) == 'This is a reply.' + assert str(ticket.comments[0]) == "This is a reply." def test_comment_repr(ticket): - assert repr(ticket.comments[0]) == '>' + assert repr(ticket.comments[0]) == ">" diff --git a/freshdesk/v1/tests/test_contact.py b/freshdesk/v1/tests/test_contact.py index 7718744..56dfe5f 100644 --- a/freshdesk/v1/tests/test_contact.py +++ b/freshdesk/v1/tests/test_contact.py @@ -12,8 +12,8 @@ def contact(api): def test_get_contact(contact): assert isinstance(contact, Contact) - assert contact.name == 'Rachel' - assert contact.email == 'rachel@freshdesk.com' + assert contact.name == "Rachel" + assert contact.email == "rachel@freshdesk.com" assert contact.helpdesk_agent is False assert contact.customer_id == 1 @@ -29,10 +29,7 @@ def test_list_contacts(api, contact): def test_create_contact(api): - contact_data = { - 'name': 'Rachel', - 'email': 'rachel@freshdesk.com' - } + contact_data = {"name": "Rachel", "email": "rachel@freshdesk.com"} contact = api.contacts.create_contact(contact_data) assert isinstance(contact, Contact) assert contact.id == contact.id @@ -47,8 +44,8 @@ def test_make_agent(api, contact): assert agent.occasional is False assert agent.id == 1 assert agent.user_id == contact.id - assert agent.user['email'] == contact.email - assert agent.user['name'] == contact.name + assert agent.user["email"] == contact.email + assert agent.user["name"] == contact.name def test_delete_contact(api): @@ -61,8 +58,8 @@ def test_contact_datetime(contact): def test_contact_str(contact): - assert str(contact) == 'Rachel' + assert str(contact) == "Rachel" def test_contact_repr(contact): - assert repr(contact) == '' + assert repr(contact) == "" diff --git a/freshdesk/v1/tests/test_customer.py b/freshdesk/v1/tests/test_customer.py index 1744a60..0433195 100644 --- a/freshdesk/v1/tests/test_customer.py +++ b/freshdesk/v1/tests/test_customer.py @@ -17,9 +17,9 @@ def contact(api): def test_customer(customer): assert isinstance(customer, Customer) - assert customer.name == 'ACME Corp.' - assert customer.domains == 'acme.com' - assert customer.cf_custom_key == 'custom_value' + assert customer.name == "ACME Corp." + assert customer.domains == "acme.com" + assert customer.cf_custom_key == "custom_value" def test_contact_datetime(customer): @@ -28,11 +28,11 @@ def test_contact_datetime(customer): def test_contact_str(customer): - assert str(customer) == 'ACME Corp.' + assert str(customer) == "ACME Corp." def test_contact_repr(customer): - assert repr(customer) == '' + assert repr(customer) == "" def test_get_customer_from_contact(api, contact): diff --git a/freshdesk/v1/tests/test_ticket.py b/freshdesk/v1/tests/test_ticket.py index 88dbd71..e7bb10f 100644 --- a/freshdesk/v1/tests/test_ticket.py +++ b/freshdesk/v1/tests/test_ticket.py @@ -14,54 +14,57 @@ def ticket(api): @pytest.fixture def ticket_json(): - return json.loads(open(os.path.join(os.path.dirname(__file__), 'sample_json_data', 'ticket_1.json')).read()) + return json.loads(open(os.path.join(os.path.dirname(__file__), "sample_json_data", "ticket_1.json")).read()) def test_str(ticket): - assert str(ticket) == 'This is a sample ticket' + assert str(ticket) == "This is a sample ticket" def test_repr(ticket): - assert repr(ticket) == '' + assert repr(ticket) == "" def test_create_ticket(api): - ticket = api.tickets.create_ticket('This is a sample ticket', - description='This is a sample ticket, feel free to delete it.', - email='test@example.com', - priority=1, status=2, - tags=['foo', 'bar'], - cc_emails=['test2@example.com']) + ticket = api.tickets.create_ticket( + "This is a sample ticket", + description="This is a sample ticket, feel free to delete it.", + email="test@example.com", + priority=1, + status=2, + tags=["foo", "bar"], + cc_emails=["test2@example.com"], + ) assert isinstance(ticket, Ticket) - assert ticket.subject == 'This is a sample ticket' - assert ticket.description, 'This is a sample ticket == feel free to delete it.' - assert ticket.priority == 'low' - assert ticket.status == 'open' - assert ticket.cc_email['cc_emails'] == ['test2@example.com'] - assert 'foo' in ticket.tags - assert 'bar' in ticket.tags + assert ticket.subject == "This is a sample ticket" + assert ticket.description, "This is a sample ticket == feel free to delete it." + assert ticket.priority == "low" + assert ticket.status == "open" + assert ticket.cc_email["cc_emails"] == ["test2@example.com"] + assert "foo" in ticket.tags + assert "bar" in ticket.tags def test_get_ticket(ticket): assert isinstance(ticket, Ticket) assert ticket.display_id == 1 - assert ticket.subject == 'This is a sample ticket' - assert ticket.description, 'This is a sample ticket == feel free to delete it.' + assert ticket.subject == "This is a sample ticket" + assert ticket.description, "This is a sample ticket == feel free to delete it." def test_ticket_priority(ticket): assert ticket._priority == 1 - assert ticket.priority == 'low' + assert ticket.priority == "low" def test_ticket_status(ticket): assert ticket._status == 2 - assert ticket.status == 'open' + assert ticket.status == "open" def test_ticket_source(ticket): assert ticket._source == 2 - assert ticket.source == 'portal' + assert ticket.source == "portal" def test_ticket_datetime(ticket): @@ -90,7 +93,7 @@ def test_deleted_tickets(api): def test_spam_tickets(api): - tickets = api.tickets.list_tickets(filter_name='spam') + tickets = api.tickets.list_tickets(filter_name="spam") assert isinstance(tickets, list) assert len(tickets) == 0 diff --git a/freshdesk/v1/tests/test_timesheets.py b/freshdesk/v1/tests/test_timesheets.py index c9ff246..debbc79 100644 --- a/freshdesk/v1/tests/test_timesheets.py +++ b/freshdesk/v1/tests/test_timesheets.py @@ -22,7 +22,7 @@ def test_timesheet_str(timesheet): def test_timesheet_repr(timesheet): - assert repr(timesheet[1]) == '' + assert repr(timesheet[1]) == "" def test_get_all_timesheets(api): diff --git a/freshdesk/v2/api.py b/freshdesk/v2/api.py index c696478..6761fc3 100644 --- a/freshdesk/v2/api.py +++ b/freshdesk/v2/api.py @@ -4,8 +4,13 @@ from requests import HTTPError from freshdesk.v2.errors import ( - FreshdeskAccessDenied, FreshdeskBadRequest, FreshdeskError, FreshdeskNotFound, FreshdeskRateLimited, - FreshdeskServerError, FreshdeskUnauthorized, + FreshdeskAccessDenied, + FreshdeskBadRequest, + FreshdeskError, + FreshdeskNotFound, + FreshdeskRateLimited, + FreshdeskServerError, + FreshdeskUnauthorized, ) from freshdesk.v2.models import Agent, Comment, Company, Contact, Customer, Group, Role, Ticket, TicketField, TimeEntry @@ -16,7 +21,7 @@ def __init__(self, api): def get_ticket(self, ticket_id): """Fetches the ticket for the given ticket ID""" - url = 'tickets/%d' % ticket_id + url = "tickets/%d" % ticket_id ticket = self._api._get(url) return Ticket(**ticket) @@ -28,16 +33,16 @@ def create_ticket(self, subject, **kwargs): ex: attachments = ('/path/to/attachment1', '/path/to/attachment2') """ - url = 'tickets' - status = kwargs.get('status', 2) - priority = kwargs.get('priority', 1) + url = "tickets" + status = kwargs.get("status", 2) + priority = kwargs.get("priority", 1) data = { - 'subject': subject, - 'status': status, - 'priority': priority, + "subject": subject, + "status": status, + "priority": priority, } data.update(kwargs) - if 'attachments' in data: + if "attachments" in data: ticket = self._create_ticket_with_attachment(url, data) return Ticket(**ticket) @@ -45,40 +50,40 @@ def create_ticket(self, subject, **kwargs): return Ticket(**ticket) def _create_ticket_with_attachment(self, url, data): - attachments = data['attachments'] - del data['attachments'] + attachments = data["attachments"] + del data["attachments"] multipart_data = [] for attachment in attachments: file_name = attachment.split("/")[-1:][0] - multipart_data.append(('attachments[]', (file_name, open(attachment, 'rb'), None))) + multipart_data.append(("attachments[]", (file_name, open(attachment, "rb"), None))) for key, value in data.copy().items(): # Reformat ticket properties to work with the multipart/form-data encoding. - if isinstance(value, list) and not key.endswith('[]'): - data[key + '[]'] = value + if isinstance(value, list) and not key.endswith("[]"): + data[key + "[]"] = value del data[key] - if 'custom_fields' in data and isinstance(data['custom_fields'], dict): + if "custom_fields" in data and isinstance(data["custom_fields"], dict): # Reformat custom fields to work with the multipart/form-data encoding. - for field, value in data['custom_fields'].items(): - data['custom_fields[{}]'.format(field)] = value - del data['custom_fields'] + for field, value in data["custom_fields"].items(): + data["custom_fields[{}]".format(field)] = value + del data["custom_fields"] # Override the content type so that `requests` correctly sets it to multipart/form-data instead of JSON. - ticket = self._api._post(url, data=data, files=multipart_data, headers={'Content-Type': None}) + ticket = self._api._post(url, data=data, files=multipart_data, headers={"Content-Type": None}) return ticket def create_outbound_email(self, subject, description, email, email_config_id, **kwargs): """Creates an outbound email""" - url = 'tickets/outbound_email' - priority = kwargs.get('priority', 1) + url = "tickets/outbound_email" + priority = kwargs.get("priority", 1) data = { - 'subject': subject, - 'description': description, - 'priority': priority, - 'email': email, - 'email_config_id': email_config_id, + "subject": subject, + "description": description, + "priority": priority, + "email": email, + "email_config_id": email_config_id, } data.update(kwargs) ticket = self._api._post(url, data=json.dumps(data)) @@ -86,13 +91,13 @@ def create_outbound_email(self, subject, description, email, email_config_id, ** def update_ticket(self, ticket_id, **kwargs): """Updates a ticket from a given ticket ID""" - url = 'tickets/%d' % ticket_id + url = "tickets/%d" % ticket_id ticket = self._api._put(url, data=json.dumps(kwargs)) return Ticket(**ticket) def delete_ticket(self, ticket_id): """Delete the ticket for the given ticket ID""" - url = 'tickets/%d' % ticket_id + url = "tickets/%d" % ticket_id self._api._delete(url) def list_tickets(self, **kwargs): @@ -109,27 +114,26 @@ def list_tickets(self, **kwargs): Multiple filters are AND'd together. """ - filter_name = 'new_and_my_open' - if 'filter_name' in kwargs: - filter_name = kwargs['filter_name'] - del kwargs['filter_name'] + filter_name = "new_and_my_open" + if "filter_name" in kwargs: + filter_name = kwargs["filter_name"] + del kwargs["filter_name"] - url = 'tickets' + url = "tickets" if filter_name is not None: - url += '?filter=%s&' % filter_name + url += "?filter=%s&" % filter_name else: - url += '?' - page = 1 if not 'page' in kwargs else kwargs['page'] - per_page = 100 if not 'per_page' in kwargs else kwargs['per_page'] + url += "?" + page = 1 if not "page" in kwargs else kwargs["page"] + per_page = 100 if not "per_page" in kwargs else kwargs["per_page"] tickets = [] # Skip pagination by looping over each page and adding tickets if 'page' key is not in kwargs. # else return the requested page and break the loop while True: - this_page = self._api._get(url + 'page=%d&per_page=%d' - % (page, per_page), kwargs) + this_page = self._api._get(url + "page=%d&per_page=%d" % (page, per_page), kwargs) tickets += this_page - if len(this_page) < per_page or 'page' in kwargs: + if len(this_page) < per_page or "page" in kwargs: break page += 1 @@ -137,15 +141,15 @@ def list_tickets(self, **kwargs): def list_new_and_my_open_tickets(self): """List all new and open tickets.""" - return self.list_tickets(filter_name='new_and_my_open') + return self.list_tickets(filter_name="new_and_my_open") def list_watched_tickets(self): """List watched tickets, closed or open.""" - return self.list_tickets(filter_name='watching') + return self.list_tickets(filter_name="watching") def list_deleted_tickets(self): """Lists all deleted tickets.""" - return self.list_tickets(filter_name='deleted') + return self.list_tickets(filter_name="deleted") def filter_tickets(self, query, **kwargs): """Filter tickets by a given query string. The query string must be in @@ -154,20 +158,19 @@ def filter_tickets(self, query, **kwargs): query = "(ticket_field:integer OR ticket_field:'string') AND ticket_field:boolean" """ - if(len(query) > 512): - raise AttributeError('Query string can have up to 512 characters') - - url = 'search/tickets?' - page = 1 if not 'page' in kwargs else kwargs['page'] + if len(query) > 512: + raise AttributeError("Query string can have up to 512 characters") + + url = "search/tickets?" + page = 1 if not "page" in kwargs else kwargs["page"] per_page = 30 tickets = [] while True: - this_page = self._api._get(url + 'page={}&query="{}"'.format(page, query), - kwargs) - this_page = this_page['results'] + this_page = self._api._get(url + 'page={}&query="{}"'.format(page, query), kwargs) + this_page = this_page["results"] tickets += this_page - if len(this_page) < per_page or page == 10 or 'page' in kwargs: + if len(this_page) < per_page or page == 10 or "page" in kwargs: break page += 1 @@ -179,21 +182,21 @@ def __init__(self, api): self._api = api def list_comments(self, ticket_id): - url = 'tickets/%d/conversations' % ticket_id + url = "tickets/%d/conversations" % ticket_id comments = [] for c in self._api._get(url): comments.append(Comment(**c)) return comments def create_note(self, ticket_id, body, **kwargs): - url = 'tickets/%d/notes' % ticket_id - data = {'body': body} + url = "tickets/%d/notes" % ticket_id + data = {"body": body} data.update(kwargs) return Comment(**self._api._post(url, data=json.dumps(data))) def create_reply(self, ticket_id, body, **kwargs): - url = 'tickets/%d/reply' % ticket_id - data = {'body': body} + url = "tickets/%d/reply" % ticket_id + data = {"body": body} data.update(kwargs) return Comment(**self._api._post(url, data=json.dumps(data))) @@ -203,23 +206,22 @@ def __init__(self, api): self._api = api def list_groups(self, **kwargs): - url = 'groups?' - page = 1 if not 'page' in kwargs else kwargs['page'] - per_page = 100 if not 'per_page' in kwargs else kwargs['per_page'] + url = "groups?" + page = 1 if not "page" in kwargs else kwargs["page"] + per_page = 100 if not "per_page" in kwargs else kwargs["per_page"] groups = [] while True: - this_page = self._api._get(url + 'page=%d&per_page=%d' - % (page, per_page), kwargs) + this_page = self._api._get(url + "page=%d&per_page=%d" % (page, per_page), kwargs) groups += this_page - if len(this_page) < per_page or 'page' in kwargs: + if len(this_page) < per_page or "page" in kwargs: break page += 1 return [Group(**g) for g in groups] def get_group(self, group_id): - url = 'groups/%s' % group_id + url = "groups/%s" % group_id return Group(**self._api._get(url)) @@ -250,19 +252,18 @@ def list_contacts(self, **kwargs): """ - url = 'contacts?' - page = 1 if not 'page' in kwargs else kwargs['page'] - per_page = 100 if not 'per_page' in kwargs else kwargs['per_page'] + url = "contacts?" + page = 1 if not "page" in kwargs else kwargs["page"] + per_page = 100 if not "per_page" in kwargs else kwargs["per_page"] contacts = [] # Skip pagination by looping over each page and adding tickets if 'page' key is not in kwargs. # else return the requested page and break the loop while True: - this_page = self._api._get(url + 'page=%d&per_page=%d' - % (page, per_page), kwargs) + this_page = self._api._get(url + "page=%d&per_page=%d" % (page, per_page), kwargs) contacts += this_page - if len(this_page) < per_page or 'page' in kwargs: + if len(this_page) < per_page or "page" in kwargs: break page += 1 @@ -271,43 +272,40 @@ def list_contacts(self, **kwargs): def create_contact(self, *args, **kwargs): """Creates a contact""" - url = 'contacts' - data = { - 'view_all_tickets': False, - 'description': 'Freshdesk Contact' - } + url = "contacts" + data = {"view_all_tickets": False, "description": "Freshdesk Contact"} data.update(kwargs) return Contact(**self._api._post(url, data=json.dumps(data))) def get_contact(self, contact_id): - url = 'contacts/%d' % contact_id + url = "contacts/%d" % contact_id return Contact(**self._api._get(url)) def update_contact(self, contact_id, **data): - url = 'contacts/%d' % contact_id + url = "contacts/%d" % contact_id return Contact(**self._api._put(url, data=json.dumps(data))) def soft_delete_contact(self, contact_id): - url = 'contacts/%d' % contact_id + url = "contacts/%d" % contact_id self._api._delete(url) def restore_contact(self, contact_id): - url = 'contacts/%d/restore' % contact_id + url = "contacts/%d/restore" % contact_id self._api._put(url) def permanently_delete_contact(self, contact_id, force=True): - url = 'contacts/%d/hard_delete?force=%r' % (contact_id, force) + url = "contacts/%d/hard_delete?force=%r" % (contact_id, force) self._api._delete(url) def make_agent(self, contact_id, **kwargs): - url = 'contacts/%d/make_agent' % contact_id + url = "contacts/%d/make_agent" % contact_id data = { - 'occasional': False, - 'ticket_scope': 2, + "occasional": False, + "ticket_scope": 2, } data.update(kwargs) contact = self._api._put(url, data=json.dumps(data)) - return self._api.agents.get_agent(contact['agent']['id']) + return self._api.agents.get_agent(contact["agent"]["id"]) class CustomerAPI(object): @@ -315,7 +313,7 @@ def __init__(self, api): self._api = api def get_customer(self, company_id): - url = 'customers/%s' % company_id + url = "customers/%s" % company_id return Customer(**self._api._get(url)) def get_customer_from_contact(self, contact): @@ -327,23 +325,22 @@ def __init__(self, api): self._api = api def get_company(self, company_id): - url = 'companies/%s' % company_id + url = "companies/%s" % company_id return Company(**self._api._get(url)) def list_companies(self, **kwargs): - url = 'companies?' - page = 1 if not 'page' in kwargs else kwargs['page'] - per_page = 100 if not 'per_page' in kwargs else kwargs['per_page'] + url = "companies?" + page = 1 if not "page" in kwargs else kwargs["page"] + per_page = 100 if not "per_page" in kwargs else kwargs["per_page"] companies = [] # Skip pagination by looping over each page and adding tickets if 'page' key is not in kwargs. # else return the requested page and break the loop while True: - this_page = self._api._get(url + 'page=%d&per_page=%d' - % (page, per_page), kwargs) + this_page = self._api._get(url + "page=%d&per_page=%d" % (page, per_page), kwargs) companies += this_page - if len(this_page) < per_page or 'page' in kwargs: + if len(this_page) < per_page or "page" in kwargs: break page += 1 @@ -356,14 +353,14 @@ def __init__(self, api): self._api = api def list_roles(self): - url = 'roles' + url = "roles" roles = [] for r in self._api._get(url): roles.append(Role(**r)) return roles def get_role(self, role_id): - url = 'roles/%s' % role_id + url = "roles/%s" % role_id return Role(**self._api._get(url)) @@ -372,16 +369,16 @@ def __init__(self, api): self._api = api def list_time_entries(self, ticket_id=None): - url = 'tickets/time_entries' + url = "tickets/time_entries" if ticket_id is not None: - url = 'tickets/%d/time_entries' % ticket_id + url = "tickets/%d/time_entries" % ticket_id timeEntries = [] for r in self._api._get(url): timeEntries.append(TimeEntry(**r)) return timeEntries def get_role(self, role_id): - url = 'roles/%s' % role_id + url = "roles/%s" % role_id return Role(**self._api._get(url)) @@ -390,11 +387,11 @@ def __init__(self, api): self._api = api def list_ticket_fields(self, **kwargs): - url = 'ticket_fields' + url = "ticket_fields" ticket_fields = [] - if 'type' in kwargs: - url = "{}?type={}".format(url, kwargs['type']) + if "type" in kwargs: + url = "{}?type={}".format(url, kwargs["type"]) for tf in self._api._get(url): ticket_fields.append(TicketField(**tf)) @@ -422,19 +419,18 @@ def list_agents(self, **kwargs): Multiple filters are AND'd together. """ - url = 'agents?' - page = 1 if not 'page' in kwargs else kwargs['page'] - per_page = 100 if not 'per_page' in kwargs else kwargs['per_page'] + url = "agents?" + page = 1 if not "page" in kwargs else kwargs["page"] + per_page = 100 if not "per_page" in kwargs else kwargs["per_page"] agents = [] # Skip pagination by looping over each page and adding tickets if 'page' key is not in kwargs. # else return the requested page and break the loop while True: - this_page = self._api._get(url + 'page=%d&per_page=%d' - % (page, per_page), kwargs) + this_page = self._api._get(url + "page=%d&per_page=%d" % (page, per_page), kwargs) agents += this_page - if len(this_page) < per_page or 'page' in kwargs: + if len(this_page) < per_page or "page" in kwargs: break page += 1 @@ -442,23 +438,23 @@ def list_agents(self, **kwargs): def get_agent(self, agent_id): """Fetches the agent for the given agent ID""" - url = 'agents/%s' % agent_id + url = "agents/%s" % agent_id return Agent(**self._api._get(url)) def update_agent(self, agent_id, **kwargs): """Updates an agent""" - url = 'agents/%s' % agent_id + url = "agents/%s" % agent_id agent = self._api._put(url, data=json.dumps(kwargs)) return Agent(**agent) def delete_agent(self, agent_id): """Delete the agent for the given agent ID""" - url = 'agents/%d' % agent_id + url = "agents/%d" % agent_id self._api._delete(url) def currently_authenticated_agent(self): """Fetches currently logged in agent""" - url = 'agents/me' + url = "agents/me" return Agent(**self._api._get(url)) @@ -474,12 +470,12 @@ def __init__(self, domain, api_key, verify=True, proxies=None): .tickets: the Ticket API """ - self._api_prefix = 'https://{}/api/v2/'.format(domain.rstrip('/')) + self._api_prefix = "https://{}/api/v2/".format(domain.rstrip("/")) self._session = requests.Session() - self._session.auth = (api_key, 'unused_with_api_key') + self._session.auth = (api_key, "unused_with_api_key") self._session.verify = verify self._session.proxies = proxies - self._session.headers = {'Content-Type': 'application/json'} + self._session.headers = {"Content-Type": "application/json"} self.tickets = TicketAPI(self) self.comments = CommentAPI(self) @@ -491,9 +487,8 @@ def __init__(self, domain, api_key, verify=True, proxies=None): self.roles = RoleAPI(self) self.ticket_fields = TicketFieldAPI(self) - if domain.find('freshdesk.com') < 0: - raise AttributeError('Freshdesk v2 API works only via Freshdesk' - 'domains and not via custom CNAMEs') + if domain.find("freshdesk.com") < 0: + raise AttributeError("Freshdesk v2 API works only via Freshdesk" "domains and not via custom CNAMEs") self.domain = domain def _action(self, req): @@ -502,12 +497,12 @@ def _action(self, req): except ValueError: j = {} - error_message = 'Freshdesk Request Failed' - if 'errors' in j: - error_message = '{}: {}'.format(j.get('description'), j.get('errors')) - elif 'message' in j: - error_message = j['message'] - + error_message = "Freshdesk Request Failed" + if "errors" in j: + error_message = "{}: {}".format(j.get("description"), j.get("errors")) + elif "message" in j: + error_message = j["message"] + if req.status_code == 400: raise FreshdeskBadRequest(error_message) elif req.status_code == 401: @@ -518,10 +513,11 @@ def _action(self, req): raise FreshdeskNotFound(error_message) elif req.status_code == 429: raise FreshdeskRateLimited( - '429 Rate Limit Exceeded: API rate-limit has been reached until {} seconds. See ' - 'http://freshdesk.com/api#ratelimit'.format(req.headers.get('Retry-After'))) + "429 Rate Limit Exceeded: API rate-limit has been reached until {} seconds. See " + "http://freshdesk.com/api#ratelimit".format(req.headers.get("Retry-After")) + ) elif 500 < req.status_code < 600: - raise FreshdeskServerError('{}: Server Error'.format(req.status_code)) + raise FreshdeskServerError("{}: Server Error".format(req.status_code)) # Catch any other errors try: diff --git a/freshdesk/v2/models.py b/freshdesk/v2/models.py index c5a1ecd..2631708 100644 --- a/freshdesk/v2/models.py +++ b/freshdesk/v2/models.py @@ -12,7 +12,7 @@ def __init__(self, **kwargs): kwargs.update(custom_fields) for k, v in kwargs.items(): if hasattr(Ticket, k): - k = '_' + k + k = "_" + k setattr(self, k, v) self._keys.add(k) self.created_at = self._to_timestamp(self.created_at) @@ -29,32 +29,32 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name, self.description) + return "".format(self.name, self.description) + class Ticket(FreshdeskModel): def __str__(self): return self.subject def __repr__(self): - return ''.format(self.subject, self.id) + return "".format(self.subject, self.id) @property def priority(self): - _p = {1: 'low', 2: 'medium', 3: 'high', 4: 'urgent'} + _p = {1: "low", 2: "medium", 3: "high", 4: "urgent"} return _p[self._priority] @property def status(self): - _s = {2: 'open', 3: 'pending', 4: 'resolved', 5: 'closed'} + _s = {2: "open", 3: "pending", 4: "resolved", 5: "closed"} try: return _s[self._status] except KeyError: - return 'status_{}'.format(self._status) + return "status_{}".format(self._status) @property def source(self): - _s = {1: 'email', 2: 'portal', 3: 'phone', 4: 'forum', 5: 'twitter', - 6: 'facebook', 7: 'chat'} + _s = {1: "email", 2: "portal", 3: "phone", 4: "forum", 5: "twitter", 6: "facebook", 7: "chat"} return _s[self._source] @@ -63,7 +63,7 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) + return "".format(self.name) class Comment(FreshdeskModel): @@ -71,12 +71,21 @@ def __str__(self): return self.body_text def __repr__(self): - return ''.format(self.ticket_id) + return "".format(self.ticket_id) @property def source(self): - _s = {0: 'reply', 2: 'note', 5: 'twitter', 6: 'survey', 7: 'facebook', - 8: 'email', 9: 'phone', 10: 'mobihelp', 11: 'e-commerce'} + _s = { + 0: "reply", + 2: "note", + 5: "twitter", + 6: "survey", + 7: "facebook", + 8: "email", + 9: "phone", + 10: "mobihelp", + 11: "e-commerce", + } return _s[self._source] @@ -85,7 +94,7 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) + return "".format(self.name) class TimeEntry(FreshdeskModel): @@ -93,7 +102,7 @@ def __str__(self): return str(self.id) def __repr__(self): - return ''.format(self.id) + return "".format(self.id) class Customer(FreshdeskModel): @@ -101,21 +110,23 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) + return "".format(self.name) + class Company(FreshdeskModel): def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) + return "".format(self.name) + class Agent(FreshdeskModel): def __str__(self): - return self.contact['name'] + return self.contact["name"] def __repr__(self): - return ''.format(self.id, self.contact['name']) + return "".format(self.id, self.contact["name"]) class Role(FreshdeskModel): @@ -123,5 +134,4 @@ def __str__(self): return self.name def __repr__(self): - return ''.format(self.name) - + return "".format(self.name) diff --git a/freshdesk/v2/tests/conftest.py b/freshdesk/v2/tests/conftest.py index 7e864c3..8474c53 100644 --- a/freshdesk/v2/tests/conftest.py +++ b/freshdesk/v2/tests/conftest.py @@ -6,102 +6,107 @@ from freshdesk.v2.api import API -DOMAIN = 'pythonfreshdesk.freshdesk.com' -API_KEY = 'MX4CEAw4FogInimEdRW2' +DOMAIN = "pythonfreshdesk.freshdesk.com" +API_KEY = "MX4CEAw4FogInimEdRW2" class MockedAPI(API): def __init__(self, *args): self.resolver = { - 'get': { - re.compile(r'tickets\?filter=new_and_my_open&page=1&per_page=100'): self.read_test_file( - 'all_tickets.json'), - re.compile(r'tickets\?filter=deleted&page=1&per_page=100'): self.read_test_file('all_tickets.json'), - re.compile(r'tickets\?filter=spam&page=1&per_page=100'): self.read_test_file('all_tickets.json'), - re.compile(r'tickets\?filter=watching&page=1&per_page=100'): self.read_test_file('all_tickets.json'), - re.compile(r'tickets\?page=1&per_page=100'): self.read_test_file('all_tickets.json'), - re.compile(r'tickets/1$'): self.read_test_file('ticket_1.json'), - re.compile(r'tickets/1/conversations'): self.read_test_file('conversations.json'), - re.compile(r'companies\?page=1&per_page=100$'): self.read_test_file('companies.json'), - re.compile(r'companies/1$'): self.read_test_file('company.json'), - re.compile(r'contacts\?page=1&per_page=100$'): self.read_test_file('contacts.json'), - re.compile(r'contacts/1$'): self.read_test_file('contact.json'), - re.compile(r'customers/1$'): self.read_test_file('customer.json'), - re.compile(r'groups\?page=1&per_page=100$'): self.read_test_file('groups.json'), - re.compile(r'groups/1$'): self.read_test_file('group_1.json'), - re.compile(r'roles$'): self.read_test_file('roles.json'), - re.compile(r'roles/1$'): self.read_test_file('role_1.json'), - re.compile(r'agents\?email=abc@xyz.com&page=1&per_page=100'): self.read_test_file('agent_1.json'), - re.compile(r'agents\?mobile=1234&page=1&per_page=100'): self.read_test_file('agent_1.json'), - re.compile(r'agents\?phone=5678&page=1&per_page=100'): self.read_test_file('agent_1.json'), - re.compile(r'agents\?state=fulltime&page=1&per_page=100'): self.read_test_file('agent_1.json'), - re.compile(r'agents\?page=1&per_page=100'): self.read_test_file('agents.json'), - re.compile(r'agents/1$'): self.read_test_file('agent_1.json'), - re.compile(r'search/tickets\?page=1&query="tag:\'mytag\'"'): self.read_test_file('search_tickets.json'), + "get": { + re.compile(r"tickets\?filter=new_and_my_open&page=1&per_page=100"): self.read_test_file( + "all_tickets.json" + ), + re.compile(r"tickets\?filter=deleted&page=1&per_page=100"): self.read_test_file("all_tickets.json"), + re.compile(r"tickets\?filter=spam&page=1&per_page=100"): self.read_test_file("all_tickets.json"), + re.compile(r"tickets\?filter=watching&page=1&per_page=100"): self.read_test_file("all_tickets.json"), + re.compile(r"tickets\?page=1&per_page=100"): self.read_test_file("all_tickets.json"), + re.compile(r"tickets/1$"): self.read_test_file("ticket_1.json"), + re.compile(r"tickets/1/conversations"): self.read_test_file("conversations.json"), + re.compile(r"companies\?page=1&per_page=100$"): self.read_test_file("companies.json"), + re.compile(r"companies/1$"): self.read_test_file("company.json"), + re.compile(r"contacts\?page=1&per_page=100$"): self.read_test_file("contacts.json"), + re.compile(r"contacts/1$"): self.read_test_file("contact.json"), + re.compile(r"customers/1$"): self.read_test_file("customer.json"), + re.compile(r"groups\?page=1&per_page=100$"): self.read_test_file("groups.json"), + re.compile(r"groups/1$"): self.read_test_file("group_1.json"), + re.compile(r"roles$"): self.read_test_file("roles.json"), + re.compile(r"roles/1$"): self.read_test_file("role_1.json"), + re.compile(r"agents\?email=abc@xyz.com&page=1&per_page=100"): self.read_test_file("agent_1.json"), + re.compile(r"agents\?mobile=1234&page=1&per_page=100"): self.read_test_file("agent_1.json"), + re.compile(r"agents\?phone=5678&page=1&per_page=100"): self.read_test_file("agent_1.json"), + re.compile(r"agents\?state=fulltime&page=1&per_page=100"): self.read_test_file("agent_1.json"), + re.compile(r"agents\?page=1&per_page=100"): self.read_test_file("agents.json"), + re.compile(r"agents/1$"): self.read_test_file("agent_1.json"), + re.compile(r'search/tickets\?page=1&query="tag:\'mytag\'"'): self.read_test_file("search_tickets.json"), }, - 'post': { - re.compile(r'tickets$'): self.read_test_file('ticket_1.json'), - re.compile(r'tickets/outbound_email$'): self.read_test_file('outbound_email_1.json'), - re.compile(r'tickets/1/notes$'): self.read_test_file('note_1.json'), - re.compile(r'tickets/1/reply$'): self.read_test_file('reply_1.json'), - re.compile(r'contacts$'): self.read_test_file('contact.json'), + "post": { + re.compile(r"tickets$"): self.read_test_file("ticket_1.json"), + re.compile(r"tickets/outbound_email$"): self.read_test_file("outbound_email_1.json"), + re.compile(r"tickets/1/notes$"): self.read_test_file("note_1.json"), + re.compile(r"tickets/1/reply$"): self.read_test_file("reply_1.json"), + re.compile(r"contacts$"): self.read_test_file("contact.json"), }, - 'put': { - re.compile(r'tickets/1$'): self.read_test_file('ticket_1_updated.json'), - re.compile(r'contacts/1$'): self.read_test_file('contact_updated.json'), - re.compile(r'contacts/1/restore$'): self.read_test_file('contact.json'), - re.compile(r'contacts/1/make_agent$'): self.read_test_file('contact_1_agent.json'), - re.compile(r'agents/1$'): self.read_test_file('agent_1_updated.json'), + "put": { + re.compile(r"tickets/1$"): self.read_test_file("ticket_1_updated.json"), + re.compile(r"contacts/1$"): self.read_test_file("contact_updated.json"), + re.compile(r"contacts/1/restore$"): self.read_test_file("contact.json"), + re.compile(r"contacts/1/make_agent$"): self.read_test_file("contact_1_agent.json"), + re.compile(r"agents/1$"): self.read_test_file("agent_1_updated.json"), + }, + "delete": { + re.compile(r"tickets/1$"): None, + re.compile(r"agents/1$"): None, + re.compile(r"contacts/1$"): None, + re.compile(r"contacts/1/hard_delete\?force=True$"): None, }, - 'delete': { - re.compile(r'tickets/1$'): None, - re.compile(r'agents/1$'): None, - re.compile(r'contacts/1$'): None, - re.compile(r'contacts/1/hard_delete\?force=True$'): None, - } } super(MockedAPI, self).__init__(*args) def read_test_file(self, filename): - path = os.path.join(os.path.dirname(__file__), 'sample_json_data', filename) - return json.loads(open(path, 'r').read()) + path = os.path.join(os.path.dirname(__file__), "sample_json_data", filename) + return json.loads(open(path, "r").read()) def _get(self, url, *args, **kwargs): - for pattern, data in self.resolver['get'].items(): + for pattern, data in self.resolver["get"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_get() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_get() has no pattern for '{}'".format(url)) def _post(self, url, *args, **kwargs): - for pattern, data in self.resolver['post'].items(): + for pattern, data in self.resolver["post"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_post() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_post() has no pattern for '{}'".format(url)) def _put(self, url, *args, **kwargs): - for pattern, data in self.resolver['put'].items(): + for pattern, data in self.resolver["put"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_put() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_put() has no pattern for '{}'".format(url)) def _delete(self, url, *args, **kwargs): - for pattern, data in self.resolver['delete'].items(): + for pattern, data in self.resolver["delete"].items(): if pattern.match(url): return data # No match found, raise 404 from requests.exceptions import HTTPError - raise HTTPError('404: mocked_api_delete() has no pattern for \'{}\''.format(url)) + + raise HTTPError("404: mocked_api_delete() has no pattern for '{}'".format(url)) @pytest.fixture() diff --git a/freshdesk/v2/tests/test_agent.py b/freshdesk/v2/tests/test_agent.py index d635910..55555db 100644 --- a/freshdesk/v2/tests/test_agent.py +++ b/freshdesk/v2/tests/test_agent.py @@ -14,38 +14,33 @@ def agent(api): @pytest.fixture def agent_json(): - return json.loads(open(os.path.join(os.path.dirname(__file__), 'sample_json_data', 'agent_1.json')).read()) + return json.loads(open(os.path.join(os.path.dirname(__file__), "sample_json_data", "agent_1.json")).read()) def test_str(agent): - assert str(agent) == 'Rachel' + assert str(agent) == "Rachel" def test_repr(agent): - assert repr(agent) == '' + assert repr(agent) == "" def test_get_agent(agent): assert isinstance(agent, Agent) assert agent.id == 1 - assert agent.contact['name'] == 'Rachel' - assert agent.contact['email'] == 'rachel@freshdesk.com' - assert agent.contact['mobile'] == 1234 - assert agent.contact['phone'] == 5678 + assert agent.contact["name"] == "Rachel" + assert agent.contact["email"] == "rachel@freshdesk.com" + assert agent.contact["mobile"] == 1234 + assert agent.contact["phone"] == 5678 assert agent.occasional is False def test_update_agent(api): - values = { - 'occasional': True, - 'contact': { - 'name': 'Updated Name' - } - } + values = {"occasional": True, "contact": {"name": "Updated Name"}} agent = api.agents.update_agent(1, **values) assert agent.occasional is True - assert agent.contact['name'] == 'Updated Name' + assert agent.contact["name"] == "Updated Name" def test_delete_agent(api): @@ -53,11 +48,11 @@ def test_delete_agent(api): def test_agent_name(agent): - assert agent.contact['name'] == 'Rachel' + assert agent.contact["name"] == "Rachel" def test_agent_mobile(agent): - assert agent.contact['mobile'] == 1234 + assert agent.contact["mobile"] == 1234 def test_agent_state(agent): diff --git a/freshdesk/v2/tests/test_api_class.py b/freshdesk/v2/tests/test_api_class.py index e128d9e..87c2009 100644 --- a/freshdesk/v2/tests/test_api_class.py +++ b/freshdesk/v2/tests/test_api_class.py @@ -3,7 +3,10 @@ from freshdesk.v2.api import API from freshdesk.v2.errors import ( - FreshdeskBadRequest, FreshdeskAccessDenied, FreshdeskNotFound, FreshdeskError, + FreshdeskBadRequest, + FreshdeskAccessDenied, + FreshdeskNotFound, + FreshdeskError, FreshdeskRateLimited, FreshdeskServerError, ) @@ -12,27 +15,31 @@ def test_custom_cname(): with pytest.raises(AttributeError): - API('custom_cname_domain', 'invalid_api_key') + API("custom_cname_domain", "invalid_api_key") def test_api_prefix(): - api = API('test_domain.freshdesk.com', 'test_key') - assert api._api_prefix == 'https://test_domain.freshdesk.com/api/v2/' - api = API('test_domain.freshdesk.com/', 'test_key') - assert api._api_prefix == 'https://test_domain.freshdesk.com/api/v2/' + api = API("test_domain.freshdesk.com", "test_key") + assert api._api_prefix == "https://test_domain.freshdesk.com/api/v2/" + api = API("test_domain.freshdesk.com/", "test_key") + assert api._api_prefix == "https://test_domain.freshdesk.com/api/v2/" @responses.activate @pytest.mark.parametrize( - ('status_code', 'exception'), - [(400, FreshdeskBadRequest), (403, FreshdeskAccessDenied), (404, FreshdeskNotFound), - (418, FreshdeskError), (429, FreshdeskRateLimited), (502, FreshdeskServerError)] + ("status_code", "exception"), + [ + (400, FreshdeskBadRequest), + (403, FreshdeskAccessDenied), + (404, FreshdeskNotFound), + (418, FreshdeskError), + (429, FreshdeskRateLimited), + (502, FreshdeskServerError), + ], ) def test_errors(status_code, exception): - responses.add(responses.GET, - 'https://{}/api/v2/tickets/1'.format(DOMAIN), - status=status_code) + responses.add(responses.GET, "https://{}/api/v2/tickets/1".format(DOMAIN), status=status_code) - api = API('pythonfreshdesk.freshdesk.com', 'test_key') + api = API("pythonfreshdesk.freshdesk.com", "test_key") with pytest.raises(exception): api.tickets.get_ticket(1) diff --git a/freshdesk/v2/tests/test_comment.py b/freshdesk/v2/tests/test_comment.py index b841941..e53ced0 100644 --- a/freshdesk/v2/tests/test_comment.py +++ b/freshdesk/v2/tests/test_comment.py @@ -15,22 +15,22 @@ def test_comments_list(comments): def test_comment_str(comments): - assert str(comments[0]) == 'This is a private note' + assert str(comments[0]) == "This is a private note" def test_comment_repr(comments): - assert repr(comments[0]) == '' + assert repr(comments[0]) == "" def test_create_note(api): - comment = api.comments.create_note(1, 'This is a private note') + comment = api.comments.create_note(1, "This is a private note") assert isinstance(comment, Comment) - assert comment.body_text == 'This is a private note' - assert comment.source == 'note' + assert comment.body_text == "This is a private note" + assert comment.source == "note" def test_create_reply(api): - comment = api.comments.create_reply(1, 'This is a reply') + comment = api.comments.create_reply(1, "This is a reply") assert isinstance(comment, Comment) - assert comment.body_text == 'This is a reply' - assert comment.source == 'reply' + assert comment.body_text == "This is a reply" + assert comment.source == "reply" diff --git a/freshdesk/v2/tests/test_company.py b/freshdesk/v2/tests/test_company.py index c629f14..88bb188 100644 --- a/freshdesk/v2/tests/test_company.py +++ b/freshdesk/v2/tests/test_company.py @@ -12,11 +12,11 @@ def company(api): def test_get_company(company): assert isinstance(company, Company) assert company.id == 1 - assert company.name == 'Super Nova' - assert company.description == 'Space Shuttle Manufacturing' + assert company.name == "Super Nova" + assert company.description == "Space Shuttle Manufacturing" assert company.domains == ["supernova", "nova", "super"] assert company.industry is None - assert company.custom_fields.get('website') == 'https://www.supernova.org' + assert company.custom_fields.get("website") == "https://www.supernova.org" def test_contact_datetime(company): @@ -25,11 +25,11 @@ def test_contact_datetime(company): def test_company_str(company): - assert str(company) == 'Super Nova' + assert str(company) == "Super Nova" def test_company_repr(company): - assert repr(company) == '' + assert repr(company) == "" def test_list_companies(api, company): diff --git a/freshdesk/v2/tests/test_contact.py b/freshdesk/v2/tests/test_contact.py index 73d6980..3832191 100644 --- a/freshdesk/v2/tests/test_contact.py +++ b/freshdesk/v2/tests/test_contact.py @@ -12,8 +12,8 @@ def contact(api): def test_get_contact(contact): assert isinstance(contact, Contact) - assert contact.name == 'Rachel' - assert contact.email == 'rachel@freshdesk.com' + assert contact.name == "Rachel" + assert contact.email == "rachel@freshdesk.com" assert contact.helpdesk_agent is False assert contact.customer_id == 1 @@ -27,10 +27,7 @@ def test_list_contact(api, contact): def test_create_contact(api): - contact_data = { - 'name': 'Rachel', - 'email': 'rachel@freshdesk.com' - } + contact_data = {"name": "Rachel", "email": "rachel@freshdesk.com"} contact = api.contacts.create_contact(contact_data) assert isinstance(contact, Contact) assert contact.email == contact.email @@ -38,12 +35,10 @@ def test_create_contact(api): def test_update_contact(api): - contact_data = { - 'name': 'New Name' - } + contact_data = {"name": "New Name"} contact = api.contacts.update_contact(1, **contact_data) assert isinstance(contact, Contact) - assert contact.name == 'New Name' + assert contact.name == "New Name" def test_soft_delete_contact(api): @@ -66,8 +61,8 @@ def test_make_agent(api, contact): assert isinstance(agent, Agent) assert agent.available is True assert agent.occasional is False - assert agent.contact['email'] == contact.email - assert agent.contact['name'] == contact.name + assert agent.contact["email"] == contact.email + assert agent.contact["name"] == contact.name def test_contact_datetime(contact): @@ -76,8 +71,8 @@ def test_contact_datetime(contact): def test_contact_str(contact): - assert str(contact) == 'Rachel' + assert str(contact) == "Rachel" def test_contact_repr(contact): - assert repr(contact) == '' + assert repr(contact) == "" diff --git a/freshdesk/v2/tests/test_customer.py b/freshdesk/v2/tests/test_customer.py index 1964d69..2bf2c09 100644 --- a/freshdesk/v2/tests/test_customer.py +++ b/freshdesk/v2/tests/test_customer.py @@ -7,7 +7,7 @@ @pytest.fixture def customer(api): - return api.customers.get_customer('1') + return api.customers.get_customer("1") @pytest.fixture @@ -17,9 +17,9 @@ def contact(api): def test_customer(customer): assert isinstance(customer, Customer) - assert customer.name == 'ACME Corp.' - assert customer.domains == 'acme.com' - assert customer.cf_custom_key == 'custom_value' + assert customer.name == "ACME Corp." + assert customer.domains == "acme.com" + assert customer.cf_custom_key == "custom_value" def test_customer_datetime(customer): @@ -28,11 +28,11 @@ def test_customer_datetime(customer): def test_customer_str(customer): - assert str(customer) == 'ACME Corp.' + assert str(customer) == "ACME Corp." def test_customer_repr(customer): - assert repr(customer) == '' + assert repr(customer) == "" def test_get_customer_from_contact(api, contact): diff --git a/freshdesk/v2/tests/test_group.py b/freshdesk/v2/tests/test_group.py index 931d3b2..62572b0 100644 --- a/freshdesk/v2/tests/test_group.py +++ b/freshdesk/v2/tests/test_group.py @@ -19,8 +19,8 @@ def test_list_groups(api, group): def test_group(group): assert isinstance(group, Group) - assert group.name == 'Entertainers' - assert group.description == 'Singers dancers and stand up comedians' + assert group.name == "Entertainers" + assert group.description == "Singers dancers and stand up comedians" def test_group_datetime(group): @@ -29,8 +29,8 @@ def test_group_datetime(group): def test_group_str(group): - assert str(group) == 'Entertainers' + assert str(group) == "Entertainers" def test_group_repr(group): - assert repr(group) == '' + assert repr(group) == "" diff --git a/freshdesk/v2/tests/test_role.py b/freshdesk/v2/tests/test_role.py index 32879ac..ee3f9f1 100644 --- a/freshdesk/v2/tests/test_role.py +++ b/freshdesk/v2/tests/test_role.py @@ -19,8 +19,8 @@ def test_list_roles(api, role): def test_role(role): assert isinstance(role, Role) - assert role.name == 'Agent' - assert role.description, 'Can log, view, reply == update and resolve tickets and manage contacts.' + assert role.name == "Agent" + assert role.description, "Can log, view, reply == update and resolve tickets and manage contacts." def test_group_datetime(role): @@ -29,4 +29,4 @@ def test_group_datetime(role): def test_group_repr(role): - assert repr(role) == '' + assert repr(role) == "" diff --git a/freshdesk/v2/tests/test_ticket.py b/freshdesk/v2/tests/test_ticket.py index 2c7aa99..9f02063 100644 --- a/freshdesk/v2/tests/test_ticket.py +++ b/freshdesk/v2/tests/test_ticket.py @@ -15,141 +15,126 @@ def ticket(api): @pytest.fixture def ticket_json(): - return json.loads(open(os.path.join(os.path.dirname(__file__), 'sample_json_data', 'ticket_1.json')).read()) + return json.loads(open(os.path.join(os.path.dirname(__file__), "sample_json_data", "ticket_1.json")).read()) @pytest.fixture def outbound_email_json(api): - return json.loads( - open(os.path.join(os.path.dirname(__file__), 'sample_json_data', 'outbound_email_1.json')).read()) + return json.loads(open(os.path.join(os.path.dirname(__file__), "sample_json_data", "outbound_email_1.json")).read()) def test_str(ticket): - assert str(ticket) == 'This is a sample ticket' + assert str(ticket) == "This is a sample ticket" def test_repr(ticket): - assert repr(ticket) == '' + assert repr(ticket) == "" def test_get_ticket(ticket): assert isinstance(ticket, Ticket) assert ticket.id == 1 - assert ticket.subject == 'This is a sample ticket' - assert ticket.description_text, 'This is a sample ticket, feel free to delete it.' - assert ticket.cc_emails == ['test2@example.com'] - assert 'foo' in ticket.tags - assert 'bar' in ticket.tags + assert ticket.subject == "This is a sample ticket" + assert ticket.description_text, "This is a sample ticket, feel free to delete it." + assert ticket.cc_emails == ["test2@example.com"] + assert "foo" in ticket.tags + assert "bar" in ticket.tags def test_create_ticket(api): ticket = api.tickets.create_ticket( - 'This is a sample ticket', - description='This is a sample ticket, feel free to delete it.', - email='test@example.com', - priority=1, status=2, - tags=['foo', 'bar'], - cc_emails=['test2@example.com', 'test3@example.com'], - custom_fields={'power': 11, 'importance': 'very'}, + "This is a sample ticket", + description="This is a sample ticket, feel free to delete it.", + email="test@example.com", + priority=1, + status=2, + tags=["foo", "bar"], + cc_emails=["test2@example.com", "test3@example.com"], + custom_fields={"power": 11, "importance": "very"}, ) assert isinstance(ticket, Ticket) - assert ticket.subject == 'This is a sample ticket' - assert ticket.description_text, 'This is a sample ticket, feel free to delete it.' - assert ticket.priority == 'low' - assert ticket.status == 'open' - assert ticket.cc_emails == ['test2@example.com'] - assert 'foo' in ticket.tags - assert 'bar' in ticket.tags + assert ticket.subject == "This is a sample ticket" + assert ticket.description_text, "This is a sample ticket, feel free to delete it." + assert ticket.priority == "low" + assert ticket.status == "open" + assert ticket.cc_emails == ["test2@example.com"] + assert "foo" in ticket.tags + assert "bar" in ticket.tags def test_create_ticket_with_attachments(api): - attachment_path = os.path.join(os.path.dirname(__file__), 'sample_json_data', 'attachment.txt') - with patch.object(api, '_post', wraps=api._post) as post_mock: + attachment_path = os.path.join(os.path.dirname(__file__), "sample_json_data", "attachment.txt") + with patch.object(api, "_post", wraps=api._post) as post_mock: ticket = api.tickets.create_ticket( - 'This is a sample ticket with an attachment', - description='This is a sample ticket, feel free to delete it.', - email='test@example.com', - priority=1, status=2, - cc_emails=['test2@example.com', 'test3@example.com'], - custom_fields={'power': 11, 'importance': 'very'}, - attachments=(attachment_path,) + "This is a sample ticket with an attachment", + description="This is a sample ticket, feel free to delete it.", + email="test@example.com", + priority=1, + status=2, + cc_emails=["test2@example.com", "test3@example.com"], + custom_fields={"power": 11, "importance": "very"}, + attachments=(attachment_path,), ) post_mock.assert_called_once_with( - 'tickets', + "tickets", data={ - 'subject': 'This is a sample ticket with an attachment', - 'status': 2, - 'priority': 1, - 'description': 'This is a sample ticket, feel free to delete it.', - 'email': 'test@example.com', + "subject": "This is a sample ticket with an attachment", + "status": 2, + "priority": 1, + "description": "This is a sample ticket, feel free to delete it.", + "email": "test@example.com", # List argument names should be sent as arrays, otherwise it's not deserialized correctly. - 'cc_emails[]': ['test2@example.com', 'test3@example.com'], + "cc_emails[]": ["test2@example.com", "test3@example.com"], # Dict arguments must unrolled into indexed arrays to work properly with the form-data encoding. - 'custom_fields[power]': 11, - 'custom_fields[importance]': 'very' + "custom_fields[power]": 11, + "custom_fields[importance]": "very", }, - files=[('attachments[]', ('attachment.txt', ANY, None))], + files=[("attachments[]", ("attachment.txt", ANY, None))], # Content-type should be unset so that `requests` uses "multipart/form-data" instead of application/json. - headers={'Content-Type': None} + headers={"Content-Type": None}, ) assert isinstance(ticket, Ticket) - assert ticket.subject == 'This is a sample ticket' - assert ticket.description_text, 'This is a sample ticket, feel free to delete it.' - assert ticket.priority == 'low' - assert ticket.status == 'open' - assert ticket.cc_emails == ['test2@example.com'] - assert ticket.attachments[0]['name'] == 'attachment.txt' - assert 'foo' in ticket.tags - assert 'bar' in ticket.tags + assert ticket.subject == "This is a sample ticket" + assert ticket.description_text, "This is a sample ticket, feel free to delete it." + assert ticket.priority == "low" + assert ticket.status == "open" + assert ticket.cc_emails == ["test2@example.com"] + assert ticket.attachments[0]["name"] == "attachment.txt" + assert "foo" in ticket.tags + assert "bar" in ticket.tags def test_create_outbound_email(api, outbound_email_json): j = outbound_email_json.copy() - email = 'test@example.com' - subject = 'This is a sample outbound email' - description = 'This is a sample outbound email, feel free to delete it.' + email = "test@example.com" + subject = "This is a sample outbound email" + description = "This is a sample outbound email, feel free to delete it." email_config_id = 5000054536 - values = { - 'status': 5, - 'priority': 1, - 'tags': ['foo', 'bar'], - 'cc_emails': ['test2@example.com'] - } - - email = api.tickets.create_outbound_email( - subject, - description, - email, - email_config_id, - **values - ) + values = {"status": 5, "priority": 1, "tags": ["foo", "bar"], "cc_emails": ["test2@example.com"]} + + email = api.tickets.create_outbound_email(subject, description, email, email_config_id, **values) - assert email.description_text == j['description_text'] - assert email._priority == j['priority'] - assert email._status == j['status'] - assert email.cc_emails == j['cc_emails'] - assert 'foo' in email.tags - assert 'bar' in email.tags + assert email.description_text == j["description_text"] + assert email._priority == j["priority"] + assert email._status == j["status"] + assert email.cc_emails == j["cc_emails"] + assert "foo" in email.tags + assert "bar" in email.tags def test_update_ticket(api, ticket_json): j = ticket_json.copy() - values = { - 'subject': 'Test subject update', - 'priority': 3, - 'status': 4, - 'tags': ['hello', 'world'] - } + values = {"subject": "Test subject update", "priority": 3, "status": 4, "tags": ["hello", "world"]} j.update(values) - ticket = api.tickets.update_ticket(j['id'], **values) - assert ticket.subject == 'Test subject update' - assert ticket.status == 'resolved' - assert ticket.priority == 'high' - assert 'hello' in ticket.tags - assert 'world' in ticket.tags + ticket = api.tickets.update_ticket(j["id"], **values) + assert ticket.subject == "Test subject update" + assert ticket.status == "resolved" + assert ticket.priority == "high" + assert "hello" in ticket.tags + assert "world" in ticket.tags def test_delete_ticket(api): @@ -158,17 +143,17 @@ def test_delete_ticket(api): def test_ticket_priority(ticket): assert ticket._priority == 1 - assert ticket.priority == 'low' + assert ticket.priority == "low" def test_ticket_status(ticket): assert ticket._status == 2 - assert ticket.status == 'open' + assert ticket.status == "open" def test_ticket_source(ticket): assert ticket._source == 2 - assert ticket.source == 'portal' + assert ticket.source == "portal" def test_ticket_datetime(ticket): @@ -197,7 +182,7 @@ def test_watched_tickets(api, ticket): def test_spam_tickets(api): - tickets = api.tickets.list_tickets(filter_name='spam') + tickets = api.tickets.list_tickets(filter_name="spam") assert isinstance(tickets, list) assert len(tickets) == 1 @@ -220,4 +205,4 @@ def test_filter_query(api, ticket): tickets = api.tickets.filter_tickets(query="tag:'mytag'") assert isinstance(tickets, list) assert len(tickets) == 2 - assert 'mytag' in tickets[0].tags + assert "mytag" in tickets[0].tags diff --git a/setup.py b/setup.py index d3d751a..5baf0c2 100644 --- a/setup.py +++ b/setup.py @@ -3,13 +3,13 @@ from freshdesk import __version__ setup( - name='python-freshdesk', + name="python-freshdesk", version=__version__, - license='BSD', - author='Sam Kingston', - author_email='sam@sjkwi.com.au', - description='An API for the Freshdesk helpdesk', - url='https://github.com/sjkingo/python-freshdesk', - install_requires=['requests', 'python-dateutil'], + license="BSD", + author="Sam Kingston", + author_email="sam@sjkwi.com.au", + description="An API for the Freshdesk helpdesk", + url="https://github.com/sjkingo/python-freshdesk", + install_requires=["requests", "python-dateutil"], packages=find_packages(), )