# Google Drive The Google Drive connector is a [Palantir-provided driver](../../data-integration/foundry-provided-drivers/index.md) for Google Drive. To create a new Google Drive source, follow the [standard setup flow for Palantir-provided drivers](../../data-integration/foundry-provided-drivers/index.md#setup), then use the sections below for Google Drive-specific configuration and networking. For the complete property reference, see the [official Google Drive driver documentation ↗](https://cdn.cdata.com/help/RGK/jdbc/pg_connectionj.htm). ## Choose a Google Drive operation Choose the source and operation based on the result you need: | Task | Foundry path | Result | |---|---|---| | Explore or sync file records | Google Drive connector, including its `Files` table | File metadata such as ID, name, MIME type, and size. | | Extract one file in a batch sync | Google Drive connector's `DownloadFile` stored procedure | Base64-encoded file content in the output dataset. | | Read a blob file on demand | Python or TypeScript v2 Function using a separate OAuth-backed REST API source | File content from Google Drive [`files.get` with `alt=media` ↗](https://developers.google.com/workspace/drive/api/guides/manage-downloads#download_blob_file_content). | | Create or replace a blob file on demand | Python or TypeScript v2 Function using the same REST API source | A Drive API [upload ↗](https://developers.google.com/workspace/drive/api/guides/manage-uploads) that changes the external file. | The `Files` table's [`FileData` property ↗](https://cdn.cdata.com/help/RGK/jdbc/pg_table-files.htm#Columns) does not turn a file-content download into a dataset column. Use the metadata sync to discover file IDs, then pass an ID to `DownloadFile` or an on-demand Function. For a Function, the calling user must have access to the file, and the outbound application's authorized scope must permit the operation. See the [Drive API access boundaries](#read-and-write-blob-files-with-the-drive-api). ## Supported capabilities | Capability | Status | |---|---| | Exploration | 🟢 Generally available | | Batch syncs | 🟢 Generally available | | Incremental | 🟢 Generally available | | [OAuth 2.0 authentication](#oauth-20-authentication) | 🟢 Generally available | | [Table exports](#table-exports) | 🟢 Generally available | ## Configuration The properties below are mandatory or recommended. | Property | Required? | Description | Default | |---|---|---|---| | [`AuthScheme` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_AuthScheme.htm) | Recommended | The type of authentication to use when connecting to Google Drive. | `OAuthJWT` | | [`InitiateOAuth` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_InitiateOAuth.htm) | Recommended | Specifies the process for obtaining or refreshing the OAuth access token, which maintains user access while an authenticated, authorized user is working. | `GETANDREFRESH` | | [`OAuthClientId` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_OAuthClientId.htm) | Recommended | Specifies the client Id that was assigned when the custom OAuth application was created. (Also known as the consumer key.) This ID registers the custom application with the OAuth authorization server. | — | | [`OAuthClientSecret` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_OAuthClientSecret.htm) | Recommended | Specifies the client secret that was assigned when the custom OAuth application was created. (Also known as the consumer secret). This secret registers the custom application with the OAuth authorization server. | — | | [`OAuthJWTCert` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_OAuthJWTCert.htm) | Recommended | The JWT Certificate store. | — | | [`OAuthJWTCertType` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_OAuthJWTCertType.htm) | Recommended | The type of key store containing the JWT Certificate. | `GOOGLEJSONBLOB` | | [`OAuthJWTSubject` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_OAuthJWTSubject.htm) | Recommended | The user subject for which the application is requesting delegated access. | — | ## Networking The table below lists the domains that the source must be able to access to run. For each domain, add a corresponding [egress policy](../../administration/configure-egress/index.md). If the source is hosted on-premises and not directly reachable from Foundry, use an [agent proxy egress policy](../../administration/configure-egress/index.md#agent-proxy-egress-policies) instead. The agent host itself must also be able to reach the listed domains. See [using an agent as a proxy](../../data-connection/agent-proxy/index.md) for details. | Domain | Required | |--- |--- | | accounts.google.com | Always. Required for OAuth | | www.googleapis.com | Always | ## OAuth 2.0 authentication This connector supports OAuth 2.0 authentication. Follow the [OAuth 2.0 guidance for Palantir-provided drivers](../../data-integration/foundry-provided-drivers/index.md#oauth-20-authentication) to configure and authorize the connection. ### Use a service account for metadata sync To use a service account for metadata sync, configure these source properties: | Property | Value | |---|---| | `AuthScheme` | `OAuthJWT` | | `InitiateOAuth` | `GETANDREFRESH` | | `OAuthJWTCertType` | [`GOOGLEJSONBLOB` ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_OAuthJWTCertType.htm), which expects the JSON document itself. `GOOGLEJSON` expects a file path. | | `OAuthJWTCert` | The entire service-account JSON document, entered in the encrypted field. | | `OAuthJWTSubject` | Leave unset unless you have configured domain-wide delegation. | | `Readonly` | Set to `true` if this source only reads metadata, restricting the driver to `SELECT` queries. | Preserve the JSON document's double-quoted keys and escaped newlines in its private key. Keep this credential out of code repositories. Remove unused `OAuthClientId` and `OAuthClientSecret` rows from the source form; they are not part of this service-account setup. ## Table exports This connector supports table exports. Learn how to [set up a table export](../../data-connection/export-overview/index.md#table-exports). ## Extract a file with a batch sync To extract one file, run the [Google Drive `DownloadFile` stored procedure ↗](https://cdn.cdata.com/help/RGK/jdbc/pg_sp-downloadfile.htm) in a sync definition: ```sql EXECUTE DownloadFile Id = '', LocalFile = '' ``` With `LocalFile` empty, the output dataset contains the file content in `FileData`, encoded as Base64 by default. Decode it to binary in a downstream data transformation. Use a source with `Readonly` unset or set to `false`: [`Readonly=true` rejects stored procedures ↗](https://cdn.cdata.com/help/RGK/jdbc/RSBGoogleDrive_p_Readonly.htm). ## Read and write blob files with the Drive API For on-demand file-content operations, configure a separate REST API source backed by an [OAuth 2.0 outbound application](../../administration/configure-outbound-applications/index.md). [Enable exports and source imports](../../functions/api-calls/index.md#configure-access-to-external-apis), then import the source into a Python or TypeScript v2 Functions repository. Replace `` in each example with the RID of that imported REST API source. Run the Function on behalf of a user who has authorized the application. This source uses the calling user's delegated access; it does not reuse the service-account JSON from the metadata source. See [OAuth 2.0 in Functions](../../functions/api-calls/index.md#use-oauth-20-with-outbound-applications) for runtime and authorization requirements. Allow the REST source to reach only the API host `www.googleapis.com:443` and the OAuth hosts `accounts.google.com:443` and `oauth2.googleapis.com:443`. Choose the [Google Drive scopes ↗](https://developers.google.com/workspace/drive/api/guides/api-specific-auth#drive-scopes) for the files each operation needs: | Operation | Scope | Access boundary | |---|---|---| | Read files the application can access | `https://www.googleapis.com/auth/drive.file` | Files created or opened with the application, or selected by the user for the application through Google Picker. | | Read any file the user can access | `https://www.googleapis.com/auth/drive.readonly` | All user-accessible Drive files; this is a restricted scope. | | Create or replace an eligible file | `https://www.googleapis.com/auth/drive.file` | New files and eligible existing files; a file ID alone does not grant edit access. | Public applications requesting `drive.readonly` must meet [Google's restricted-scope verification requirements ↗](https://developers.google.com/workspace/drive/api/guides/api-specific-auth#restricted). A direct Function call in Workshop cannot start interactive authorization. To offer first-time authorization there, use a separate [function-backed action](../../functions/api-calls/index.md#use-oauth-backed-functions-in-actions); otherwise, the user must authorize the same outbound application before calling these Functions. Use [serverless mode](../../functions/functions-deployed/index.md#choose-between-deployed-and-serverless-execution-modes) so the user's OAuth credential can refresh. ### Read one blob file Use Google Drive [`files.get` with `alt=media` ↗](https://developers.google.com/workspace/drive/api/guides/manage-downloads#download_blob_file_content) to read blob bytes. Check the file's metadata and download capability before using this with an arbitrary file ID. Native Google Workspace documents require [`files.export` ↗](https://developers.google.com/workspace/drive/api/guides/manage-downloads#export_google_workspace_document_content) in a supported format. These examples return blob bytes as a base64 string. ```python tab="Python function" import base64 import requests from functions.api import function from functions.sources import get_source SOURCE_RID = "" @function(sources=[SOURCE_RID]) def read_google_drive_blob(file_id: str) -> str: source = get_source(rid=SOURCE_RID) session = requests.Session() proxy = source.get_https_proxy_uri() if proxy: session.proxies.update({"http": proxy, "https": proxy}) session.headers["Authorization"] = ( f"Bearer {source.get_session_credentials().get().access_token}" ) response = session.get( f"https://www.googleapis.com/drive/v3/files/{file_id}", params={"alt": "media", "supportsAllDrives": "true"}, allow_redirects=False, ) if not 200 <= response.status_code < 300: raise RuntimeError(f"Google Drive returned {response.status_code}") return base64.b64encode(response.content).decode("ascii") ``` ```typescript tab="TypeScript v2 function" import { getFetch, getSource } from "@palantir/functions-sources"; const SOURCE_RID = ""; export const config = { sources: [SOURCE_RID] }; export default async function readGoogleDriveBlob(fileId: string): Promise { const source = await getSource({ rid: SOURCE_RID }); const credentials = await source.sessionCredentials?.get(); if (credentials?.type !== "oauth") throw new Error("Authorize the source first"); const sourceFetch = await getFetch(source); const response = await sourceFetch( `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media&supportsAllDrives=true`, { headers: { Authorization: `Bearer ${credentials.accessToken}` }, redirect: "error", }, ); if (!response.ok) throw new Error(`Google Drive returned ${response.status}`); return Buffer.from(await response.arrayBuffer()).toString("base64"); } ``` ### Create a blob file The WRITE examples use a [simple media upload ↗](https://developers.google.com/workspace/drive/api/guides/manage-uploads#perform_a_simple_upload) for content up to 5 MB. They do not retry an interrupted upload or read back the final bytes; add those controls before writing files you cannot easily restore. This example creates metadata in the selected folder, then uploads the bytes. If the upload fails, the new file can remain empty. It returns only the new file ID. ```python tab="Python function" import base64 import requests from functions.api import function from functions.sources import get_source SOURCE_RID = "" @function(sources=[SOURCE_RID]) def create_google_drive_blob( parent_folder_id: str, file_name: str, mime_type: str, content_base64: str ) -> str: source = get_source(rid=SOURCE_RID) session = requests.Session() proxy = source.get_https_proxy_uri() if proxy: session.proxies.update({"http": proxy, "https": proxy}) session.headers["Authorization"] = ( f"Bearer {source.get_session_credentials().get().access_token}" ) created = session.post( "https://www.googleapis.com/drive/v3/files", json={"name": file_name, "mimeType": mime_type, "parents": [parent_folder_id]}, params={"fields": "id", "supportsAllDrives": "true"}, allow_redirects=False, ) if not 200 <= created.status_code < 300: raise RuntimeError(f"Google Drive returned {created.status_code}") file_id = created.json()["id"] uploaded = session.patch( f"https://www.googleapis.com/upload/drive/v3/files/{file_id}", params={"uploadType": "media", "supportsAllDrives": "true"}, data=base64.b64decode(content_base64), headers={"Content-Type": mime_type}, allow_redirects=False, ) if not 200 <= uploaded.status_code < 300: raise RuntimeError(f"Google Drive returned {uploaded.status_code}") return file_id ``` ```typescript tab="TypeScript v2 function" import { getFetch, getSource } from "@palantir/functions-sources"; const SOURCE_RID = ""; export const config = { sources: [SOURCE_RID] }; export default async function createGoogleDriveBlob( parentFolderId: string, fileName: string, mimeType: string, contentBase64: string, ): Promise { const source = await getSource({ rid: SOURCE_RID }); const credentials = await source.sessionCredentials?.get(); if (credentials?.type !== "oauth") throw new Error("Authorize the source first"); const sourceFetch = await getFetch(source); const created = await sourceFetch( "https://www.googleapis.com/drive/v3/files?fields=id&supportsAllDrives=true", { method: "POST", headers: { Authorization: `Bearer ${credentials.accessToken}`, "Content-Type": "application/json" }, body: JSON.stringify({ name: fileName, mimeType, parents: [parentFolderId] }), redirect: "error", }, ); if (!created.ok) throw new Error(`Google Drive returned ${created.status}`); const { id } = (await created.json()) as { id: string }; const uploaded = await sourceFetch( `https://www.googleapis.com/upload/drive/v3/files/${id}?uploadType=media&supportsAllDrives=true`, { method: "PATCH", headers: { Authorization: `Bearer ${credentials.accessToken}`, "Content-Type": mimeType }, body: Buffer.from(contentBase64, "base64"), redirect: "error", }, ); if (!uploaded.ok) throw new Error(`Google Drive returned ${uploaded.status}`); return id; } ``` ### Replace an editable blob file This example overwrites the supplied file ID if the application can edit it. Supply only a file you intend to replace: there is no confirmation, collision check, or read-back. ```python tab="Python function" import base64 import requests from functions.api import function from functions.sources import get_source SOURCE_RID = "" @function(sources=[SOURCE_RID]) def replace_google_drive_blob( file_id: str, mime_type: str, content_base64: str ) -> str: source = get_source(rid=SOURCE_RID) session = requests.Session() proxy = source.get_https_proxy_uri() if proxy: session.proxies.update({"http": proxy, "https": proxy}) session.headers["Authorization"] = ( f"Bearer {source.get_session_credentials().get().access_token}" ) response = session.patch( f"https://www.googleapis.com/upload/drive/v3/files/{file_id}", params={"uploadType": "media", "supportsAllDrives": "true"}, data=base64.b64decode(content_base64), headers={"Content-Type": mime_type}, allow_redirects=False, ) if not 200 <= response.status_code < 300: raise RuntimeError(f"Google Drive returned {response.status_code}") return file_id ``` ```typescript tab="TypeScript v2 function" import { getFetch, getSource } from "@palantir/functions-sources"; const SOURCE_RID = ""; export const config = { sources: [SOURCE_RID] }; export default async function replaceGoogleDriveBlob( fileId: string, mimeType: string, contentBase64: string, ): Promise { const source = await getSource({ rid: SOURCE_RID }); const credentials = await source.sessionCredentials?.get(); if (credentials?.type !== "oauth") throw new Error("Authorize the source first"); const sourceFetch = await getFetch(source); const response = await sourceFetch( `https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=media&supportsAllDrives=true`, { method: "PATCH", headers: { Authorization: `Bearer ${credentials.accessToken}`, "Content-Type": mimeType, }, body: Buffer.from(contentBase64, "base64"), redirect: "error", }, ); if (!response.ok) throw new Error(`Google Drive returned ${response.status}`); return fileId; } ```