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

Trello source: Add oAuth flow #6968

Merged
merged 3 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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": "8da67652-004c-11ec-9a03-0242ac130003",
"name": "Trello",
"dockerRepository": "airbyte/source-trello",
"dockerImageTag": "0.1.0",
"dockerImageTag": "0.1.1",
"documentationUrl": "https://docs.airbyte.io/integrations/sources/trello"
}
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@
- sourceDefinitionId: 8da67652-004c-11ec-9a03-0242ac130003
name: Trello
dockerRepository: airbyte/source-trello
dockerImageTag: 0.1.0
dockerImageTag: 0.1.1
documentationUrl: https://docs.airbyte.io/integrations/sources/trello
sourceType: api
- sourceDefinitionId: 374ebc65-6636-4ea0-925c-7d35999a8ffc
Expand Down
2 changes: 1 addition & 1 deletion airbyte-integrations/connectors/source-trello/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ COPY source_trello ./source_trello
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-trello
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ class SourceTrello(AbstractSource):
Source Trello fetch date from web-based, Kanban-style, list-making application.
"""

@staticmethod
def _get_authenticator(config: dict) -> TrelloAuthenticator:
if "credentials" not in config:
# Backward compatability code
key, token = config["key"], config["token"]
else:
key, token = config["credentials"]["key"], config["credentials"]["token"]

return TrelloAuthenticator(token=token, key=key)

def check_connection(self, logger, config) -> Tuple[bool, any]:
"""
Testing connection availability for the connector by granting the credentials.
Expand All @@ -193,7 +203,7 @@ def check_connection(self, logger, config) -> Tuple[bool, any]:
try:
url = f"{TrelloStream.url_base}members/me"

authenticator = TrelloAuthenticator(token=config["token"], key=config["key"])
authenticator = self._get_authenticator(config)

session = requests.get(url, headers=authenticator.get_auth_header())
session.raise_for_status()
Expand All @@ -203,6 +213,6 @@ def check_connection(self, logger, config) -> Tuple[bool, any]:
return False, e

def streams(self, config: Mapping[str, Any]) -> List[Stream]:
config["authenticator"] = TrelloAuthenticator(token=config["token"], key=config["key"])
config["authenticator"] = self._get_authenticator(config)

return [Actions(config), Boards(config), Cards(config), Checklists(config), Lists(config), Users(config)]
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,81 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Trello Spec",
"type": "object",
"required": ["token", "key", "start_date"],
"additionalProperties": false,
"required": ["start_date"],
"additionalProperties": true,
"properties": {
"token": {
"type": "string",
"title": "API token",
"description": "A Trello token. See the <a href=\"https://developer.atlassian.com/cloud/trello/guides/rest-api/authorization/#using-basic-oauth\">docs</a> for instructions on how to generate it.",
"airbyte_secret": true
},
"key": {
"type": "string",
"title": "API key",
"description": "A Trello token. See the <a href=\"https://developer.atlassian.com/cloud/trello/guides/rest-api/authorization/#using-basic-oauth\">docs</a> for instructions on how to generate it.",
"airbyte_secret": true
"credentials": {
"title": "Authentication mechanism",
"description": "Choose how to authenticate to Github",
"type": "object",
"oneOf": [
{
"type": "object",
"title": "Authenticate via Trello (Oauth)",
"required": ["token", "key"],
"properties": {
"option_title": {
"type": "string",
"title": "Credentials title",
"description": "OAuth Credentials",
"const": "OAuth Credentials (OAuth flow)"
},
"token": {
"type": "string",
"title": "API token",
"description": "token obtained from oAuth flow",
"airbyte_secret": true
},
"key": {
"type": "string",
"title": "API key",
"description": "oAuth application key",
"airbyte_secret": true
}
}
},
{
"type": "object",
avida marked this conversation as resolved.
Show resolved Hide resolved
"title": "Enter credentials manually",
"required": ["token", "key"],
"properties": {
"option_title": {
"type": "string",
"title": "Credentials title",
"description": "OAuth Credentials",
"const": "OAuth Credentials (manual)"
},
"token": {
"type": "string",
"title": "API token",
"description": "A Trello token. See the <a href=\"https://developer.atlassian.com/cloud/trello/guides/rest-api/authorization/#using-basic-oauth\">docs</a> for instructions on how to generate it.",
"airbyte_secret": true
},
"key": {
"type": "string",
"title": "API key",
"description": "A Trello token. See the <a href=\"https://developer.atlassian.com/cloud/trello/guides/rest-api/authorization/#using-basic-oauth\">docs</a> for instructions on how to generate it.",
"airbyte_secret": true
}
}
}
]
},
"start_date": {
"type": "string",
"title": "Start date",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z$",
"description": "UTC date and time in the format 2017-01-25T00:00:00Z. Any data before this date will not be replicated.",
"examples": ["2021-03-01T00:00:00.000Z"]
}
}
},
"authSpecification": {
"auth_type": "oauth2.0",
"oauth2Specification": {
"rootObject": ["credentials", 0],
"oauthFlowInitParameters": [],
"oauthFlowOutputParameters": [["token"], ["key"]]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ public TrelloOAuthFlow(ConfigRepository configRepository, HttpTransport transpor

public String getSourceConsentUrl(UUID workspaceId, UUID sourceDefinitionId, String redirectUrl) throws IOException, ConfigNotFoundException {
final JsonNode oAuthParamConfig = getSourceOAuthParamConfig(workspaceId, sourceDefinitionId);
return getConsetntUrl(oAuthParamConfig, redirectUrl);
return getConsentUrl(oAuthParamConfig, redirectUrl);
}

public String getDestinationConsentUrl(UUID workspaceId, UUID destinationDefinitionId, String redirectUrl)
throws IOException, ConfigNotFoundException {
final JsonNode oAuthParamConfig = getDestinationOAuthParamConfig(workspaceId, destinationDefinitionId);
return getConsetntUrl(oAuthParamConfig, redirectUrl);
return getConsentUrl(oAuthParamConfig, redirectUrl);
}

private String getConsetntUrl(JsonNode oAuthParamConfig, String redirectUrl) throws IOException, ConfigNotFoundException {
private String getConsentUrl(JsonNode oAuthParamConfig, String redirectUrl) throws IOException, ConfigNotFoundException {
final String clientKey = getClientIdUnsafe(oAuthParamConfig);
final String clientSecret = getClientSecretUnsafe(oAuthParamConfig);
final OAuthGetTemporaryToken oAuthGetTemporaryToken = new OAuthGetTemporaryToken(REQUEST_TOKEN_URL);
Expand Down Expand Up @@ -111,7 +111,7 @@ private Map<String, Object> completeOAuth(JsonNode oAuthParamConfig, Map<String,
oAuthGetAccessToken.consumerKey = clientKey;
OAuthCredentialsResponse accessTokenResponse = oAuthGetAccessToken.execute();
String accessToken = accessTokenResponse.token;
return Map.of("token", accessToken, "key", clientKey);
return Map.of("credentials", Map.of("token", accessToken, "key", clientKey));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun
final Map<String, Object> params = trelloOAuthFlow.completeSourceOAuth(workspaceId, definitionId,
Map.of("oauth_verifier", serverHandler.getParamValue(), "oauth_token", serverHandler.getResponseQuery().get("oauth_token")), REDIRECT_URL);
LOGGER.info("Response from completing OAuth Flow is: {}", params.toString());
assertTrue(params.containsKey("token"));
assertTrue(params.containsKey("key"));
assertTrue(params.get("token").toString().length() > 0);
final Map<String, String> creds = (Map<String, String>)params.get("credentials");
assertTrue(creds.containsKey("token"));
assertTrue(creds.containsKey("key"));
assertTrue(creds.get("token").toString().length() > 0);
}

static class ServerHandler implements HttpHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void testGetSourceConcentUrl() throws IOException, InterruptedException,

@Test
public void testCompleteSourceAuth() throws IOException, InterruptedException, ConfigNotFoundException {
Map<String, String> expectedParams = Map.of("key", "test_client_id", "token", "test_token");
final Map<String, Object> expectedParams = Map.of("credentials", Map.of("key", "test_client_id", "token", "test_token"));
final Map<String, Object> queryParams = Map.of("oauth_token", "token", "oauth_verifier", "verifier");
final Map<String, Object> returnedParams =
trelloOAuthFlow.completeSourceOAuth(workspaceId, definitionId, queryParams, REDIRECT_URL);
Expand Down
1 change: 1 addition & 0 deletions docs/integrations/sources/trello.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ Please read [How to get your APIs Token and Key](https://developer.atlassian.com

| Version | Date | Pull Request | Subject |
| :--- | :--- | :--- | :--- |
| 0.1.1 | 2021-10-12 | [6968](https://github.com/airbytehq/airbyte/pull/6968) | Add oAuth flow support |
| 0.1.0 | 2021-08-18 | [5501](https://github.com/airbytehq/airbyte/pull/5501) | Release Trello CDK Connector |