Skip to content

Commit f367640

Browse files
committed
chore: code annotations S3TM functions
1 parent 53ef260 commit f367640

File tree

6 files changed

+199
-53
lines changed

6 files changed

+199
-53
lines changed

lib/lib-storage/src/s3-transfer-manager/S3TransferManager.e2e.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ describe(S3TransferManager.name, () => {
253253
}
254254
});
255255

256-
describe("(SEP) download single object tests", () => {
257-
async function sepTests(
256+
describe("Required compliance download single object tests", () => {
257+
async function complianceTests(
258258
objectType: "single" | "multipart",
259259
multipartType: "PART" | "RANGE",
260260
range: string | undefined,
@@ -300,16 +300,16 @@ describe(S3TransferManager.name, () => {
300300
}
301301

302302
it("single object: multipartDownloadType = PART, range = 0-12MB, partNumber = null", async () => {
303-
await sepTests("single", "PART", `bytes=0-${12 * 1024 * 1024}`, undefined);
303+
await complianceTests("single", "PART", `bytes=0-${12 * 1024 * 1024}`, undefined);
304304
}, 60_000);
305305
it("multipart object: multipartDownloadType = RANGE, range = 0-12MB, partNumber = null", async () => {
306-
await sepTests("multipart", "RANGE", `bytes=0-${12 * 1024 * 1024}`, undefined);
306+
await complianceTests("multipart", "RANGE", `bytes=0-${12 * 1024 * 1024}`, undefined);
307307
}, 60_000);
308308
it("single object: multipartDownloadType = PART, range = null, partNumber = null", async () => {
309-
await sepTests("single", "PART", undefined, undefined);
309+
await complianceTests("single", "PART", undefined, undefined);
310310
}, 60_000);
311311
it("single object: multipartDownloadType = RANGE, range = null, partNumber = null", async () => {
312-
await sepTests("single", "RANGE", undefined, undefined);
312+
await complianceTests("single", "RANGE", undefined, undefined);
313313
}, 60_000);
314314
});
315315
});

lib/lib-storage/src/s3-transfer-manager/S3TransferManager.ts

Lines changed: 155 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@ import type {
2323
} from "./types";
2424

2525
/**
26-
* Describe what this is
27-
* TODO: Switch all @public to @alpha
28-
* TODO: tag internal for itneral functions
26+
* Client for efficient transfer of objects to and from Amazon S3.
27+
* Provides methods to optimize uploading and downloading individual objects
28+
* as well as entire directories, with support for multipart operations,
29+
* concurrency control, and request cancellation.
30+
* Implements an eventTarget-based progress tracking system with methods to register,
31+
* dispatch, and remove listeners for transfer lifecycle events.
32+
*
2933
* @alpha
3034
*/
35+
3136
export class S3TransferManager implements IS3TransferManager {
3237
private static MIN_PART_SIZE = 5 * 1024 * 1024; // 5MB
3338
private static DEFAULT_PART_SIZE = 8 * 1024 * 1024; // 8MB
@@ -90,6 +95,16 @@ export class S3TransferManager implements IS3TransferManager {
9095
options?: AddEventListenerOptions | boolean
9196
): void;
9297
public addEventListener(type: string, callback: EventListener, options?: AddEventListenerOptions | boolean): void;
98+
/**
99+
* Registers a callback function to be executed when a specific transfer event occurs.
100+
* Supports monitoring the full lifecycle of transfers.
101+
*
102+
* @param type - The type of event to listen for.
103+
* @param callback - Function to execute when the specified event occurs.
104+
* @param options - Optional configuration for event listener behavior.
105+
*
106+
* @alpha
107+
*/
93108
public addEventListener(type: string, callback: EventListener, options?: AddEventListenerOptions | boolean): void {
94109
const eventType = type as keyof TransferEventListeners;
95110
const listeners = this.eventListeners[eventType];
@@ -129,18 +144,18 @@ export class S3TransferManager implements IS3TransferManager {
129144
listeners.push(updatedCallback);
130145
}
131146

147+
public dispatchEvent(event: Event & TransferEvent): boolean;
148+
public dispatchEvent(event: Event & TransferCompleteEvent): boolean;
149+
public dispatchEvent(event: Event): boolean;
132150
/**
133-
* todo: what does the return boolean mean?
151+
* Dispatches an event to the registered event listeners.
152+
* Triggers callbacks registered via addEventListener with matching event types.
134153
*
135-
* it returns false if the event is cancellable, and at least oneo the handlers which received event called
136-
* Event.preventDefault(). Otherwise true.
137-
* The use cases of preventDefault() does not apply to transfermanager but we should still keep the boolean
138-
* and continue to return true to stay consistent with EventTarget.
154+
* @param event - The event object to dispatch.
155+
* @returns whether the event ran to completion
139156
*
157+
* @alpha
140158
*/
141-
public dispatchEvent(event: Event & TransferEvent): boolean;
142-
public dispatchEvent(event: Event & TransferCompleteEvent): boolean;
143-
public dispatchEvent(event: Event): boolean;
144159
public dispatchEvent(event: Event): boolean {
145160
const eventType = event.type;
146161
const listeners = this.eventListeners[eventType as keyof TransferEventListeners] as EventListener[];
@@ -182,6 +197,16 @@ export class S3TransferManager implements IS3TransferManager {
182197
callback: EventListener,
183198
options?: RemoveEventListenerOptions | boolean
184199
): void;
200+
/**
201+
* Removes a previously registered event listener from the specified event type.
202+
* Stops the callback from being invoked when the event occurs.
203+
*
204+
* @param type - The type of event to stop listening for.
205+
* @param callback - The function that was previously registered.
206+
* @param options - Optional configuration for the event listener.
207+
*
208+
* @alpha
209+
*/
185210
public removeEventListener(
186211
type: string,
187212
callback: EventListener,
@@ -209,10 +234,32 @@ export class S3TransferManager implements IS3TransferManager {
209234
}
210235
}
211236

237+
/**
238+
* Uploads objects to S3 with automatic multipart upload handling.
239+
* Automatically chooses between single object upload or multipart upload based on content length threshold.
240+
*
241+
* @param request - PutObjectCommandInput and CreateMultipartUploadCommandInput parameters for single or multipart uploads.
242+
* @param transferOptions - Optional abort signal and event listeners for transfer lifecycle monitoring.
243+
*
244+
* @returns S3 PutObject or CompleteMultipartUpload response with transfer event dispatching.
245+
*
246+
* @alpha
247+
*/
212248
public upload(request: UploadRequest, transferOptions?: TransferOptions): Promise<UploadResponse> {
213249
throw new Error("Method not implemented.");
214250
}
215251

252+
/**
253+
* Downloads single objects from S3 with automatic multipart handling.
254+
* Automatically chooses between PART or RANGE download strategies and joins streams into a single response.
255+
*
256+
* @param request - GetObjectCommandInput parameters. PartNumber is not supported - use GetObjectCommand directly for specific parts.
257+
* @param transferOptions - Optional abort signal and event listeners for transfer lifecycle monitoring.
258+
*
259+
* @returns S3 GetObject response with joined Body stream and transfer event dispatching.
260+
*
261+
* @alpha
262+
*/
216263
public async download(request: DownloadRequest, transferOptions?: TransferOptions): Promise<DownloadResponse> {
217264
const partNumber = request.PartNumber;
218265
if (typeof partNumber === "number") {
@@ -248,12 +295,6 @@ export class S3TransferManager implements IS3TransferManager {
248295
}
249296
};
250297

251-
// TODO:
252-
// after completing SEP requirements:
253-
// - acquire lock on webstreams in the same
254-
// - synchronous frame as they are opened or else
255-
// - the connection might be closed too early.
256-
257298
const response = {
258299
...metadata,
259300
Body: await joinStreams(streams, {
@@ -299,6 +340,16 @@ export class S3TransferManager implements IS3TransferManager {
299340
return response;
300341
}
301342

343+
/**
344+
* Uploads all files in a directory recursively to an S3 bucket.
345+
* Automatically maps local file paths to S3 object keys using prefix and delimiter configuration.
346+
*
347+
* @param options - Configuration including bucket, source directory, filtering, failure handling, and transfer settings.
348+
*
349+
* @returns the number of objects that have been uploaded and the number of objects that have failed
350+
*
351+
* @alpha
352+
*/
302353
public uploadAll(options: {
303354
bucket: string;
304355
source: string;
@@ -314,6 +365,16 @@ export class S3TransferManager implements IS3TransferManager {
314365
throw new Error("Method not implemented.");
315366
}
316367

368+
/**
369+
* Downloads all objects in a bucket to a local directory.
370+
* Uses ListObjectsV2 to retrieve objects and automatically maps S3 object keys to local file paths.
371+
*
372+
* @param options - Configuration including bucket, destination directory, filtering, failure handling, and transfer settings.
373+
*
374+
* @returns The number of objects that have been downloaded and the number of objects that have failed
375+
*
376+
* @alpha
377+
*/
317378
public downloadAll(options: {
318379
bucket: string;
319380
destination: string;
@@ -328,6 +389,11 @@ export class S3TransferManager implements IS3TransferManager {
328389
throw new Error("Method not implemented.");
329390
}
330391

392+
/**
393+
* Downloads object using part-based strategy with concurrent part requests.
394+
*
395+
* @internal
396+
*/
331397
protected async downloadByPart(
332398
request: DownloadRequest,
333399
transferOptions: TransferOptions,
@@ -444,6 +510,11 @@ export class S3TransferManager implements IS3TransferManager {
444510
};
445511
}
446512

513+
/**
514+
* Downloads object using range-based strategy with concurrent range requests.
515+
*
516+
* @internal
517+
*/
447518
protected async downloadByRange(
448519
request: DownloadRequest,
449520
transferOptions: TransferOptions,
@@ -589,6 +660,11 @@ export class S3TransferManager implements IS3TransferManager {
589660
};
590661
}
591662

663+
/**
664+
* Adds all event listeners from provided collection to the transfer manager.
665+
*
666+
* @internal
667+
*/
592668
private addEventListeners(eventListeners?: TransferEventListeners): void {
593669
for (const listeners of this.iterateListeners(eventListeners)) {
594670
for (const listener of listeners) {
@@ -597,6 +673,11 @@ export class S3TransferManager implements IS3TransferManager {
597673
}
598674
}
599675

676+
/**
677+
* Removes event listeners from provided collection from the transfer manager.
678+
*
679+
* @internal
680+
*/
600681
private removeEventListeners(eventListeners?: TransferEventListeners): void {
601682
for (const listeners of this.iterateListeners(eventListeners)) {
602683
for (const listener of listeners) {
@@ -605,6 +686,11 @@ export class S3TransferManager implements IS3TransferManager {
605686
}
606687
}
607688

689+
/**
690+
* Copies all response properties except Body to the container object.
691+
*
692+
* @internal
693+
*/
608694
private assignMetadata(container: any, response: any) {
609695
for (const key in response) {
610696
if (key === "Body") {
@@ -614,13 +700,23 @@ export class S3TransferManager implements IS3TransferManager {
614700
}
615701
}
616702

703+
/**
704+
* Updates response ContentLength and ContentRange based on total object size.
705+
*
706+
* @internal
707+
*/
617708
private updateResponseLengthAndRange(response: DownloadResponse, totalSize: number | undefined): void {
618709
if (totalSize !== undefined) {
619710
response.ContentLength = totalSize;
620711
response.ContentRange = `bytes 0-${totalSize - 1}/${totalSize}`;
621712
}
622713
}
623714

715+
/**
716+
* Clears checksum values for composite multipart downloads.
717+
*
718+
* @internal
719+
*/
624720
private updateChecksumValues(initialPart: DownloadResponse, metadata: Omit<DownloadResponse, "Body">) {
625721
if (initialPart.ChecksumType === "COMPOSITE") {
626722
metadata.ChecksumCRC32 = undefined;
@@ -630,6 +726,11 @@ export class S3TransferManager implements IS3TransferManager {
630726
}
631727
}
632728

729+
/**
730+
* Processes response metadata by updating length, copying properties, and handling checksums.
731+
*
732+
* @internal
733+
*/
633734
private processResponseMetadata(
634735
response: DownloadResponse,
635736
metadata: Omit<DownloadResponse, "Body">,
@@ -640,18 +741,33 @@ export class S3TransferManager implements IS3TransferManager {
640741
this.updateChecksumValues(response, metadata);
641742
}
642743

744+
/**
745+
* Throws AbortError if transfer has been aborted via signal.
746+
*
747+
* @internal
748+
*/
643749
private checkAborted(transferOptions?: TransferOptions): void {
644750
if (transferOptions?.abortSignal?.aborted) {
645751
throw Object.assign(new Error("Download aborted."), { name: "AbortError" });
646752
}
647753
}
648754

755+
/**
756+
* Validates configuration parameters meet minimum requirements.
757+
*
758+
* @internal
759+
*/
649760
private validateConfig(): void {
650761
if (this.targetPartSizeBytes < S3TransferManager.MIN_PART_SIZE) {
651762
throw new Error(`targetPartSizeBytes must be at least ${S3TransferManager.MIN_PART_SIZE} bytes`);
652763
}
653764
}
654765

766+
/**
767+
* Dispatches transferInitiated event with initial progress snapshot.
768+
*
769+
* @internal
770+
*/
655771
private dispatchTransferInitiatedEvent(request: DownloadRequest | UploadRequest, totalSize?: number): boolean {
656772
this.dispatchEvent(
657773
Object.assign(new Event("transferInitiated"), {
@@ -665,6 +781,11 @@ export class S3TransferManager implements IS3TransferManager {
665781
return true;
666782
}
667783

784+
/**
785+
* Dispatches transferFailed event with error details and progress snapshot.
786+
*
787+
* @internal
788+
*/
668789
private dispatchTransferFailedEvent(
669790
request: DownloadRequest | UploadRequest,
670791
totalSize?: number,
@@ -683,6 +804,11 @@ export class S3TransferManager implements IS3TransferManager {
683804
return true;
684805
}
685806

807+
/**
808+
* Generator that yields event listeners from the provided collection for iteration.
809+
*
810+
* @internal
811+
*/
686812
private *iterateListeners(eventListeners: TransferEventListeners = {}) {
687813
for (const key in eventListeners) {
688814
const eventType = key as keyof TransferEventListeners;
@@ -700,6 +826,11 @@ export class S3TransferManager implements IS3TransferManager {
700826
}
701827
}
702828

829+
/**
830+
* Validates part download ContentRange matches expected part boundaries.
831+
*
832+
* @internal
833+
*/
703834
private validatePartDownload(contentRange: string | undefined, partNumber: number, partSize: number) {
704835
if (!contentRange) {
705836
throw new Error(`Missing ContentRange for part ${partNumber}.`);
@@ -724,6 +855,11 @@ export class S3TransferManager implements IS3TransferManager {
724855
}
725856
}
726857

858+
/**
859+
* Validates range download ContentRange matches requested byte range.
860+
*
861+
* @internal
862+
*/
727863
private validateRangeDownload(requestRange: string, responseRange: string | undefined) {
728864
if (!responseRange) {
729865
throw new Error(`Missing ContentRange for range ${requestRange}.`);
@@ -757,8 +893,9 @@ export class S3TransferManager implements IS3TransferManager {
757893
throw new Error(`Expected range to end at ${expectedEnd} but got ${end}`);
758894
}
759895
}
896+
760897
/**
761-
*
898+
* Internal event handler for download lifecycle hooks.
762899
*
763900
* @internal
764901
*/

0 commit comments

Comments
 (0)