Skip to content

Commit

Permalink
Merge pull request #43 from cloudsufi/PLUGIN-1762-and-1764
Browse files Browse the repository at this point in the history
[PLUGIN-1762][PLUGIN-1764]Access Token support Added and Macro added for refresh token fields.
  • Loading branch information
vikasrathee-cs authored Mar 21, 2024
2 parents c58f79f + 18fab3a commit 9660439
Show file tree
Hide file tree
Showing 15 changed files with 381 additions and 16 deletions.
5 changes: 5 additions & 0 deletions docs/GoogleDrive-batchsink.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ Make sure that:
OAuth2 client credentials can be generated on Google Cloud
[Credentials Page](https://console.cloud.google.com/apis/credentials)

**OAuth Method:** The method used to get OAuth access tokens. Users have the option to either directly provide
the OAuth access token or supply a client ID, client secret, and refresh token.

**Access Token:** Short lived access token used for connecting.

**Client ID:** OAuth2 client id used to identify the application.

**Client Secret:** OAuth2 client secret used to access the authorization server.
Expand Down
5 changes: 5 additions & 0 deletions docs/GoogleDrive-batchsource.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ Make sure that:
OAuth2 client credentials can be generated on Google Cloud
[Credentials Page](https://console.cloud.google.com/apis/credentials)

**OAuth Method:** The method used to get OAuth access tokens. Users have the option to either directly provide
the OAuth access token or supply a client ID, client secret, and refresh token.

**Access Token:** Short lived access token used for connecting.

**Client ID:** OAuth2 client id used to identify the application.

**Client Secret:** OAuth2 client secret used to access the authorization server.
Expand Down
5 changes: 5 additions & 0 deletions docs/GoogleSheets-batchsink.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ Make sure that:
OAuth2 client credentials can be generated on Google Cloud
[Credentials Page](https://console.cloud.google.com/apis/credentials)

**OAuth Method:** The method used to get OAuth access tokens. Users have the option to either directly provide
the OAuth access token or supply a client ID, client secret, and refresh token.

**Access Token:** Short lived access token used for connecting.

**Client ID:** OAuth2 client id used to identify the application.

**Client Secret:** OAuth2 client secret used to access the authorization server.
Expand Down
5 changes: 5 additions & 0 deletions docs/GoogleSheets-batchsource.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ Make sure that:
OAuth2 client credentials can be generated on Google Cloud
[Credentials Page](https://console.cloud.google.com/apis/credentials)

**OAuth Method:** The method used to get OAuth access tokens. Users have the option to either directly provide
the OAuth access token or supply a client ID, client secret, and refresh token.

**Access Token:** Short lived access token used for connecting.

**Client ID:** OAuth2 client id used to identify the application.

**Client Secret:** OAuth2 client secret used to access the authorization server.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,23 @@ public abstract class GoogleAuthBaseConfig extends PluginConfig {
public static final String AUTO_DETECT_VALUE = "auto-detect";
public static final String REFERENCE_NAME = "referenceName";
public static final String AUTH_TYPE = "authType";
public static final String AUTH_TYPE_LABEL = "Auth type";
public static final String AUTH_TYPE_LABEL = "Authentication Type";
public static final String CLIENT_ID = "clientId";
public static final String CLIENT_ID_LABEL = "Client ID";
public static final String CLIENT_SECRET = "clientSecret";
public static final String CLIENT_SECRET_LABEL = "Client secret";
public static final String CLIENT_SECRET_LABEL = "Client Secret";
public static final String REFRESH_TOKEN = "refreshToken";
public static final String REFRESH_TOKEN_LABEL = "Refresh token";
public static final String REFRESH_TOKEN_LABEL = "Refresh Token";
public static final String ACCOUNT_FILE_PATH = "accountFilePath";
public static final String DIRECTORY_IDENTIFIER = "directoryIdentifier";
public static final String NAME_SERVICE_ACCOUNT_TYPE = "serviceAccountType";
public static final String NAME_SERVICE_ACCOUNT_JSON = "serviceAccountJSON";
public static final String SERVICE_ACCOUNT_FILE_PATH = "filePath";
public static final String SERVICE_ACCOUNT_JSON = "JSON";
public static final String SCHEMA = "schema";
public static final String ACCESS_TOKEN = "accessToken";
public static final String ACCESS_TOKEN_LABEL = "Access Token";
public static final String OAUTH_METHOD = "oAuthMethod";

private static final String IS_SET_FAILURE_MESSAGE_PATTERN = "'%s' property is empty or macro is not available.";

Expand All @@ -76,21 +79,37 @@ public abstract class GoogleAuthBaseConfig extends PluginConfig {
@Macro
protected String serviceAccountType;

@Macro
@Nullable
@Name(OAUTH_METHOD)
@Description("The method used to get OAuth access tokens. Users have the option to either directly provide " +
"the OAuth access token or supply a client ID, client secret, and refresh token.")
private String oAuthMethod;

@Nullable
@Macro
@Name(CLIENT_ID)
@Description("OAuth2 client id.")
private String clientId;

@Nullable
@Macro
@Name(CLIENT_SECRET)
@Description("OAuth2 client secret.")
private String clientSecret;

@Nullable
@Macro
@Name(REFRESH_TOKEN)
@Description("OAuth2 refresh token.")
private String refreshToken;

@Nullable
@Macro
@Name(ACCESS_TOKEN)
@Description("Short lived access token used for connecting.")
private String accessToken;

@Nullable
@Macro
@Name(ACCOUNT_FILE_PATH)
Expand Down Expand Up @@ -177,9 +196,13 @@ private boolean validateAuthType(FailureCollector collector) {
}

private boolean validateOAuth2Properties(FailureCollector collector) {
return checkPropertyIsSet(collector, clientId, CLIENT_ID, CLIENT_ID_LABEL)
& checkPropertyIsSet(collector, clientSecret, CLIENT_SECRET, CLIENT_SECRET_LABEL)
& checkPropertyIsSet(collector, refreshToken, REFRESH_TOKEN, REFRESH_TOKEN_LABEL);
if (OAuthMethod.REFRESH_TOKEN.equals(getOAuthMethod())) {
return checkPropertyIsSet(collector, clientId, CLIENT_ID, CLIENT_ID_LABEL)
& checkPropertyIsSet(collector, clientSecret, CLIENT_SECRET, CLIENT_SECRET_LABEL)
& checkPropertyIsSet(collector, refreshToken, REFRESH_TOKEN, REFRESH_TOKEN_LABEL);
} else {
return checkPropertyIsSet(collector, accessToken, ACCESS_TOKEN, ACCESS_TOKEN_LABEL);
}
}

private boolean validateServiceAccount(FailureCollector collector) {
Expand Down Expand Up @@ -290,6 +313,14 @@ public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}

public void setoAuthMethod(String oAuthMethod) {
this.oAuthMethod = oAuthMethod;
}

public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}

@Nullable
public String getClientId() {
return clientId;
Expand All @@ -305,6 +336,11 @@ public String getRefreshToken() {
return refreshToken;
}

@Nullable
public String getAccessToken() {
return accessToken;
}

@Nullable
public String getServiceAccountFilePath() {
if (containsMacro(ACCOUNT_FILE_PATH) || Strings.isNullOrEmpty(accountFilePath)
Expand Down Expand Up @@ -344,4 +380,15 @@ public Boolean isServiceAccountFilePath() {
String serviceAccountType = getServiceAccountType();
return Strings.isNullOrEmpty(serviceAccountType) ? null : serviceAccountType.equals(SERVICE_ACCOUNT_FILE_PATH);
}

public OAuthMethod getOAuthMethod() {
if (oAuthMethod == null) {
return OAuthMethod.REFRESH_TOKEN;
}
try {
return OAuthMethod.valueOf(oAuthMethod.toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid oAuth method " + oAuthMethod);
}
}
}
19 changes: 12 additions & 7 deletions src/main/java/io/cdap/plugin/google/common/GoogleDriveClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,18 @@ protected Drive getDriveClient() throws IOException {
}

protected Credential getOAuth2Credential() {
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setClientSecrets(config.getClientId(),
config.getClientSecret())
.build();
credential.createScoped(getRequiredScopes()).setRefreshToken(config.getRefreshToken());
GoogleCredential credential;
GoogleCredential.Builder builder = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY);
if (OAuthMethod.ACCESS_TOKEN.equals(config.getOAuthMethod())) {
credential = builder.build();
credential.createScoped(getRequiredScopes()).setAccessToken(config.getAccessToken());
} else {
credential = builder.setClientSecrets(config.getClientId(),
config.getClientSecret()).build();
credential.createScoped(getRequiredScopes()).setRefreshToken(config.getRefreshToken());
}
return credential;
}

Expand Down
24 changes: 24 additions & 0 deletions src/main/java/io/cdap/plugin/google/common/OAuthMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright © 2024 Cask Data, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.cdap.plugin.google.common;

/**
* Methods of getting an OAuth access token.
*/
public enum OAuthMethod {
ACCESS_TOKEN,
REFRESH_TOKEN;
}
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,12 @@ public static GoogleDriveSourceConfig of(JsonObject properties) throws IOExcepti
if (properties.has(GoogleDriveSourceConfig.REFRESH_TOKEN)) {
googleDriveSourceConfig.setRefreshToken(properties.get(GoogleDriveSourceConfig.REFRESH_TOKEN).getAsString());
}
if (properties.has(GoogleDriveSourceConfig.ACCESS_TOKEN)) {
googleDriveSourceConfig.setAccessToken(properties.get(GoogleDriveSourceConfig.ACCESS_TOKEN).getAsString());
}
if (properties.has(GoogleDriveSourceConfig.OAUTH_METHOD)) {
googleDriveSourceConfig.setoAuthMethod(properties.get(GoogleDriveSourceConfig.OAUTH_METHOD).getAsString());
}
return googleDriveSourceConfig;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,10 @@ private boolean shouldGetSchema() {
return !containsMacro(SHEETS_TO_PULL) && !containsMacro(SHEETS_IDENTIFIERS) &&
!containsMacro(COLUMN_NAMES_SELECTION) && !containsMacro(CUSTOM_COLUMN_NAMES_ROW) &&
!containsMacro(LAST_DATA_COLUMN) && !containsMacro(NAME_SERVICE_ACCOUNT_TYPE) &&
!containsMacro(ACCOUNT_FILE_PATH) && !containsMacro(NAME_SERVICE_ACCOUNT_JSON);
!containsMacro(ACCOUNT_FILE_PATH) && !containsMacro(NAME_SERVICE_ACCOUNT_JSON) &&
!containsMacro(CLIENT_ID) && !containsMacro(CLIENT_SECRET) &&
!containsMacro(REFRESH_TOKEN) && !containsMacro(ACCESS_TOKEN) &&
!containsMacro(OAUTH_METHOD);
}

/**
Expand Down Expand Up @@ -1258,6 +1261,15 @@ public static GoogleSheetsSourceConfig of(JsonObject properties) throws IOExcept
properties.get(GoogleSheetsSourceConfig.DIRECTORY_IDENTIFIER).getAsString());
}

if (properties.has(GoogleSheetsSourceConfig.OAUTH_METHOD)) {
googleSheetsSourceConfig.setoAuthMethod(
properties.get(GoogleSheetsSourceConfig.OAUTH_METHOD).getAsString());
}

if (properties.has(GoogleSheetsSourceConfig.ACCESS_TOKEN)) {
googleSheetsSourceConfig.setAccessToken(
properties.get(GoogleSheetsSourceConfig.ACCESS_TOKEN).getAsString());
}
return googleSheetsSourceConfig;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,48 @@ public void testValidationErrorJSON() {
Assert.assertEquals("serviceAccountJSON",
collector.getValidationFailures().get(0).getCauses().get(0).getAttribute("stageConfig"));
}

@Test
public void testValidationOauthWithoutAccessToken() {
GoogleDriveSourceConfig config = new GoogleDriveSourceConfig("ref");
config.setReferenceName("validationErrorFilePath");
config.setAuthType("oAuth2");
config.setoAuthMethod(OAuthMethod.ACCESS_TOKEN.name());
config.setModificationDateRange("today");
config.getBodyFormat("string");
config.setStartDate("today");
FailureCollector collector = new DefaultFailureCollector("stageConfig", Collections.EMPTY_MAP);
config.validate(collector);
Assert.assertEquals(1, collector.getValidationFailures().size());
Assert.assertEquals("'Access Token' property is empty or macro is not available.",
collector.getValidationFailures().get(0).getMessage());
Assert.assertEquals("accessToken",
collector.getValidationFailures().get(0).getCauses().get(0).getAttribute("stageConfig"));
}

@Test
public void testValidationOauthWithoutRefreshToken() {
GoogleDriveSourceConfig config = new GoogleDriveSourceConfig(null);
config.setReferenceName("validationErrorFilePath");
config.setAuthType("oAuth2");
config.setoAuthMethod(OAuthMethod.REFRESH_TOKEN.name());
config.setModificationDateRange("today");
config.getBodyFormat("string");
config.setStartDate("today");
FailureCollector collector = new DefaultFailureCollector("stageConfig", Collections.EMPTY_MAP);
config.validate(collector);
Assert.assertEquals(3, collector.getValidationFailures().size());
Assert.assertEquals("'Client ID' property is empty or macro is not available.",
collector.getValidationFailures().get(0).getMessage());
Assert.assertEquals("'Client Secret' property is empty or macro is not available.",
collector.getValidationFailures().get(1).getMessage());
Assert.assertEquals("'Refresh Token' property is empty or macro is not available.",
collector.getValidationFailures().get(2).getMessage());
Assert.assertEquals("clientId",
collector.getValidationFailures().get(0).getCauses().get(0).getAttribute("stageConfig"));
Assert.assertEquals("clientSecret",
collector.getValidationFailures().get(1).getCauses().get(0).getAttribute("stageConfig"));
Assert.assertEquals("refreshToken",
collector.getValidationFailures().get(2).getCauses().get(0).getAttribute("stageConfig"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.api.services.sheets.v4.model.MergeCellsRequest;
import com.google.api.services.sheets.v4.model.RowData;
import io.cdap.plugin.google.common.AuthType;
import io.cdap.plugin.google.common.OAuthMethod;
import io.cdap.plugin.google.sheets.sink.utils.ComplexHeader;
import io.cdap.plugin.google.sheets.sink.utils.FlatteredRowsRecord;
import io.cdap.plugin.google.sheets.sink.utils.FlatteredRowsRequest;
Expand Down Expand Up @@ -49,7 +50,7 @@ public static void setupClient() throws IOException {
EasyMock.expect(sinkConfig.getRefreshToken()).andReturn("dsfdsfdsfdsf").anyTimes();
EasyMock.expect(sinkConfig.getClientSecret()).andReturn("dsfdsfdsfdsf").anyTimes();
EasyMock.expect(sinkConfig.getClientId()).andReturn("dsdsrfegvrb").anyTimes();

EasyMock.expect(sinkConfig.getOAuthMethod()).andReturn(OAuthMethod.REFRESH_TOKEN).anyTimes();
EasyMock.replay(sinkConfig);
sinkClient = new GoogleSheetsSinkClient(sinkConfig);

Expand Down
51 changes: 51 additions & 0 deletions widgets/GoogleDrive-batchsink.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,33 @@
]
}
},
{
"name": "oAuthMethod",
"label": "OAuth Method",
"widget-type": "radio-group",
"widget-attributes": {
"layout": "inline",
"default": "REFRESH_TOKEN",
"options": [
{
"id": "REFRESH_TOKEN",
"label": "Refresh Token"
},
{
"id": "ACCESS_TOKEN",
"label": "Access Token"
}
]
}
},
{
"name": "accessToken",
"label": "Access Token",
"widget-type": "textbox",
"widget-attributes": {
"placeholder": "${oauthAccessToken(provider,credential)}"
}
},
{
"widget-type": "textbox",
"label": "Client ID",
Expand Down Expand Up @@ -115,6 +142,30 @@
"operator": "equal to",
"value": "oAuth2"
},
"show": [
{
"name": "oAuthMethod",
"type": "property"
}
]
},
{
"name": "Authenticate with OAuth2 Access Token",
"condition": {
"expression": "oAuthMethod == 'ACCESS_TOKEN' && authType == 'oAuth2'"
},
"show": [
{
"name": "accessToken",
"type": "property"
}
]
},
{
"name": "Authenticate with OAuth2 Refresh Token",
"condition": {
"expression": "oAuthMethod == null || (oAuthMethod != 'ACCESS_TOKEN' && authType == 'oAuth2')"
},
"show": [
{
"name": "clientId",
Expand Down
Loading

0 comments on commit 9660439

Please sign in to comment.