Skip to content

Commit

Permalink
Trello source: Add oAuth flow.
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmytro Rezchykov committed Oct 12, 2021
1 parent d3fd3b4 commit 2c67c31
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 26 deletions.
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",
"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 |

0 comments on commit 2c67c31

Please sign in to comment.