-
Notifications
You must be signed in to change notification settings - Fork 345
feat(metadata-view): Bulk download logic only #4237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -36,6 +36,8 @@ import OpenWithAPI from './OpenWith'; | |||||||||||||||||||||||||||
| import MetadataQueryAPI from './MetadataQuery'; | ||||||||||||||||||||||||||||
| import BoxEditAPI from './box-edit'; | ||||||||||||||||||||||||||||
| import IntelligenceAPI from './Intelligence'; | ||||||||||||||||||||||||||||
| // $FlowFixMe | ||||||||||||||||||||||||||||
| import ZipDownloadAPI from './ZipDownload'; | ||||||||||||||||||||||||||||
| import { DEFAULT_HOSTNAME_API, DEFAULT_HOSTNAME_UPLOAD, TYPE_FOLDER, TYPE_FILE, TYPE_WEBLINK } from '../constants'; | ||||||||||||||||||||||||||||
| import type { ItemType } from '../common/types/core'; | ||||||||||||||||||||||||||||
| import type { APIOptions } from '../common/types/api'; | ||||||||||||||||||||||||||||
|
|
@@ -204,6 +206,8 @@ class APIFactory { | |||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| intelligenceAPI: IntelligenceAPI; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| zipDownloadAPI: ZipDownloadAPI; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+209
to
+210
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add destroy handling for zipDownloadAPI to prevent leaks APIFactory.destroy currently does not clear zipDownloadAPI, unlike other APIs. Add symmetrical cleanup to avoid retaining instances/options. Apply this diff near the other cleanup blocks in destroy(): if (this.intelligenceAPI) {
this.intelligenceAPI.destroy();
delete this.intelligenceAPI;
}
+
+ if (this.zipDownloadAPI) {
+ // No-op if destroy is not implemented
+ if (typeof this.zipDownloadAPI.destroy === 'function') {
+ this.zipDownloadAPI.destroy();
+ }
+ delete this.zipDownloadAPI;
+ }And consider adding a no-op destroy() method to ZipDownloadAPI for consistency. I can add the no-op destroy() to ZipDownloadAPI if you want. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (2.1.2)[error] 208-208: return types can only be used in TypeScript files remove this type annotation (parse) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * [constructor] | ||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||
|
|
@@ -857,6 +861,11 @@ class APIFactory { | |||||||||||||||||||||||||||
| this.intelligenceAPI = new IntelligenceAPI(this.options); | ||||||||||||||||||||||||||||
| return this.intelligenceAPI; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| getZipDownloadAPI(): ZipDownloadAPI { | ||||||||||||||||||||||||||||
| this.zipDownloadAPI = new ZipDownloadAPI(this.options); | ||||||||||||||||||||||||||||
| return this.zipDownloadAPI; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+865
to
+868
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent ❓ Verification inconclusiveGuard against non-string tokens; require a concrete token for ZipDownload. ZipDownloadAPI expects a string token, but APIFactory forwards options where token can be string|function. That’ll blow up at runtime, fool. Apply this diff to enforce a concrete token and optionally accept a file name: - getZipDownloadAPI(): ZipDownloadAPI {
- this.zipDownloadAPI = new ZipDownloadAPI(this.options);
- return this.zipDownloadAPI;
- }
+ getZipDownloadAPI(opts?: { downloadFileName?: string }): ZipDownloadAPI {
+ const { token } = this.options;
+ if (typeof token !== 'string') {
+ throw new Error('ZipDownloadAPI requires a concrete string token. Resolve function/async tokens before calling getZipDownloadAPI.');
+ }
+ this.zipDownloadAPI = new ZipDownloadAPI({
+ token,
+ downloadFileName: opts?.downloadFileName,
+ });
+ return this.zipDownloadAPI;
+ }If you prefer passing the token explicitly (to mirror past patterns), I can switch the signature to Guard getZipDownloadAPI against non-string tokens, fool—throw if token isn’t a string!
🧰 Tools🪛 Biome (2.1.2)[error] 865-865: return type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax. TypeScript only syntax (parse) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export default APIFactory; | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| /** | ||
| * @file ZipDownload class for creating and downloading ZIP archives using Box TypeScript SDK | ||
| * @author Box | ||
| */ | ||
|
|
||
| import { BoxClient, BoxDeveloperTokenAuth } from 'box-typescript-sdk-gen'; | ||
| import { ZipDownloadRequest } from 'box-typescript-sdk-gen/lib/schemas/zipDownloadRequest.generated.d.ts.js'; | ||
|
|
||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| export interface ZipDownloadItem { | ||
| id: string; | ||
| type: 'file' | 'folder'; | ||
| } | ||
|
|
||
| export interface ZipDownloadOptions { | ||
| token: string; | ||
| downloadFileName?: string; | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export interface ZipDownloadResponse { | ||
| downloadUrl?: string; | ||
| statusUrl?: string; | ||
| expiresAt?: string; | ||
| state?: 'in_progress' | 'failed' | 'succeeded'; | ||
| totalCount?: number; | ||
| downloadedCount?: number; | ||
| skippedCount?: number; | ||
| } | ||
|
|
||
| /** | ||
| * ZipDownload class for creating and downloading ZIP archives from Box items | ||
| * Uses the box-typescript-sdk-gen for modern TypeScript support | ||
| */ | ||
| export default class ZipDownloadAPI { | ||
| private client: BoxClient; | ||
|
|
||
| private options: ZipDownloadOptions; | ||
|
|
||
| /** | ||
| * Constructor | ||
| * @param options - Configuration options including auth token | ||
| */ | ||
| constructor(options: ZipDownloadOptions) { | ||
| this.options = options; | ||
|
|
||
| // Initialize Box client with developer token authentication | ||
| const auth = new BoxDeveloperTokenAuth({ token: options.token }); | ||
| this.client = new BoxClient({ | ||
| auth, | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Create a ZIP download request and initiate the download | ||
| * @param items - Array of file and folder items to include in ZIP | ||
| * @returns Promise resolving to the ZIP download response | ||
| */ | ||
| async createZipDownload(items: ZipDownloadItem[]): Promise<ZipDownloadResponse> { | ||
| if (!items || items.length === 0) { | ||
| throw new Error('Items array cannot be empty'); | ||
| } | ||
|
|
||
| // Create the ZIP download request | ||
| const zipRequest: ZipDownloadRequest = { | ||
| items, | ||
| downloadFileName: this.options.downloadFileName, | ||
| }; | ||
|
|
||
| try { | ||
| // Create the ZIP download using the Box SDK | ||
| const zipDownload = await this.client.zipDownloads.createZipDownload(zipRequest); | ||
|
|
||
| // Only download if we have a download URL | ||
| if (zipDownload.downloadUrl) { | ||
| this.downloadZipFile(zipDownload.downloadUrl); | ||
| } | ||
|
|
||
| return zipDownload as unknown as ZipDownloadResponse; | ||
| } catch (error: unknown) { | ||
| const errorMessage = error instanceof Error ? error.message : String(error); | ||
| throw new Error(`Failed to create ZIP download: ${errorMessage}`); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Download the ZIP file bytestream to the user's device using window.open | ||
| * @param url - The URL of the ZIP file to download | ||
| */ | ||
| private downloadZipFile(url: string): void { | ||
| try { | ||
| // Open in new tab - user can save from there | ||
| window.open(url, '_blank', 'noopener,noreferrer'); | ||
|
|
||
| window.focus(); | ||
|
|
||
| // Clean up after a delay to allow the download to start | ||
| setTimeout(() => { | ||
| URL.revokeObjectURL(url); | ||
| }, 1000); | ||
| } catch (error: unknown) { | ||
| const errorMessage = error instanceof Error ? error.message : String(error); | ||
| throw new Error(`Failed to download ZIP file: ${errorMessage}`); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Move box-typescript-sdk-gen to peerDeps (and devDeps) to avoid bundling the whole SDK.
This is a library; shipping the SDK in dependencies risks duplicate installs and bigger bundles for consumers. Make it a peer, keep a devDep for local builds. Don’t weigh folks down, fool.
Apply this diff:
And add to peerDependencies:
"peerDependencies": { + "box-typescript-sdk-gen": ">=1.17.1",📝 Committable suggestion