-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🎉 intercom added oauth support (#7060)
* Added oauth support * fixed description of access_token field in Oauth authentication * test selenium * add java implementation * remove commented dependencies| remove star import * remove System.out.println * added support for old format of spec.json files. * added support for old format of spec.json files. * added support for old format of spec.json files * added credential with new spec format * bumped connector version * Reverted format of spec.json file * Override extractRefreshToken method * java oauth test fixes, minor updates, cloud spec version update * Fixed docs strings * Added HttpClient argument * revert changes in seed folder to follow new dev flow * Updated image version in seed * Fixed import of IntercomOAuthFlow * updated IntercomOAuthFlow to match base class changes * Change image version to 0.1.8 Co-authored-by: vmaltsev <vitalii.maltsev@globallogic.com>
- Loading branch information
Showing
15 changed files
with
270 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
airbyte-oauth/src/main/java/io/airbyte/oauth/flows/IntercomOAuthFlow.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* Copyright (c) 2021 Airbyte, Inc., all rights reserved. | ||
*/ | ||
|
||
package io.airbyte.oauth.flows; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.base.Preconditions; | ||
import io.airbyte.config.persistence.ConfigRepository; | ||
import io.airbyte.oauth.BaseOAuth2Flow; | ||
import java.io.IOException; | ||
import java.net.URISyntaxException; | ||
import java.net.http.HttpClient; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import java.util.function.Supplier; | ||
import org.apache.http.client.utils.URIBuilder; | ||
|
||
public class IntercomOAuthFlow extends BaseOAuth2Flow { | ||
|
||
private static final String AUTHORIZE_URL = "https://app.intercom.com/a/oauth/connect"; | ||
private static final String ACCESS_TOKEN_URL = "https://api.intercom.io/auth/eagle/token"; | ||
|
||
public IntercomOAuthFlow(ConfigRepository configRepository, HttpClient httpClient) { | ||
super(configRepository, httpClient); | ||
} | ||
|
||
@VisibleForTesting | ||
public IntercomOAuthFlow(ConfigRepository configRepository, final HttpClient httpClient, Supplier<String> stateSupplier) { | ||
super(configRepository, httpClient, stateSupplier); | ||
} | ||
|
||
@Override | ||
protected String formatConsentUrl(UUID definitionId, String clientId, String redirectUrl) throws IOException { | ||
try { | ||
return new URIBuilder(AUTHORIZE_URL) | ||
.addParameter("client_id", clientId) | ||
.addParameter("redirect_uri", redirectUrl) | ||
.addParameter("response_type", "code") | ||
.addParameter("state", getState()) | ||
.build().toString(); | ||
} catch (URISyntaxException e) { | ||
throw new IOException("Failed to format Consent URL for OAuth flow", e); | ||
} | ||
} | ||
|
||
@Override | ||
protected String getAccessTokenUrl() { | ||
return ACCESS_TOKEN_URL; | ||
} | ||
|
||
@Override | ||
protected Map<String, Object> extractOAuthOutput(final JsonNode data, final String accessTokenUrl) { | ||
// Intercom does not have refresh token but calls it "long lived access token" instead: | ||
// see https://developers.intercom.com/building-apps/docs/setting-up-oauth | ||
Preconditions.checkArgument(data.has("access_token"), "Missing 'access_token' in query params from %s", ACCESS_TOKEN_URL); | ||
return Map.of("access_token", data.get("access_token").asText()); | ||
} | ||
|
||
@Override | ||
protected List<String> getDefaultOAuthOutputPath() { | ||
return List.of(); | ||
} | ||
|
||
} |
89 changes: 89 additions & 0 deletions
89
...th/src/test-integration/java/io.airbyte.oauth.flows/IntercomOAuthFlowIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright (c) 2021 Airbyte, Inc., all rights reserved. | ||
*/ | ||
|
||
package io.airbyte.oauth.flows; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.google.common.collect.ImmutableMap; | ||
import io.airbyte.commons.json.Jsons; | ||
import io.airbyte.config.SourceOAuthParameter; | ||
import io.airbyte.config.persistence.ConfigNotFoundException; | ||
import io.airbyte.config.persistence.ConfigRepository; | ||
import io.airbyte.oauth.OAuthFlowImplementation; | ||
import io.airbyte.validation.json.JsonValidationException; | ||
import java.io.IOException; | ||
import java.net.http.HttpClient; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class IntercomOAuthFlowIntegrationTest extends OAuthFlowIntegrationTest { | ||
|
||
protected static final Path CREDENTIALS_PATH = Path.of("secrets/intercom.json"); | ||
protected static final String REDIRECT_URL = "http://localhost:8000/code"; | ||
protected static final int SERVER_LISTENING_PORT = 8000; | ||
|
||
@Override | ||
protected Path getCredentialsPath() { | ||
return CREDENTIALS_PATH; | ||
} | ||
|
||
@Override | ||
protected OAuthFlowImplementation getFlowImplementation(ConfigRepository configRepository, HttpClient httpClient) { | ||
return new IntercomOAuthFlow(configRepository, httpClient); | ||
} | ||
|
||
@Override | ||
protected int getServerListeningPort() { | ||
return SERVER_LISTENING_PORT; | ||
} | ||
|
||
@BeforeEach | ||
public void setup() throws IOException { | ||
super.setup(); | ||
} | ||
|
||
@Test | ||
public void testFullIntercomOAuthFlow() throws InterruptedException, ConfigNotFoundException, IOException, JsonValidationException { | ||
int limit = 20; | ||
final UUID workspaceId = UUID.randomUUID(); | ||
final UUID definitionId = UUID.randomUUID(); | ||
final String fullConfigAsString = new String(Files.readAllBytes(CREDENTIALS_PATH)); | ||
final JsonNode credentialsJson = Jsons.deserialize(fullConfigAsString); | ||
when(configRepository.listSourceOAuthParam()).thenReturn(List.of(new SourceOAuthParameter() | ||
.withOauthParameterId(UUID.randomUUID()) | ||
.withSourceDefinitionId(definitionId) | ||
.withWorkspaceId(workspaceId) | ||
.withConfiguration(Jsons.jsonNode( | ||
Map.of("authorization", | ||
ImmutableMap.builder() | ||
.put("client_id", credentialsJson.get("client_id").asText()) | ||
.put("client_secret", credentialsJson.get("client_secret").asText()) | ||
.build()))))); | ||
|
||
final String url = flow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); | ||
LOGGER.info("Waiting for user consent at: {}", url); | ||
|
||
// TODO: To automate, start a selenium job to navigate to the Consent URL and click on allowing | ||
// access... | ||
while (!serverHandler.isSucceeded() && limit > 0) { | ||
Thread.sleep(1000); | ||
limit -= 1; | ||
} | ||
assertTrue(serverHandler.isSucceeded(), "Failed to get User consent on time"); | ||
final Map<String, Object> params = flow.completeSourceOAuth(workspaceId, definitionId, | ||
Map.of("code", serverHandler.getParamValue()), REDIRECT_URL); | ||
LOGGER.info("Response from completing OAuth Flow is: {}", params.toString()); | ||
assertTrue(params.containsKey("access_token")); | ||
assertTrue(params.get("access_token").toString().length() > 0); | ||
} | ||
|
||
} |
80 changes: 80 additions & 0 deletions
80
airbyte-oauth/src/test/java/io/airbyte/oauth/flows/IntercomOAuthFlowTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright (c) 2021 Airbyte, Inc., all rights reserved. | ||
*/ | ||
|
||
package io.airbyte.oauth.flows; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import io.airbyte.commons.json.Jsons; | ||
import io.airbyte.config.SourceOAuthParameter; | ||
import io.airbyte.config.persistence.ConfigNotFoundException; | ||
import io.airbyte.config.persistence.ConfigRepository; | ||
import io.airbyte.validation.json.JsonValidationException; | ||
import java.io.IOException; | ||
import java.net.http.HttpClient; | ||
import java.net.http.HttpResponse; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.UUID; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class IntercomOAuthFlowTest { | ||
|
||
private UUID workspaceId; | ||
private UUID definitionId; | ||
private IntercomOAuthFlow intercomoAuthFlow; | ||
private HttpClient httpClient; | ||
|
||
private static final String REDIRECT_URL = "https://airbyte.io"; | ||
|
||
private static String getConstantState() { | ||
return "state"; | ||
} | ||
|
||
@BeforeEach | ||
public void setup() throws IOException, JsonValidationException { | ||
workspaceId = UUID.randomUUID(); | ||
definitionId = UUID.randomUUID(); | ||
ConfigRepository configRepository = mock(ConfigRepository.class); | ||
httpClient = mock(HttpClient.class); | ||
when(configRepository.listSourceOAuthParam()).thenReturn(List.of(new SourceOAuthParameter() | ||
.withOauthParameterId(UUID.randomUUID()) | ||
.withSourceDefinitionId(definitionId) | ||
.withWorkspaceId(workspaceId) | ||
.withConfiguration(Jsons.jsonNode( | ||
ImmutableMap.builder() | ||
.put("client_id", "test_client_id") | ||
.put("client_secret", "test_client_secret") | ||
.build())))); | ||
intercomoAuthFlow = new IntercomOAuthFlow(configRepository, httpClient, IntercomOAuthFlowTest::getConstantState); | ||
|
||
} | ||
|
||
@Test | ||
public void testGetSourceConcentUrl() throws IOException, ConfigNotFoundException { | ||
final String concentUrl = | ||
intercomoAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL); | ||
assertEquals(concentUrl, | ||
"https://app.intercom.com/a/oauth/connect?client_id=test_client_id&redirect_uri=https%3A%2F%2Fairbyte.io&response_type=code&state=state"); | ||
} | ||
|
||
@Test | ||
public void testCompleteSourceOAuth() throws IOException, InterruptedException, ConfigNotFoundException { | ||
|
||
Map<String, String> returnedCredentials = Map.of("access_token", "refresh_token_response"); | ||
final HttpResponse response = mock(HttpResponse.class); | ||
when(response.body()).thenReturn(Jsons.serialize(returnedCredentials)); | ||
when(httpClient.send(any(), any())).thenReturn(response); | ||
final Map<String, Object> queryParams = Map.of("code", "test_code"); | ||
final Map<String, Object> actualQueryParams = | ||
intercomoAuthFlow.completeSourceOAuth(workspaceId, definitionId, queryParams, REDIRECT_URL); | ||
assertEquals(Jsons.serialize(returnedCredentials), Jsons.serialize(actualQueryParams)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.