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

Update Google Analytics Oauth flow #6321

Merged
merged 3 commits into from
Sep 21, 2021
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -24,9 +24,13 @@

package io.airbyte.oauth.flows.google;

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 java.io.IOException;
import java.net.http.HttpClient;
import java.util.Map;
import java.util.function.Supplier;

public class GoogleAnalyticsOAuthFlow extends GoogleOAuthFlow {
Expand All @@ -47,4 +51,24 @@ protected String getScope() {
return SCOPE_URL;
}

@Override
protected String getClientIdUnsafe(JsonNode config) {
// the config object containing client ID and secret is nested inside the "credentials" object
Preconditions.checkArgument(config.hasNonNull("credentials"));
return super.getClientIdUnsafe(config.get("credentials"));
}

@Override
protected String getClientSecretUnsafe(JsonNode config) {
// the config object containing client ID and secret is nested inside the "credentials" object
Preconditions.checkArgument(config.hasNonNull("credentials"));
return super.getClientSecretUnsafe(config.get("credentials"));
}

@Override
protected Map<String, Object> extractRefreshToken(JsonNode data) throws IOException {
// the config object containing refresh token is nested inside the "credentials" object
return Map.of("credentials", super.extractRefreshToken(data));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun
.withOauthParameterId(UUID.randomUUID())
.withSourceDefinitionId(definitionId)
.withWorkspaceId(workspaceId)
.withConfiguration(Jsons.jsonNode(ImmutableMap.builder()
.put("client_id", credentialsJson.get("client_id").asText())
.put("client_secret", credentialsJson.get("client_secret").asText())
.build()))));
.withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder()
.put("client_id", credentialsJson.get("credentials").get("client_id").asText())
.put("client_secret", credentialsJson.get("credentials").get("client_secret").asText())
.build())))));
final String url = googleAnalyticsOAuthFlow.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
Expand All @@ -112,10 +112,12 @@ public void testFullGoogleOAuthFlow() throws InterruptedException, ConfigNotFoun
final Map<String, Object> params = googleAnalyticsOAuthFlow.completeSourceOAuth(workspaceId, definitionId,
Map.of("code", serverHandler.getParamValue()), REDIRECT_URL);
LOGGER.info("Response from completing OAuth Flow is: {}", params.toString());
assertTrue(params.containsKey("refresh_token"));
assertTrue(params.get("refresh_token").toString().length() > 0);
assertTrue(params.containsKey("access_token"));
assertTrue(params.get("access_token").toString().length() > 0);
assertTrue(params.containsKey("credentials"));
final Map<String, Object> credentials = (Map<String, Object>) params.get("credentials");
assertTrue(credentials.containsKey("refresh_token"));
assertTrue(credentials.get("refresh_token").toString().length() > 0);
assertTrue(credentials.containsKey("access_token"));
assertTrue(credentials.get("access_token").toString().length() > 0);
}

static class ServerHandler implements HttpHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ public void testGetSourceConsentUrl() throws IOException, ConfigNotFoundExceptio
.withOauthParameterId(UUID.randomUUID())
.withSourceDefinitionId(definitionId)
.withWorkspaceId(workspaceId)
.withConfiguration(Jsons.jsonNode(ImmutableMap.builder()
.withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder()
.put("client_id", getClientId())
.build()))));
.build())))));
final String actualSourceUrl = googleAnalyticsOAuthFlow.getSourceConsentUrl(workspaceId, definitionId, REDIRECT_URL);
final String expectedSourceUrl = String.format(
"https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&redirect_uri=%s&response_type=code&scope=%s&access_type=offline&state=%s&include_granted_scopes=true&prompt=consent",
Expand All @@ -128,9 +128,9 @@ public void testGetDestinationConsentUrl() throws IOException, ConfigNotFoundExc
.withOauthParameterId(UUID.randomUUID())
.withDestinationDefinitionId(definitionId)
.withWorkspaceId(workspaceId)
.withConfiguration(Jsons.jsonNode(ImmutableMap.builder()
.withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder()
.put("client_id", getClientId())
.build()))));
.build())))));
// It would be better to make this comparison agnostic of the order of query params but the URI
// class' equals() method
// considers URLs with different qparam orders different URIs..
Expand All @@ -151,10 +151,10 @@ public void testCompleteOAuthMissingCode() throws IOException, ConfigNotFoundExc
.withOauthParameterId(UUID.randomUUID())
.withSourceDefinitionId(definitionId)
.withWorkspaceId(workspaceId)
.withConfiguration(Jsons.jsonNode(ImmutableMap.builder()
.withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder()
.put("client_id", getClientId())
.put("client_secret", "test_client_secret")
.build()))));
.build())))));
final Map<String, Object> queryParams = Map.of();
assertThrows(IOException.class, () -> googleAnalyticsOAuthFlow.completeSourceOAuth(workspaceId, definitionId, queryParams, REDIRECT_URL));
}
Expand All @@ -165,17 +165,17 @@ public void testCompleteSourceOAuth() throws IOException, ConfigNotFoundExceptio
.withOauthParameterId(UUID.randomUUID())
.withSourceDefinitionId(definitionId)
.withWorkspaceId(workspaceId)
.withConfiguration(Jsons.jsonNode(ImmutableMap.builder()
.withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder()
.put("client_id", getClientId())
.put("client_secret", "test_client_secret")
.build()))));
final String expectedQueryParams = Jsons.serialize(Map.of("refresh_token", "refresh_token_response"));
.build())))));
Map<String, String> returnedCredentials = Map.of("refresh_token", "refresh_token_response");
final HttpResponse response = mock(HttpResponse.class);
when(response.body()).thenReturn(expectedQueryParams);
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 = googleAnalyticsOAuthFlow.completeSourceOAuth(workspaceId, definitionId, queryParams, REDIRECT_URL);
assertEquals(expectedQueryParams, Jsons.serialize(actualQueryParams));
assertEquals(Jsons.serialize(Map.of("credentials", returnedCredentials)), Jsons.serialize(actualQueryParams));
}

@Test
Expand All @@ -184,18 +184,18 @@ public void testCompleteDestinationOAuth() throws IOException, ConfigNotFoundExc
.withOauthParameterId(UUID.randomUUID())
.withDestinationDefinitionId(definitionId)
.withWorkspaceId(workspaceId)
.withConfiguration(Jsons.jsonNode(ImmutableMap.builder()
.withConfiguration(Jsons.jsonNode(Map.of("credentials", ImmutableMap.builder()
.put("client_id", getClientId())
.put("client_secret", "test_client_secret")
.build()))));
final String expectedQueryParams = Jsons.serialize(Map.of("refresh_token", "refresh_token_response"));
.build())))));
Map<String, String> returnedCredentials = Map.of("refresh_token", "refresh_token_response");
final HttpResponse response = mock(HttpResponse.class);
when(response.body()).thenReturn(expectedQueryParams);
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 = googleAnalyticsOAuthFlow
.completeDestinationOAuth(workspaceId, definitionId, queryParams, REDIRECT_URL);
assertEquals(expectedQueryParams, Jsons.serialize(actualQueryParams));
assertEquals(Jsons.serialize(Map.of("credentials", returnedCredentials)), Jsons.serialize(actualQueryParams));
}

private String getClientId() throws IOException {
Expand All @@ -204,7 +204,7 @@ private String getClientId() throws IOException {
} else {
final String fullConfigAsString = new String(Files.readAllBytes(CREDENTIALS_PATH));
final JsonNode credentialsJson = Jsons.deserialize(fullConfigAsString);
return credentialsJson.get("client_id").asText();
return credentialsJson.get("credentials").get("client_id").asText();
}
}

Expand Down