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

🎉 Source Zendesk Sunshine: support oauth #7976

Merged
merged 10 commits into from
Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"sourceDefinitionId": "325e0640-e7b3-4e24-b823-3361008f603f",
"name": "Zendesk Sunshine",
"dockerRepository": "airbyte/source-zendesk-sunshine",
"dockerImageTag": "0.1.0",
"dockerImageTag": "0.1.1",
"documentationUrl": "https://docs.airbyte.io/integrations/sources/zendesk-sunshine"
}
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@
- name: Zendesk Sunshine
sourceDefinitionId: 325e0640-e7b3-4e24-b823-3361008f603f
dockerRepository: airbyte/source-zendesk-sunshine
dockerImageTag: 0.1.0
dockerImageTag: 0.1.1
documentationUrl: https://docs.airbyte.io/integrations/sources/zendesk-sunshine
sourceType: api
- name: Zendesk Support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ RUN pip install .
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.1.0
LABEL io.airbyte.version=0.1.1
LABEL io.airbyte.name=airbyte/source-zendesk-sunshine
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ tests:
connection:
- config_path: "secrets/config.json"
status: "succeed"
- config_path: "secrets/config_oauth.json"
status: "succeed"
- config_path: "secrets/config_api_token.json"
status: "succeed"
- config_path: "integration_tests/invalid_config.json"
status: "failed"
- config_path: "integration_tests/invalid_config_api_token.json"
status: "failed"
- config_path: "integration_tests/invalid_config_oauth.json"
status: "failed"
discovery:
- config_path: "secrets/config.json"
basic_read:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"credentials": {
"auth_type": "api_token",
"email": "test@ayhghghte.io",
"api_token": "fgfgvf ghnbvg hnghbvnhbvnvbn"
},
"subdomain": "d3v-airbyte",
"start_date": "2020-01-01T00:00:00Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"credentials": {
"auth_type": "oauth2.0",
"client_id": "some_client_id",
"client_secret": "some_client_secret",
"access_token": "some_access_token"
},
"subdomain": "d3v-airbyte",
"start_date": "2020-01-01T00:00:00Z"
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from airbyte_cdk.sources import AbstractSource
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http.auth import TokenAuthenticator
from requests.auth import AuthBase

from .streams import Limits, ObjectRecords, ObjectTypePolicies, ObjectTypes, RelationshipRecords, RelationshipTypes

Expand All @@ -23,11 +24,37 @@ def __init__(self, auth: Tuple[str, str], auth_method: str = "Basic", **kwargs):
super().__init__(token=b64_encoded, auth_method=auth_method, **kwargs)


class ZendeskSunshineAuthenticator:
"""Provides the authentication capabilities for both old and new methods."""

@staticmethod
def get_auth(config: Mapping[str, Any]) -> AuthBase:
credentials = config.get("credentials", {})
auth_method = credentials.get("auth_method")

if auth_method == "api_token" or not credentials:
api_token = credentials.get("api_token") or config.get("api_token")
if not api_token:
raise Exception("No api_token in creds")
email = credentials.get("email") or config.get("email")
if not email:
raise Exception("No email in creds")
auth = Base64HttpAuthenticator(auth=(f"{email}/token", api_token))

elif auth_method == "oauth2.0":
auth = TokenAuthenticator(token=credentials["access_token"])

else:
raise Exception(f"Invalid auth method: {auth_method}")

return auth


class SourceZendeskSunshine(AbstractSource):
def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, Any]:
try:
pendulum.parse(config["start_date"], strict=True)
authenticator = Base64HttpAuthenticator(auth=(f'{config["email"]}/token', config["api_token"]))
authenticator = ZendeskSunshineAuthenticator.get_auth(config)
stream = Limits(authenticator=authenticator, subdomain=config["subdomain"], start_date=pendulum.parse(config["start_date"]))
records = stream.read_records(sync_mode=SyncMode.full_refresh)
next(records)
Expand All @@ -47,7 +74,7 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]:
After this time is passed we have no data. It will require permanent population, to pass
the test criteria `stream should contain at least 1 record)
"""
authenticator = Base64HttpAuthenticator(auth=(f'{config["email"]}/token', config["api_token"]))
authenticator = ZendeskSunshineAuthenticator.get_auth(config)
args = {"authenticator": authenticator, "subdomain": config["subdomain"], "start_date": config["start_date"]}
return [
ObjectTypes(**args),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,9 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Zendesk Sunshine Spec",
"type": "object",
"required": ["api_token", "email", "start_date", "subdomain"],
"additionalProperties": false,
"required": ["start_date", "subdomain"],
"additionalProperties": true,
"properties": {
"api_token": {
"type": "string",
"airbyte_secret": true,
"description": "API Token. See the <a href=\"https://docs.airbyte.io/integrations/sources/zendesk_sunshine\">docs</a> for information on how to generate this key."
},
"email": {
"type": "string",
"description": "The user email for your Zendesk account"
},
"subdomain": {
"type": "string",
"description": "The subdomain for your Zendesk Account"
Expand All @@ -25,8 +16,79 @@
"type": "string",
"description": "The date from which you'd like to replicate the data",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
"examples": "2021-01-01T00:00:00.000000Z"
"examples": ["2021-01-01T00:00:00.000000Z"]
},
"credentials": {
"title": "Authorization Method",
"type": "object",
"oneOf": [
{
"type": "object",
"title": "OAuth2.0",
"required": ["access_token"],
"properties": {
"auth_method": {
"type": "string",
"const": "oauth2.0",
"enum": ["oauth2.0"],
"default": "oauth2.0",
"order": 0
},
"client_id": {
"type": "string",
"title": "Client ID",
"description": "The Client ID of your OAuth application.",
"airbyte_secret": true
},
"client_secret": {
"type": "string",
"title": "Client Secret",
"description": "The Client Secret of your OAuth application.",
"airbyte_secret": true
},
"access_token": {
"type": "string",
"title": "Access Token",
"description": "Long-term access Token for making authenticated requests.",
"airbyte_secret": true
}
}
},
{
"type": "object",
"title": "API Token",
"required": ["api_token", "email"],
"properties": {
"auth_method": {
"type": "string",
"const": "api_token",
"enum": ["api_token"],
"default": "api_token",
"order": 1
},
"api_token": {
"type": "string",
"title": "API Token",
"description": "API Token. See the <a href=\"https://docs.airbyte.io/integrations/sources/zendesk_sunshine\">docs</a> for information on how to generate this key.",
"airbyte_secret": true
},
"email": {
"type": "string",
"title": "Email",
"description": "The user email for your Zendesk account"
}
}
}
]
}
}
},
"authSpecification": {
"auth_type": "oauth2.0",
"oauth2Specification": {
"rootObject": ["credentials", "0"],
"oauthFlowInitParameters": [["client_id"], ["client_secret"]],
"oauthFlowOutputParameters": [["access_token"]]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


class SunshineStream(HttpStream, ABC):
primary_key = "id"
primary_key = None
data_field = "data"
page_size = 100

Expand Down
8 changes: 6 additions & 2 deletions docs/integrations/sources/zendesk-sunshine.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,23 @@ The Zendesk connector should not run into Zendesk API limitations under normal u

### Requirements

* Zendesk Sunshine Access Token
* Zendesk Sunshine API Token

OR
* Zendesk Sunshine oauth2.0 application (client_id, client_secret, access_token)

### Setup guide

Please follow this [guide](https://developer.zendesk.com/documentation/custom-data/custom-objects/getting-started-with-custom-objects/#enabling-custom-objects)

Generate a Access Token as described in [here](https://developer.zendesk.com/api-reference/ticketing/introduction/#security-and-authentication)
Generate an API Token or oauth2.0 Access token as described in [here](https://developer.zendesk.com/api-reference/ticketing/introduction/#security-and-authentication)

We recommend creating a restricted, read-only key specifically for Airbyte access. This will allow you to control which resources Airbyte should be able to access.

## Changelog

| Version | Date | Pull Request | Subject |
| :--- | :--- | :--- | :--- |
| 0.1.1 | 2021-11-15 | [7976](https://github.com/airbytehq/airbyte/pull/7976) | Add oauth2.0 support |
| 0.1.0 | 2021-07-08 | [4359](https://github.com/airbytehq/airbyte/pull/4359) | Initial Release |

2 changes: 2 additions & 0 deletions tools/bin/ci_credentials.sh
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ read_secrets source-typeform "$SOURCE_TYPEFORM_CREDS"
read_secrets source-us-census "$SOURCE_US_CENSUS_TEST_CREDS"
read_secrets source-zendesk-chat "$ZENDESK_CHAT_INTEGRATION_TEST_CREDS"
read_secrets source-zendesk-sunshine "$ZENDESK_SUNSHINE_TEST_CREDS"
read_secrets source-zendesk-sunshine "$ZENDESK_SUNSHINE_TEST_API_TOKEN_CREDS" "config_api_token.json"
read_secrets source-zendesk-sunshine "$ZENDESK_SUNSHINE_TEST_OAUTH_CREDS" "config_oauth.json"
read_secrets source-zendesk-support "$ZENDESK_SUPPORT_TEST_CREDS"
read_secrets source-zendesk-support "$ZENDESK_SUPPORT_OAUTH_TEST_CREDS" "config_oauth.json"
read_secrets source-zendesk-talk "$ZENDESK_TALK_TEST_CREDS"
Expand Down