feat: Google Drive Export Integration#5145
Open
Sentiaus wants to merge 20 commits into
Open
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5145 +/- ##
============================================
- Coverage 43.35% 42.93% -0.43%
Complexity 2214 2214
============================================
Files 1049 1060 +11
Lines 40561 41514 +953
Branches 4322 4461 +139
============================================
+ Hits 17586 17823 +237
- Misses 21887 22581 +694
- Partials 1088 1110 +22
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The clientSecret is required for auth code exchange on backend for the access and refresh tokens. The apiKey is required for the Google Drive Picker component in the frontend.
Pass exception as second arg to logger.error so stack traces are captured. Also fetch user from DB in /token instead of JWT, and add LazyLogging trait.
- Add DriveService with OAuth connect flow, Google Picker integration, and multipart upload to Drive - Add GoogleDriveConnectComponent for the OAuth callback page - Add export dropdown to list-item (Download / Export to Drive) for workflows and datasets - Register /dashboard/user/google-drive route for the callback - Load Google API script in index.html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Track dropdown visibility with exportMenuVisible - Lock hover styles (background, button group, description) on the card via export-menu-open class - Slow dropdown animation scoped to this dropdown via nzOverlayClassName Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both the per-file and per-version download buttons show a dropdown with Download and Export to Drive options. Button stays highlighted while the menu is open. Toasts on successful connection and export. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tail Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…valid_grant reauth Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ba86047 to
e8487ff
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npm install had replaced it with Yarn Classic format, breaking yarn --immutable in CI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Author
Contributor
|
Thanks for the PR! I I haven't checked all the details yet, but I will take a closer look soon. |
Contributor
Author
|
Got it! @xuang7 I'll try breaking it down into the frontend and backend changes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
There is also a page for explicitly connecting, reconnecting, and checking folders at
/dashboard/user/google-drive, but there is no UI navigation to this.Notably, there is currently no option to remove access, or change drives, and the drive the user connects can be separate from the gmail the user signs in with.
Schema Changes:
This change modifies the User Table, adding
google_drive_refresh_tokenas a VARCHAR(512).Backend Changes:
Created a new resource on the backend called
GoogleDriveAuthResourcewith threeGETendpoints,/token,/connectand/callback.Created new values in
user-system.confandUserSystemConfigcalledclientSecretandapiKey.These are required to set up Google Drive integration and must be configured on Texera GCP.
@RolesAllowed(Array("REGULAR", "ADMIN"))is applied to/tokenand/connect. The/callbackendpoint is not role-gated because it is called via a browser redirect from Google's OAuth servers (noAuthorizationheader), and authenticates the user instead via a short-lived JWT passed in thestatequery 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_tokenstored in the DB.If the user does not have a refresh token, it returns a
no_refresh_tokenstatus. If the refresh token has been revoked or expired, it returnsinvalid_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
reauthquery parameter — whentrue, setsprompt=consentto force Google to issue a new refresh token. This is used when a previous token has returnedinvalid_grant.GET Callback:
Called by Google's OAuth redirect. Authenticates the user via the JWT in the
stateparameter, exchanges thecodeparameter 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/tokenand returns the connection status and access tokenconnect(reauth?): opens a popup to the/connectURL and emits ononConnected()when the OAuth flow completesonConnected(): observable that fires when the popup posts agdrive-connectedmessageexportToDrive(blob, fileName): opens the Google Picker folder selector, then uploads the blob to the chosen folder via the Drive multipart upload APIExport dropdowns — the download button in three locations has been converted to a dropdown with "Download" and "Export to Drive / Connect to Drive" options:
list-item.component) — for workflows and datasetsdataset-detail.component) — separate dropdowns for per-version ZIP download and per-file downloadmenu.component) — for the workflow JSON export buttonAll three locations show a success toast on connection and on successful export, and keep the dropdown button highlighted while the menu is open.
Screenshots of changes:





invalid_granthandling — whengetToken()returnsinvalid_grant(expired/revoked refresh token), adriveNeedsReauthflag is set. Clicking "Connect to Drive" in this state callsconnect(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 aDrive integrationdescribe block covering: token status setsisDriveConnectedcorrectly,onConnected()emission setsisDriveConnected = trueand shows a toast,connect()is called when not connected, workflow export callsexportToDriveand shows a success toast, dataset export callsexportToDriveand shows a success toast.menu.component.spec.ts— added aDrive integrationdescribe block covering: token status,onConnectedtoast,connect()fallback when not connected, andonClickDriveExportWorkflowserializes workflow content and shows a success toast.dataset-detail.component.spec.ts— new spec file covering: token status,onConnectedtoast, and bothonClickDriveExportVersionandonClickDriveExportFileacross connected/disconnected/missing-selection/success paths.The backend
/callbackendpoint 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