Skip to content

Commit

Permalink
🐛 Bugfix: inject http client into server to prevent file churn (#7688)
Browse files Browse the repository at this point in the history
  • Loading branch information
sherifnada authored Nov 5, 2021
1 parent 45f6559 commit 9bda6a7
Show file tree
Hide file tree
Showing 35 changed files with 130 additions and 86 deletions.
21 changes: 8 additions & 13 deletions airbyte-oauth/src/main/java/io/airbyte/oauth/BaseOAuthFlow.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -55,19 +54,12 @@ public enum TOKEN_REQUEST_CONTENT_TYPE {

}

protected final HttpClient httpClient;
private final TOKEN_REQUEST_CONTENT_TYPE tokenReqContentType;
protected HttpClient httpClient;
private final Supplier<String> stateSupplier;

public BaseOAuthFlow(final ConfigRepository configRepository) {
this(configRepository, HttpClient.newBuilder().version(Version.HTTP_1_1).build(), BaseOAuthFlow::generateRandomState);
}

public BaseOAuthFlow(ConfigRepository configRepository, TOKEN_REQUEST_CONTENT_TYPE tokenReqContentType) {
this(configRepository,
HttpClient.newBuilder().version(Version.HTTP_1_1).build(),
BaseOAuthFlow::generateRandomState,
tokenReqContentType);
public BaseOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
this(configRepository, httpClient, BaseOAuthFlow::generateRandomState);
}

public BaseOAuthFlow(ConfigRepository configRepository, HttpClient httpClient, Supplier<String> stateSupplier) {
Expand Down Expand Up @@ -161,7 +153,8 @@ protected Map<String, Object> completeOAuthFlow(final String clientId,
.header("Accept", "application/json")
.build();
try {
final HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response;
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return extractRefreshToken(Jsons.deserialize(response.body()), accessTokenUrl);
} catch (final InterruptedException e) {
throw new IOException("Failed to complete OAuth flow", e);
Expand Down Expand Up @@ -235,7 +228,9 @@ private static String toUrlEncodedString(final Map<String, String> body) {

protected static String toJson(final Map<String, String> body) {
final Gson gson = new Gson();
Type gsonType = new TypeToken<Map<String, String>>() {}.getType();
Type gsonType = new TypeToken<Map<String, String>>() {

}.getType();
return gson.toJson(body, gsonType);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,29 @@
import io.airbyte.oauth.flows.google.GoogleAnalyticsOAuthFlow;
import io.airbyte.oauth.flows.google.GoogleSearchConsoleOAuthFlow;
import io.airbyte.oauth.flows.google.GoogleSheetsOAuthFlow;
import java.net.http.HttpClient;
import java.util.Map;
import java.util.UUID;

public class OAuthImplementationFactory {

private final Map<String, OAuthFlowImplementation> OAUTH_FLOW_MAPPING;

public OAuthImplementationFactory(final ConfigRepository configRepository) {
public OAuthImplementationFactory(final ConfigRepository configRepository, final HttpClient httpClient) {
OAUTH_FLOW_MAPPING = ImmutableMap.<String, OAuthFlowImplementation>builder()
.put("airbyte/source-asana", new AsanaOAuthFlow(configRepository))
.put("airbyte/source-facebook-marketing", new FacebookMarketingOAuthFlow(configRepository))
.put("airbyte/source-facebook-pages", new FacebookPagesOAuthFlow(configRepository))
.put("airbyte/source-github", new GithubOAuthFlow(configRepository))
.put("airbyte/source-google-ads", new GoogleAdsOAuthFlow(configRepository))
.put("airbyte/source-google-analytics-v4", new GoogleAnalyticsOAuthFlow(configRepository))
.put("airbyte/source-google-search-console", new GoogleSearchConsoleOAuthFlow(configRepository))
.put("airbyte/source-google-sheets", new GoogleSheetsOAuthFlow(configRepository))
.put("airbyte/source-instagram", new InstagramOAuthFlow(configRepository))
.put("airbyte/source-salesforce", new SalesforceOAuthFlow(configRepository))
.put("airbyte/source-surveymonkey", new SurveymonkeyOAuthFlow(configRepository))
.put("airbyte/source-trello", new TrelloOAuthFlow(configRepository))
.put("airbyte/source-hubspot", new HubspotOAuthFlow(configRepository))
.put("airbyte/source-asana", new AsanaOAuthFlow(configRepository, httpClient))
.put("airbyte/source-facebook-marketing", new FacebookMarketingOAuthFlow(configRepository, httpClient))
.put("airbyte/source-facebook-pages", new FacebookPagesOAuthFlow(configRepository, httpClient))
.put("airbyte/source-github", new GithubOAuthFlow(configRepository, httpClient))
.put("airbyte/source-google-ads", new GoogleAdsOAuthFlow(configRepository, httpClient))
.put("airbyte/source-google-analytics-v4", new GoogleAnalyticsOAuthFlow(configRepository, httpClient))
.put("airbyte/source-google-search-console", new GoogleSearchConsoleOAuthFlow(configRepository, httpClient))
.put("airbyte/source-google-sheets", new GoogleSheetsOAuthFlow(configRepository, httpClient))
.put("airbyte/source-instagram", new InstagramOAuthFlow(configRepository, httpClient))
.put("airbyte/source-salesforce", new SalesforceOAuthFlow(configRepository, httpClient))
.put("airbyte/source-surveymonkey", new SurveymonkeyOAuthFlow(configRepository, httpClient))
.put("airbyte/source-trello", new TrelloOAuthFlow(configRepository, httpClient))
.put("airbyte/source-hubspot", new HubspotOAuthFlow(configRepository, httpClient))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public class AsanaOAuthFlow extends BaseOAuthFlow {
private static final String AUTHORIZE_URL = "https://app.asana.com/-/oauth_authorize";
private static final String ACCESS_TOKEN_URL = "https://app.asana.com/-/oauth_token";

public AsanaOAuthFlow(ConfigRepository configRepository) {
super(configRepository);
public AsanaOAuthFlow(ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class GithubOAuthFlow extends BaseOAuthFlow {
private static final String AUTHORIZE_URL = "https://github.com/login/oauth/authorize";
private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";

public GithubOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public GithubOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public class HubspotOAuthFlow extends BaseOAuthFlow {

private final String AUTHORIZE_URL = "https://app.hubspot.com/oauth/authorize";

public HubspotOAuthFlow(ConfigRepository configRepository) {
super(configRepository);
public HubspotOAuthFlow(ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

public HubspotOAuthFlow(ConfigRepository configRepository, HttpClient httpClient, Supplier<String> stateSupplier) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public class SalesforceOAuthFlow extends BaseOAuthFlow {
private static final String AUTHORIZE_URL = "https://login.salesforce.com/services/oauth2/authorize";
private static final String ACCESS_TOKEN_URL = "https://login.salesforce.com/services/oauth2/token";

public SalesforceOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public SalesforceOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class SurveymonkeyOAuthFlow extends BaseOAuthFlow {
private static final String AUTHORIZE_URL = "https://api.surveymonkey.com/oauth/authorize";
private static final String ACCESS_TOKEN_URL = "https://api.surveymonkey.com/oauth/token";

public SurveymonkeyOAuthFlow(ConfigRepository configRepository) {
super(configRepository);
public SurveymonkeyOAuthFlow(ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.airbyte.config.persistence.ConfigRepository;
import io.airbyte.oauth.BaseOAuthConfig;
import java.io.IOException;
import java.net.http.HttpClient;
import java.util.Map;
import java.util.UUID;

Expand All @@ -38,7 +39,7 @@ public class TrelloOAuthFlow extends BaseOAuthConfig {
private static final OAuthHmacSigner signer = new OAuthHmacSigner();
private final HttpTransport transport;

public TrelloOAuthFlow(final ConfigRepository configRepository) {
public TrelloOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository);
transport = new NetHttpTransport();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public class FacebookMarketingOAuthFlow extends FacebookOAuthFlow {

private static final String SCOPES = "ads_management,ads_read,read_insights";

public FacebookMarketingOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public FacebookMarketingOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public abstract class FacebookOAuthFlow extends BaseOAuthFlow {
private static final String ACCESS_TOKEN_URL = "https://graph.facebook.com/v12.0/oauth/access_token";
private static final String AUTH_CODE_TOKEN_URL = "https://www.facebook.com/v12.0/dialog/oauth";

public FacebookOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public FacebookOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
package io.airbyte.oauth.flows.facebook;

import io.airbyte.config.persistence.ConfigRepository;
import java.net.http.HttpClient;

public class FacebookPagesOAuthFlow extends FacebookOAuthFlow {

private static final String SCOPES = "pages_manage_ads,pages_manage_metadata,pages_read_engagement,pages_read_user_content";

public FacebookPagesOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public FacebookPagesOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
package io.airbyte.oauth.flows.facebook;

import io.airbyte.config.persistence.ConfigRepository;
import java.net.http.HttpClient;

// Instagram Graph API require Facebook API User token
public class InstagramOAuthFlow extends FacebookMarketingOAuthFlow {

private static final String SCOPES = "ads_management,instagram_basic,instagram_manage_insights,read_insights";

public InstagramOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public InstagramOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class GoogleAdsOAuthFlow extends GoogleOAuthFlow {
@VisibleForTesting
static final String SCOPE_URL = "https://www.googleapis.com/auth/adwords";

public GoogleAdsOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public GoogleAdsOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class GoogleAnalyticsOAuthFlow extends GoogleOAuthFlow {

public static final String SCOPE_URL = "https://www.googleapis.com/auth/analytics.readonly";

public GoogleAnalyticsOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public GoogleAnalyticsOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public abstract class GoogleOAuthFlow extends BaseOAuthFlow {

private static final String ACCESS_TOKEN_URL = "https://oauth2.googleapis.com/token";

public GoogleOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public GoogleOAuthFlow(final ConfigRepository configRepository, final HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public class GoogleSearchConsoleOAuthFlow extends GoogleOAuthFlow {
@VisibleForTesting
static final String SCOPE_URL = "https://www.googleapis.com/auth/webmasters.readonly";

public GoogleSearchConsoleOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public GoogleSearchConsoleOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class GoogleSheetsOAuthFlow extends GoogleOAuthFlow {
@VisibleForTesting
static final String SCOPE_URL = "https://www.googleapis.com/auth/spreadsheets.readonly https://www.googleapis.com/auth/drive.readonly";

public GoogleSheetsOAuthFlow(final ConfigRepository configRepository) {
super(configRepository);
public GoogleSheetsOAuthFlow(final ConfigRepository configRepository, HttpClient httpClient) {
super(configRepository, httpClient);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.airbyte.oauth.flows.OAuthFlowIntegrationTest;
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;
Expand All @@ -36,8 +37,8 @@ protected Path getCredentialsPath() {
}

@Override
protected OAuthFlowImplementation getFlowObject(ConfigRepository configRepository) {
return new FacebookMarketingOAuthFlow(configRepository);
protected OAuthFlowImplementation getFlowImplementation(ConfigRepository configRepository, HttpClient httpClient) {
return new FacebookMarketingOAuthFlow(configRepository, httpClient);
}

@BeforeEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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;
Expand All @@ -36,8 +37,8 @@ protected Path getCredentialsPath() {
}

@Override
protected OAuthFlowImplementation getFlowObject(ConfigRepository configRepository) {
return new GithubOAuthFlow(configRepository);
protected OAuthFlowImplementation getFlowImplementation(ConfigRepository configRepository, HttpClient httpClient) {
return new GithubOAuthFlow(configRepository, httpClient);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.http.HttpClient;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
Expand All @@ -43,6 +44,7 @@ public class SalesforceOAuthFlowIntegrationTest {
private SalesforceOAuthFlow salesforceOAuthFlow;
private HttpServer server;
private ServerHandler serverHandler;
private HttpClient httpClient;

@BeforeEach
public void setup() throws IOException {
Expand All @@ -51,7 +53,8 @@ public void setup() throws IOException {
"Must provide path to a oauth credentials file.");
}
configRepository = mock(ConfigRepository.class);
salesforceOAuthFlow = new SalesforceOAuthFlow(configRepository);
httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
salesforceOAuthFlow = new SalesforceOAuthFlow(configRepository, httpClient);

server = HttpServer.create(new InetSocketAddress(8000), 0);
server.setExecutor(null); // creates a default executor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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;
Expand All @@ -35,8 +36,8 @@ protected Path getCredentialsPath() {
}

@Override
protected OAuthFlowImplementation getFlowObject(ConfigRepository configRepository) {
return new SurveymonkeyOAuthFlow(configRepository);
protected OAuthFlowImplementation getFlowImplementation(ConfigRepository configRepository, HttpClient httpClient) {
return new SurveymonkeyOAuthFlow(configRepository, httpClient);
}

@BeforeEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.http.HttpClient;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
Expand All @@ -43,6 +44,7 @@ public class TrelloOAuthFlowIntegrationTest {
private TrelloOAuthFlow trelloOAuthFlow;
private HttpServer server;
private ServerHandler serverHandler;
private HttpClient httpClient;

@BeforeEach
public void setup() throws IOException {
Expand All @@ -51,7 +53,8 @@ public void setup() throws IOException {
"Must provide path to a oauth credentials file.");
}
configRepository = mock(ConfigRepository.class);
trelloOAuthFlow = new TrelloOAuthFlow(configRepository);
httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
trelloOAuthFlow = new TrelloOAuthFlow(configRepository, httpClient);

server = HttpServer.create(new InetSocketAddress(8000), 0);
server.setExecutor(null); // creates a default executor
Expand Down
Loading

0 comments on commit 9bda6a7

Please sign in to comment.