Skip to content
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

fix(lib-storage): add missing return keys #2700

Merged
merged 11 commits into from
May 6, 2022
44 changes: 43 additions & 1 deletion lib/lib-storage/src/Upload.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,27 @@ const putObjectTaggingMock = jest.fn().mockResolvedValue({
Success: "Tags have been applied!",
});

const endpointMock = jest.fn().mockResolvedValue({
hostname: "s3.region.amazonaws.com",
port: undefined,
protocol: "https:",
path: "/",
query: undefined,
});

jest.mock("@aws-sdk/client-s3", () => ({
...(jest.requireActual("@aws-sdk/client-s3") as {}),
S3: jest.fn().mockReturnValue({
send: sendMock,
config: {
endpoint: endpointMock,
},
}),
S3Client: jest.fn().mockReturnValue({
send: sendMock,
config: {
endpoint: endpointMock,
},
}),
CreateMultipartUploadCommand: createMultipartMock,
UploadPartCommand: uploadPartMock,
Expand All @@ -36,7 +50,7 @@ jest.mock("@aws-sdk/client-s3", () => ({
PutObjectCommand: putObjectMock,
}));

import { S3 } from "@aws-sdk/client-s3";
import { CompleteMultipartUploadCommandOutput, S3 } from "@aws-sdk/client-s3";
import { Readable } from "stream";

import { Progress, Upload } from "./index";
Expand Down Expand Up @@ -184,6 +198,34 @@ describe(Upload.name, () => {
expect(putObjectTaggingMock).toHaveBeenCalledTimes(0);
});

it("should return a Bucket, Key and Location fields when upload uses a PUT", async () => {
const buffer = Buffer.from("");
const actionParams = { ...params, Body: buffer };
const upload = new Upload({
params: actionParams,
client: new S3({}),
});

const result = (await upload.done()) as CompleteMultipartUploadCommandOutput;
expect(result.Key).toEqual("example-key");
expect(result.Bucket).toEqual("example-bucket");
expect(result.Location).toEqual("https://example-bucket.s3.region.amazonaws.com/example-key");
});

it("should return a Location field formatted in path style when forcePathStyle is true", async () => {
const buffer = Buffer.from("");
const actionParams = { ...params, Body: buffer };
const s3Client = new S3({});
s3Client.config.forcePathStyle = true;
const upload = new Upload({
params: actionParams,
client: s3Client,
});

const result = (await upload.done()) as CompleteMultipartUploadCommandOutput;
expect(result.Location).toEqual("https://s3.region.amazonaws.com/example-bucket/example-key");
});

it("should upload using multi-part when parts are larger than part size", async () => {
// create a string that's larger than 5MB.
const partSize = 1024 * 1024 * 5;
Expand Down
28 changes: 24 additions & 4 deletions lib/lib-storage/src/Upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { AbortController, AbortSignal } from "@aws-sdk/abort-controller";
import {
CompletedPart,
CompleteMultipartUploadCommand,
CompleteMultipartUploadCommandOutput,
CreateMultipartUploadCommand,
CreateMultipartUploadCommandOutput,
PutObjectCommand,
PutObjectCommandInput,
PutObjectCommandOutput,
PutObjectTaggingCommand,
ServiceOutputTypes,
Tag,
UploadPartCommand,
} from "@aws-sdk/client-s3";
import { extendedEncodeURIComponent } from "@aws-sdk/smithy-client";
import { EventEmitter } from "events";

import { byteLength } from "./bytelength";
Expand Down Expand Up @@ -55,7 +56,7 @@ export class Upload extends EventEmitter {
uploadEvent?: string;

private isMultiPart = true;
private putResponse?: PutObjectCommandOutput;
private putResponse?: CompleteMultipartUploadCommandOutput;

constructor(options: Options) {
super();
Expand Down Expand Up @@ -97,8 +98,27 @@ export class Upload extends EventEmitter {
async __uploadUsingPut(dataPart: RawDataPart) {
this.isMultiPart = false;
const params = { ...this.params, Body: dataPart.data };
const putResult = await this.client.send(new PutObjectCommand(params));
this.putResponse = putResult;
const [putResult, endpoint] = await Promise.all([
this.client.send(new PutObjectCommand(params)),
this.client.config.endpoint(),
]);

const locationKey = this.params
.Key!.split("/")
.map((segment) => extendedEncodeURIComponent(segment))
.join("/");
const locationBucket = extendedEncodeURIComponent(this.params.Bucket!);

const Location: string = this.client.config.forcePathStyle
? `${endpoint.protocol}//${endpoint.hostname}/${locationBucket}/${locationKey}`
: `${endpoint.protocol}//${locationBucket}.${endpoint.hostname}/${locationKey}`;

this.putResponse = {
...putResult,
Bucket: this.params.Bucket,
Key: this.params.Key,
Location,
};
const totalSize = byteLength(dataPart.data);
this.__notifyProgress({
loaded: totalSize,
Expand Down