Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add support for subdomain #52

Merged
merged 13 commits into from
Oct 29, 2024
Merged
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
command: |
source dev_env.sh
source /usr/local/share/virtualenvs/tap-zendesk-chat/bin/activate
pylint tap_zendesk_chat -d "$PYLINT_DISABLE_LIST,no-self-use"
pylint tap_zendesk_chat -d "$PYLINT_DISABLE_LIST"
- run:
name: 'JSON Validator'
command: |
Expand Down
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Changelog
## 0.4.0

## 0.5.0
* Zendesk Domain Change [#52](https://github.com/singer-io/tap-zendesk-chat/pull/52)

## 0.4.1
* Dependabot update [#50](https://github.com/singer-io/tap-zendesk-chat/pull/50)


## 0.4.0
* Code Refactoring [#45](https://github.com/singer-io/tap-zendesk-chat/pull/45)
- Improved directory structure
- Added pagination support to BANS stream
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

setup(
name="tap-zendesk-chat",
version="0.4.1",
version="0.5.0",
description="Singer.io tap for extracting data from the Zendesk Chat API",
author="Stitch",
url="https://singer.io",
Expand Down
37 changes: 35 additions & 2 deletions tap_zendesk_chat/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,54 @@
class RateLimitException(Exception):
pass

class InvalidConfigurationError(Exception):
pass


class Client:
def __init__(self, config):
self.access_token = config["access_token"]
self.user_agent = config.get("user_agent", "tap-zendesk-chat")
self.headers = {}
self.subdomain = config.get("subdomain")
self.headers["Authorization"] = f"Bearer {self.access_token}"
self.headers["User-Agent"] = self.user_agent
self.base_url = self.get_base_url()
self.session = requests.Session()

def get_base_url(self):
"""
Determines the base URL to use for Zendesk API requests.

Checks the availability of zendesk chat endpoints
and returns the available one
Returns:
str: The base URL to use for subsequent API requests.

Raises:
InvalidConfigurationError: If neither endpoint is accessible.
"""
urls = [
(f"https://{self.subdomain}.zendesk.com" , "/api/v2/chat/agents"),
prijendev marked this conversation as resolved.
Show resolved Hide resolved
(BASE_URL , "/api/v2/agents")
]
if not self.subdomain:
# return base url incase of missing subdomain
return BASE_URL
for domain, endpoint in urls:
resp = requests.get(f"{domain}{endpoint}", headers=self.headers, timeout=25)
LOGGER.info("API CHECK %s %s", resp.url, resp.status_code)
if resp.status_code == 200:
return domain
raise InvalidConfigurationError("Please check the URL or reauthenticate")

@backoff.on_exception(backoff.expo, RateLimitException, max_tries=10, factor=2)
def request(self, tap_stream_id, params=None, url=None, url_extra=""):
with metrics.http_request_timer(tap_stream_id) as timer:

url = url or f"{BASE_URL}/api/v2/{tap_stream_id}{url_extra}"
if self.base_url == BASE_URL:
url = url or f"{self.base_url}/api/v2/{tap_stream_id}{url_extra}"
else:
url = url or f"{self.base_url}/api/v2/chat/{tap_stream_id}{url_extra}"
LOGGER.info("calling %s %s", url, params)
response = self.session.get(url, headers=self.headers, params=params)
timer.tags[metrics.Tag.http_status_code] = response.status_code
Expand Down
1 change: 1 addition & 0 deletions tap_zendesk_chat/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def _bulk_chats(self, ctx, chat_ids: List):
body = ctx.client.request(self.tap_stream_id, params=params)
return list(body["docs"].values())

# pylint: disable=too-many-positional-arguments
def _pull(self, ctx, chat_type, ts_field, full_sync, schema: Dict, stream_metadata: Dict, transformer: Transformer):
"""Pulls and writes pages of data for the given chat_type, where
chat_type can be either "chat" or "offline_msg".
Expand Down
17 changes: 13 additions & 4 deletions tests/unittests/test_auth_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from requests.exceptions import HTTPError

import tap_zendesk_chat
from tap_zendesk_chat.http import Client
from tap_zendesk_chat.http import Client, InvalidConfigurationError


# Mock args
Expand All @@ -13,6 +13,7 @@ def __init__(self):
self.discover = True
self.properties = False
self.config = {"access_token": "abc-def"}
self.config_wd_subdomain = {"access_token": "abc-def", "subdomain":"test"}
self.state = False
self.properties = {}

Expand Down Expand Up @@ -43,9 +44,16 @@ def test_basic_auth_no_access_401(self):

with self.assertRaises(HTTPError) as e:
tap_zendesk_chat.discover(args.config)
# Verifying the message formed for the custom exception
expected_error_message = "401 Client Error: Unauthorized for url:"
self.assertIn(expected_error_message, str(e.exception))
# Verifying the message formed for the custom exception
expected_error_message = "401 Client Error: Unauthorized for url:"
self.assertIn(expected_error_message, str(e.exception))

expected_error_message = "Please check the URL or reauthenticate"

with self.assertRaises(InvalidConfigurationError) as e:
tap_zendesk_chat.discover(args.config_wd_subdomain)
self.assertIn(expected_error_message, str(e.exception))


@mock.patch("tap_zendesk_chat.utils", return_value=Args())
@mock.patch("singer.catalog.Catalog.from_dict", return_value={"key": "value"})
Expand All @@ -63,6 +71,7 @@ def test_discovery(self, mock_utils, mock_catalog, mock_request):
self.assertEqual(tap_zendesk_chat.discover(Args().config), expected)



class TestAccountEndpointAuthorized(unittest.TestCase):
def test_is_account_not_authorized_404(self):
"""tests if account_not_authorized method in discover raises http
Expand Down