[
https://issues.apache.org/jira/browse/DRILL-8235?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17562745#comment-17562745
]
ASF GitHub Bot commented on DRILL-8235:
---------------------------------------
github-code-scanning[bot] commented on code in PR #2585:
URL: https://github.com/apache/drill/pull/2585#discussion_r913978697
##########
contrib/storage-googlesheets/src/main/java/org/apache/drill/exec/store/googlesheets/utils/GoogleSheetsUtils.java:
##########
@@ -0,0 +1,534 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.drill.exec.store.googlesheets.utils;
+
+import com.google.api.client.auth.oauth2.AuthorizationCodeFlow;
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.api.client.auth.oauth2.DataStoreCredentialRefreshListener;
+import com.google.api.client.auth.oauth2.StoredCredential;
+import
com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
+import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
+import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
+import com.google.api.client.json.JsonFactory;
+import com.google.api.client.json.gson.GsonFactory;
+import com.google.api.client.util.store.DataStore;
+import com.google.api.services.sheets.v4.Sheets;
+import com.google.api.services.sheets.v4.Sheets.Spreadsheets.Values.BatchGet;
+import com.google.api.services.sheets.v4.SheetsScopes;
+import com.google.api.services.sheets.v4.model.AddSheetRequest;
+import com.google.api.services.sheets.v4.model.BatchUpdateSpreadsheetRequest;
+import com.google.api.services.sheets.v4.model.Request;
+import com.google.api.services.sheets.v4.model.Sheet;
+import com.google.api.services.sheets.v4.model.SheetProperties;
+import com.google.api.services.sheets.v4.model.Spreadsheet;
+import com.google.api.services.sheets.v4.model.SpreadsheetProperties;
+import com.google.api.services.sheets.v4.model.UpdateValuesResponse;
+import com.google.api.services.sheets.v4.model.ValueRange;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.drill.common.expression.SchemaPath;
+import org.apache.drill.exec.oauth.OAuthTokenProvider;
+import org.apache.drill.exec.record.metadata.SchemaBuilder;
+import org.apache.drill.exec.record.metadata.TupleMetadata;
+import org.apache.drill.exec.store.googlesheets.DrillDataStore;
+import org.apache.drill.exec.store.googlesheets.GoogleSheetsColumn;
+import
org.apache.drill.exec.store.googlesheets.GoogleSheetsStoragePluginConfig;
+import
org.apache.drill.exec.store.googlesheets.columns.GoogleSheetsColumnRange;
+import org.apache.drill.exec.util.Utilities;
+import org.apache.parquet.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static com.google.api.client.util.Strings.isNullOrEmpty;
+
+
+public class GoogleSheetsUtils {
+
+ private static final Logger logger =
LoggerFactory.getLogger(GoogleSheetsUtils.class);
+ private static final int SAMPLE_SIZE = 5;
+ private static final JsonFactory JSON_FACTORY =
GsonFactory.getDefaultInstance();
+ private static final String UNKNOWN_HEADER = "field_";
+
+ /**
+ * Represents the possible data types found in a GoogleSheets document
+ */
+ public enum DATA_TYPES {
+ /**
+ * Represents a field before the datatype is known
+ */
+ UNKNOWN,
+ /**
+ * A numeric data type, either a float or an int. These are all
+ * converted to Doubles when projected.
+ */
+ NUMERIC,
+ /**
+ * A string data type
+ */
+ VARCHAR,
+ /**
+ * A field containing a date
+ */
+ DATE,
+ /**
+ * A field containing a time
+ */
+ TIME,
+ /**
+ * A field containing timestamps.
+ */
+ TIMESTAMP
+ }
+
+ /**
+ * Returns a Google Credential Object which shall be used to authenticate
calls to the Google Sheets service
+ * @param config The Drill GoogleSheets config
+ * @return An authorized Credential
+ * @throws IOException in the event of network or other connectivity issues,
throws an IOException
+ * @throws GeneralSecurityException In the event the credentials are
incorrect, throws a Security Exception
+ */
+ public static Credential authorize(GoogleSheetsStoragePluginConfig config,
+ DataStore<StoredCredential> dataStore,
+ OAuthTokenProvider tokenProvider,
+ String pluginName,
+ String queryUser) throws IOException,
GeneralSecurityException {
+ GoogleClientSecrets clientSecrets = config.getSecrets();
+ GoogleAuthorizationCodeFlow flow;
+ List<String> scopes = Collections.singletonList(SheetsScopes.SPREADSHEETS);
+
+ if (dataStore == null) {
+ logger.debug("Datastore is null");
+ } else if (dataStore.getDataStoreFactory() == null) {
+ logger.debug("Datastore factory is null");
+ }
+
+ flow = new GoogleAuthorizationCodeFlow.Builder
+ (GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY,
clientSecrets, scopes)
+ .setDataStoreFactory(dataStore.getDataStoreFactory())
+ .setAccessType("offline")
+ .build();
+
+ return loadCredential(queryUser, flow, dataStore);
+ }
+
+ public static Credential loadCredential(String userId,
GoogleAuthorizationCodeFlow flow, DataStore<StoredCredential>
credentialDataStore) throws IOException {
+
+ // No requests need to be performed when userId is not specified.
+ if (isNullOrEmpty(userId)) {
+ return null;
+ }
+
+ if (credentialDataStore == null) {
+ return null;
+ }
+ Credential credential = newCredential(userId, flow, credentialDataStore);
+ StoredCredential stored =
((DrillDataStore<StoredCredential>)credentialDataStore).getStoredCredential(userId);
+ if (stored == null) {
+ return null;
+ }
+ credential.setAccessToken(stored.getAccessToken());
+ credential.setRefreshToken(stored.getRefreshToken());
+
credential.setExpirationTimeMilliseconds(stored.getExpirationTimeMilliseconds());
+
+ return credential;
+ }
+
+ /**
+ * Returns a new credential instance based on the given user ID.
+ *
+ * @param userId user ID or {@code null} if not using a persisted credential
store
+ */
+ private static Credential newCredential(String userId, AuthorizationCodeFlow
flow, DataStore<StoredCredential> credentialDataStore) {
+ Credential.Builder builder =
+ new Credential.Builder(flow.getMethod())
+ .setTransport(flow.getTransport())
+ .setJsonFactory(flow.getJsonFactory())
+ .setTokenServerEncodedUrl(flow.getTokenServerEncodedUrl())
+ .setClientAuthentication(flow.getClientAuthentication())
+ .setRequestInitializer(flow.getRequestInitializer())
+ .setClock(flow.getClock());
+
+ if (credentialDataStore != null) {
+ builder.addRefreshListener(
+ new DataStoreCredentialRefreshListener(userId, credentialDataStore));
+ }
+ builder.getRefreshListeners().addAll(flow.getRefreshListeners());
+ return builder.build();
+ }
+
+
+
+ public static Sheets getSheetsService(GoogleSheetsStoragePluginConfig config,
+ DataStore<StoredCredential> dataStore,
+ OAuthTokenProvider tokenProvider,
+ String pluginName,
+ String queryUser)
+ throws IOException, GeneralSecurityException {
+ Credential credential = GoogleSheetsUtils.authorize(config, dataStore,
tokenProvider, pluginName, queryUser);
+ return new Sheets.Builder(
+ GoogleNetHttpTransport.newTrustedTransport(),
GsonFactory.getDefaultInstance(), credential)
+ .setApplicationName("Drill")
+ .build();
+ }
+
+ /**
+ * Returns the title of the GoogleSheet corresponding with the sheetID.
This is the human readable
+ * name of the actual GoogleSheet document.
+ * @param service An authenticated GoogleSheet service
+ * @param sheetID The sheetID. This can be obtained from the sheet URL
+ * @return The title of the sheet document.
+ * @throws IOException
+ */
+ public static String getSheetTitle (Sheets service, String sheetID) throws
IOException {
+ Spreadsheet spreadsheet = service.spreadsheets().get(sheetID).execute();
+ SpreadsheetProperties properties = spreadsheet.getProperties();
+ return properties.getTitle();
+ }
+
+ /**
+ * Returns a list of the titles of the available spreadsheets within a given
Google sheet.
+ * @param service The Google Sheets service
+ * @param sheetID The sheetID for the Google sheet. This can be obtained
from the URL of your Google sheet
+ * @return A list of spreadsheet names within a given Google Sheet
+ * @throws IOException If the Google sheet is unreachable or invalid.
+ */
+ public static List<Sheet> getSheetList(Sheets service, String sheetID)
throws IOException {
+ Spreadsheet spreadsheet = service.spreadsheets().get(sheetID).execute();
+ return spreadsheet.getSheets();
+ }
+
+ /**
+ * Converts a column index to A1 notation. Google sheets has a limitation of
approx 18k
+ * columns, but that is not enforced here. The column index must be greater
than zero or
+ * the function will return null.
+ *
+ * References code found here:
+ * <a
href="https://stackoverflow.com/questions/21229180/convert-column-index-into-corresponding-column-letter">Stack
Overflow Article</a>
+ * @param column The column index for the desired column. Must be greater
than zero
+ * @return The A1 representation of the column index.
+ */
+ public static String columnToLetter(int column) {
+ if (column <= 0) {
+ return null;
+ }
+
+ int temp = 0;
+ StringBuilder letter = new StringBuilder();
+ while (column > 0) {
+ temp = (column - 1) % 26;
+ letter.insert(0, (char) (temp + 65));
+ column = (column - temp - 1) / 26;
+ }
+ return letter.toString();
+ }
+
+ /**
+ * Given a column reference in A1 notation, this function will
+ * return the column numeric index. GoogleSheets has a limit of approx
+ * 18k columns, but that is not enforced here.
+ *
+ * References code found here:
+ * <a
href="https://stackoverflow.com/questions/21229180/convert-column-index-into-corresponding-column-letter">Stack
Overflow Article</a>
+ * @param letter The desired column in A1 notation
+ * @return The index of the supplied column
+ */
+ public static int letterToColumnIndex(String letter) {
+ // Make sure the letters are all upper case.
+ letter = letter.toUpperCase();
+ int column = 0;
+ int length = letter.length();
+ for (int i = 0; i < length; i++) {
+ column += (Character.codePointAt(letter, i) - 64) * Math.pow(26, length
- i - 1);
Review Comment:
## Implicit narrowing conversion in compound assignment
Implicit cast of source type double to narrower destination type int.
[Show more
details](https://github.com/apache/drill/security/code-scanning/33)
> Add Storage Plugin for Google Sheets
> ------------------------------------
>
> Key: DRILL-8235
> URL: https://issues.apache.org/jira/browse/DRILL-8235
> Project: Apache Drill
> Issue Type: Improvement
> Components: Storage - Other
> Affects Versions: 1.20.1
> Reporter: Charles Givre
> Assignee: Charles Givre
> Priority: Major
> Fix For: 2.0.0
>
>
> Google Sheets is a very commonly used data source among business users.
> Presto and other query engines do include integrations with Google Sheets and
> so it would be useful for Drill to add this functionality.
> The proposed plugin supports both reading and writing to Google Sheets.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)