Sentiaus opened a new pull request, #5145:
URL: https://github.com/apache/texera/pull/5145

   # PR Description
   
   ### What changes were proposed in this PR?
   
   In this PR, I've added integration with Google Drive, such that users can 
now export Datasets, Workflows, and Files to Google Drive. To complete 
integration, Google Picker needs to be enabled in the Texera GCP, as well as a 
apiKey and ClientSecret generated and stored in the production user-system.conf
   
   #### Schema Changes:
   This change modifies the User Table, adding `google_drive_refresh_token` as 
a VARCHAR(512).
   
   #### Backend Changes:
   Created a new resource on the backend called `GoogleDriveAuthResource` with 
three `GET` endpoints, `/token`, `/connect` and `/callback`.
   
   Created new values in `user-system.conf` and `UserSystemConfig` called 
`clientSecret` and `apiKey`.
   **These are required to set up Google Drive integration and must be 
configured on Texera GCP.**
   
   `@RolesAllowed(Array("REGULAR", "ADMIN"))` is applied to `/token` and 
`/connect`. The `/callback` endpoint is not role-gated because it is called via 
a browser redirect from Google's OAuth servers (no `Authorization` header), and 
authenticates the user instead via a short-lived JWT passed in the `state` 
query parameter.
   
   ##### GET Token:
   Makes a call to Google APIs to get a short-lived in-memory access token for 
the user. This is not stored anywhere, and is fetched using the 
`google_drive_refresh_token` stored in the DB.
   
   If the user does not have a refresh token, it returns a `no_refresh_token` 
status. If the refresh token has been revoked or expired, it returns 
`invalid_grant`. The frontend uses these statuses to update its connection 
state accordingly.
   
   ##### GET Connect:
   Returns a Google OAuth authorization URL for the frontend to open in a 
popup. Accepts a `reauth` query parameter — when `true`, sets `prompt=consent` 
to force Google to issue a new refresh token. This is used when a previous 
token has returned `invalid_grant`.
   
   ##### GET Callback:
   Called by Google's OAuth redirect. Authenticates the user via the JWT in the 
`state` parameter, exchanges the `code` parameter for a refresh token using 
Google's token endpoint, then stores the refresh token on the user record.
   
   #### Frontend Changes:
   
   **`DriveService`** — new injectable service (`drive.service.ts`) that:
   - `getToken()`: calls `/token` and returns the connection status and access 
token
   - `connect(reauth?)`: opens a popup to the `/connect` URL and emits on 
`onConnected()` when the OAuth flow completes
   - `onConnected()`: observable that fires when the popup posts a 
`gdrive-connected` message
   - `exportToDrive(blob, fileName)`: opens the Google Picker folder selector, 
then uploads the blob to the chosen folder via the Drive multipart upload API
   
   **Export dropdowns** — the download button in three locations has been 
converted to a dropdown with "Download" and "Export to Drive / Connect to 
Drive" options:
   - **Dashboard list items** (`list-item.component`) — for workflows and 
datasets
   - **Dataset detail page** (`dataset-detail.component`) — separate dropdowns 
for per-version ZIP download and per-file download
   - **Workflow editor menu** (`menu.component`) — for the workflow JSON export 
button
   
   All three locations show a success toast on connection and on successful 
export, and keep the dropdown button highlighted while the menu is open.
   
   **`invalid_grant` handling** — when `getToken()` returns `invalid_grant` 
(expired/revoked refresh token), a `driveNeedsReauth` flag is set. Clicking 
"Connect to Drive" in this state calls `connect(true)`, forcing Google to 
re-issue a refresh token via the consent screen.
   
   ### Any related issues, documentation, discussions?
   Google Documentation to enable Google Picker: 
https://developers.google.com/workspace/drive/picker/guides/overview
   
   Closes #4240
   
   ### How was this PR tested?
   
   Unit tests were added for all Drive-related frontend changes:
   
   - **`list-item.component.spec.ts`** — added a `Drive integration` describe 
block covering: token status sets `isDriveConnected` correctly, `onConnected()` 
emission sets `isDriveConnected = true` and shows a toast, `connect()` is 
called when not connected, workflow export calls `exportToDrive` and shows a 
success toast, dataset export calls `exportToDrive` and shows a success toast.
   
   - **`menu.component.spec.ts`** — added a `Drive integration` describe block 
covering: token status, `onConnected` toast, `connect()` fallback when not 
connected, and `onClickDriveExportWorkflow` serializes workflow content and 
shows a success toast.
   
   - **`dataset-detail.component.spec.ts`** — new spec file covering: token 
status, `onConnected` toast, and both `onClickDriveExportVersion` and 
`onClickDriveExportFile` across 
connected/disconnected/missing-selection/success paths.
   
   The backend `/callback` endpoint was tested manually via the full OAuth flow 
in a local dev environment.
   
   ### Was this PR authored or co-authored using generative AI tooling?
   
   Frontend changes and commit messages generated-by: Claude Sonnet 4.6
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to