Skip to content

Commit cbab94e

Browse files
authored
fix(lib-storage): add missing return keys (#2700)
* fix(lib-storage): add missing return keys * feat: assert putResponse is defined * feat: add encodeURIComponent to Bucket and Key * refactor: move extra key insertion * fix: correct wrong line being deleted * chore: change to ternary operator * chore: remove done from tests
1 parent c0ed833 commit cbab94e

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

Diff for: lib/lib-storage/src/Upload.spec.ts

+43-1
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,27 @@ const putObjectTaggingMock = jest.fn().mockResolvedValue({
2121
Success: "Tags have been applied!",
2222
});
2323

24+
const endpointMock = jest.fn().mockResolvedValue({
25+
hostname: "s3.region.amazonaws.com",
26+
port: undefined,
27+
protocol: "https:",
28+
path: "/",
29+
query: undefined,
30+
});
31+
2432
jest.mock("@aws-sdk/client-s3", () => ({
2533
...(jest.requireActual("@aws-sdk/client-s3") as {}),
2634
S3: jest.fn().mockReturnValue({
2735
send: sendMock,
36+
config: {
37+
endpoint: endpointMock,
38+
},
2839
}),
2940
S3Client: jest.fn().mockReturnValue({
3041
send: sendMock,
42+
config: {
43+
endpoint: endpointMock,
44+
},
3145
}),
3246
CreateMultipartUploadCommand: createMultipartMock,
3347
UploadPartCommand: uploadPartMock,
@@ -36,7 +50,7 @@ jest.mock("@aws-sdk/client-s3", () => ({
3650
PutObjectCommand: putObjectMock,
3751
}));
3852

39-
import { S3 } from "@aws-sdk/client-s3";
53+
import { CompleteMultipartUploadCommandOutput, S3 } from "@aws-sdk/client-s3";
4054
import { Readable } from "stream";
4155

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

201+
it("should return a Bucket, Key and Location fields when upload uses a PUT", async () => {
202+
const buffer = Buffer.from("");
203+
const actionParams = { ...params, Body: buffer };
204+
const upload = new Upload({
205+
params: actionParams,
206+
client: new S3({}),
207+
});
208+
209+
const result = (await upload.done()) as CompleteMultipartUploadCommandOutput;
210+
expect(result.Key).toEqual("example-key");
211+
expect(result.Bucket).toEqual("example-bucket");
212+
expect(result.Location).toEqual("https://example-bucket.s3.region.amazonaws.com/example-key");
213+
});
214+
215+
it("should return a Location field formatted in path style when forcePathStyle is true", async () => {
216+
const buffer = Buffer.from("");
217+
const actionParams = { ...params, Body: buffer };
218+
const s3Client = new S3({});
219+
s3Client.config.forcePathStyle = true;
220+
const upload = new Upload({
221+
params: actionParams,
222+
client: s3Client,
223+
});
224+
225+
const result = (await upload.done()) as CompleteMultipartUploadCommandOutput;
226+
expect(result.Location).toEqual("https://s3.region.amazonaws.com/example-bucket/example-key");
227+
});
228+
187229
it("should upload using multi-part when parts are larger than part size", async () => {
188230
// create a string that's larger than 5MB.
189231
const partSize = 1024 * 1024 * 5;

Diff for: lib/lib-storage/src/Upload.ts

+24-4
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ import { AbortController, AbortSignal } from "@aws-sdk/abort-controller";
22
import {
33
CompletedPart,
44
CompleteMultipartUploadCommand,
5+
CompleteMultipartUploadCommandOutput,
56
CreateMultipartUploadCommand,
67
CreateMultipartUploadCommandOutput,
78
PutObjectCommand,
89
PutObjectCommandInput,
9-
PutObjectCommandOutput,
1010
PutObjectTaggingCommand,
1111
ServiceOutputTypes,
1212
Tag,
1313
UploadPartCommand,
1414
} from "@aws-sdk/client-s3";
15+
import { extendedEncodeURIComponent } from "@aws-sdk/smithy-client";
1516
import { EventEmitter } from "events";
1617

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

5758
private isMultiPart = true;
58-
private putResponse?: PutObjectCommandOutput;
59+
private putResponse?: CompleteMultipartUploadCommandOutput;
5960

6061
constructor(options: Options) {
6162
super();
@@ -97,8 +98,27 @@ export class Upload extends EventEmitter {
9798
async __uploadUsingPut(dataPart: RawDataPart) {
9899
this.isMultiPart = false;
99100
const params = { ...this.params, Body: dataPart.data };
100-
const putResult = await this.client.send(new PutObjectCommand(params));
101-
this.putResponse = putResult;
101+
const [putResult, endpoint] = await Promise.all([
102+
this.client.send(new PutObjectCommand(params)),
103+
this.client.config.endpoint(),
104+
]);
105+
106+
const locationKey = this.params
107+
.Key!.split("/")
108+
.map((segment) => extendedEncodeURIComponent(segment))
109+
.join("/");
110+
const locationBucket = extendedEncodeURIComponent(this.params.Bucket!);
111+
112+
const Location: string = this.client.config.forcePathStyle
113+
? `${endpoint.protocol}//${endpoint.hostname}/${locationBucket}/${locationKey}`
114+
: `${endpoint.protocol}//${locationBucket}.${endpoint.hostname}/${locationKey}`;
115+
116+
this.putResponse = {
117+
...putResult,
118+
Bucket: this.params.Bucket,
119+
Key: this.params.Key,
120+
Location,
121+
};
102122
const totalSize = byteLength(dataPart.data);
103123
this.__notifyProgress({
104124
loaded: totalSize,

0 commit comments

Comments
 (0)