Skip to content

Commit f8926a5

Browse files
authored
fix(endpoint): misc endpoints 2.0 fixes for s3 (#4031)
1 parent f23158a commit f8926a5

File tree

9 files changed

+106
-7
lines changed

9 files changed

+106
-7
lines changed

packages/middleware-endpoint/src/adaptors/getEndpointFromInstructions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const resolveParams = async <
7777
}
7878

7979
if (String(clientConfig.serviceId).toLowerCase() === "s3") {
80-
resolveParamsForS3(endpointParams);
80+
await resolveParamsForS3(endpointParams);
8181
}
8282

8383
return endpointParams;

packages/middleware-endpoint/src/integration-tests/integration.spec.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,40 @@ import { EndpointV2 } from "@aws-sdk/types";
33

44
// import { defaultEndpointResolver } from "../../../../clients/client-s3/src/endpoint/endpointResolver";
55
const defaultEndpointResolver = (...args: any): any => {};
6-
76
import { resolveParams } from "../adaptors";
87
import { EndpointExpectation, EndpointTestCase, ErrorExpectation, ServiceNamespace } from "./integration-test-types";
98
import customTests from "./s3/custom-tests.json";
9+
import generatedTests from "./s3/generated-tests.json";
10+
import o from "./s3/o.json";
11+
import oa from "./s3/oa.json";
12+
import ol from "./s3/ol.json";
13+
import s from "./s3/s.json";
1014

1115
describe("endpoints 2.0 service integration", () => {
1216
describe("s3", () => {
1317
it("placeholder", () => {});
14-
runTestCases(customTests, S3Namespace as ServiceNamespace);
18+
19+
if (customTests.testCases?.length) {
20+
describe("custom", () => {
21+
runTestCases(customTests, S3Namespace as ServiceNamespace);
22+
});
23+
}
24+
25+
if (generatedTests.testCases?.length) {
26+
describe("generated", () => {
27+
runTestCases(generatedTests, S3Namespace as ServiceNamespace);
28+
});
29+
}
30+
31+
return; // skipping additional tests for now.
32+
33+
for (const group of [o, oa, ol, s]) {
34+
if (group.testCases?.length) {
35+
describe("additional test group", () => {
36+
runTestCases(group, S3Namespace as ServiceNamespace);
37+
});
38+
}
39+
}
1540
});
1641
});
1742

@@ -26,6 +51,15 @@ function runTestCases({ testCases }: { testCases: EndpointTestCase[] }, service:
2651
async function runTestCase(testCase: EndpointTestCase, service: ServiceNamespace) {
2752
const { documentation, params = {}, expect: expectation, operationInputs } = testCase;
2853

54+
if (params.UseGlobalEndpoint || params.Region === "aws-global") {
55+
it.skip(documentation || "undocumented testcase", () => {});
56+
return;
57+
}
58+
59+
if (service === S3Namespace) {
60+
params.serviceId = "s3";
61+
}
62+
2963
it(documentation || "undocumented testcase", async () => {
3064
if (isEndpointExpectation(expectation)) {
3165
const { endpoint } = expectation;
@@ -54,16 +88,18 @@ async function runTestCase(testCase: EndpointTestCase, service: ServiceNamespace
5488
const endpointParams = await resolveParams(operationParams, service[`${operationName}Command`], {
5589
...params,
5690
endpointProvider: defaultEndpointResolver,
57-
});
91+
}).catch(pass);
5892
const observedError = await (async () => defaultEndpointResolver(endpointParams as any))().catch(pass);
5993
expect(observedError).not.toBeUndefined();
94+
expect(observedError?.url).toBeUndefined();
6095
// expect(normalizeQuotes(String(observedError))).toContain(normalizeQuotes(error));
6196
}
6297
} else {
63-
const endpointParams = await resolveParams({}, {}, params);
98+
const endpointParams = await resolveParams({}, {}, params).catch(pass);
6499
const observedError = await (async () => defaultEndpointResolver(endpointParams as any))().catch(pass);
65100
console.error(observedError);
66101
expect(observedError).not.toBeUndefined();
102+
expect(observedError?.url).toBeUndefined();
67103
// expect(normalizeQuotes(String(observedError))).toContain(normalizeQuotes(error));
68104
}
69105
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"testCases": [],
3+
"version": "1.0"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"testCases": [],
3+
"version": "1.0"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"testCases": [],
3+
"version": "1.0"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"testCases": [],
3+
"version": "1.0"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"testCases": [],
3+
"version": "1.0"
4+
}

packages/middleware-endpoint/src/service-customizations/s3.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,29 @@ import { EndpointParameters } from "@aws-sdk/types";
33
export const resolveParamsForS3 = async (endpointParams: EndpointParameters) => {
44
const bucket = (endpointParams?.Bucket as string) || "";
55

6-
if (!isDnsCompatibleBucketName(bucket) || bucket.indexOf(".") !== -1) {
6+
if (typeof endpointParams.Bucket === "string") {
7+
endpointParams.Bucket = bucket.replace(/#/g, encodeURIComponent("#")).replace(/\?/g, encodeURIComponent("?"));
8+
}
9+
10+
if (isArnBucketName(bucket)) {
11+
if (endpointParams.ForcePathStyle === true) {
12+
throw new Error("Path-style addressing cannot be used with ARN buckets");
13+
}
14+
} else if (
15+
!isDnsCompatibleBucketName(bucket) ||
16+
(bucket.indexOf(".") !== -1 && !String(endpointParams.Endpoint).startsWith("http:")) ||
17+
bucket.toLowerCase() !== bucket ||
18+
bucket.length < 3
19+
) {
720
endpointParams.ForcePathStyle = true;
821
}
922

23+
if (endpointParams.DisableMultiRegionAccessPoints) {
24+
// inconsistent naming
25+
endpointParams.disableMultiRegionAccessPoints = true;
26+
endpointParams.DisableMRAP = true;
27+
}
28+
1029
return endpointParams;
1130
};
1231

@@ -26,3 +45,13 @@ export const S3_HOSTNAME_PATTERN = /^(.+\.)?s3(-fips)?(\.dualstack)?[.-]([a-z0-9
2645
*/
2746
export const isDnsCompatibleBucketName = (bucketName: string): boolean =>
2847
DOMAIN_PATTERN.test(bucketName) && !IP_ADDRESS_PATTERN.test(bucketName) && !DOTS_PATTERN.test(bucketName);
48+
49+
export const isArnBucketName = (bucketName: string): boolean => {
50+
const [arn, partition, service, region, account, typeOrId] = bucketName.split(":");
51+
const isArn = arn === "arn" && bucketName.split(":").length >= 6;
52+
const isValidArn = [arn, partition, service, account, typeOrId].filter(Boolean).length === 5;
53+
if (isArn && !isValidArn) {
54+
throw new Error(`Invalid ARN: ${bucketName} was an invalid ARN.`);
55+
}
56+
return arn === "arn" && !!partition && !!service && !!account && !!typeOrId;
57+
};

packages/util-endpoints/src/resolveEndpoint.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,19 @@ export const resolveEndpoint = (ruleSetObject: RuleSetObject, options: EndpointR
3131
}
3232
}
3333

34-
return evaluateRules(rules, { endpointParams, logger, referenceRecord: {} });
34+
const endpoint = evaluateRules(rules, { endpointParams, logger, referenceRecord: {} });
35+
36+
if (options.endpointParams?.Endpoint) {
37+
// take protocol and port from custom Endpoint if present.
38+
try {
39+
const givenEndpoint = new URL(options.endpointParams.Endpoint as string);
40+
const { protocol, port } = givenEndpoint;
41+
endpoint.url.protocol = protocol;
42+
endpoint.url.port = port;
43+
} catch (e) {
44+
// ignored
45+
}
46+
}
47+
48+
return endpoint;
3549
};

0 commit comments

Comments
 (0)