Skip to content

Commit 7b4021b

Browse files
authored
chore(util-endpoints): add endpoint ruleset library specific to aws (#3909)
1 parent 9917648 commit 7b4021b

File tree

8 files changed

+362
-0
lines changed

8 files changed

+362
-0
lines changed

packages/types/src/endpoint.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
export interface EndpointPartition {
2+
name: string;
3+
dnsSuffix: string;
4+
dualStackDnsSuffix: string;
5+
supportsFIPS: boolean;
6+
supportsDualStack: boolean;
7+
}
8+
9+
export interface EndpointARN {
10+
partition: string;
11+
service: string;
12+
region: string;
13+
accountId: string;
14+
resourceId: Array<string>;
15+
}
16+
117
export enum EndpointURLScheme {
218
HTTP = "http",
319
HTTPS = "https",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./partition";
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { EndpointARN } from "@aws-sdk/types";
2+
3+
import { parseArn } from "./parseArn";
4+
5+
describe(parseArn.name, () => {
6+
const VALID_TEST_CASES: Array<[string, EndpointARN]> = [
7+
[
8+
"arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
9+
{
10+
partition: "aws",
11+
service: "s3",
12+
region: "us-west-2",
13+
accountId: "123456789012",
14+
resourceId: ["accesspoint", "myendpoint"],
15+
},
16+
],
17+
[
18+
"arn:aws:s3:us-west-2:123456789012:accesspoint/myendpoint",
19+
{
20+
partition: "aws",
21+
service: "s3",
22+
region: "us-west-2",
23+
accountId: "123456789012",
24+
resourceId: ["accesspoint", "myendpoint"],
25+
},
26+
],
27+
[
28+
"arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint",
29+
{
30+
partition: "aws",
31+
service: "s3",
32+
region: "us-east-1",
33+
accountId: "123456789012",
34+
resourceId: ["accesspoint", "myendpoint"],
35+
},
36+
],
37+
[
38+
"arn:aws-cn:s3:cn-north-1:123456789012:accesspoint:myendpoint",
39+
{
40+
partition: "aws-cn",
41+
service: "s3",
42+
region: "cn-north-1",
43+
accountId: "123456789012",
44+
resourceId: ["accesspoint", "myendpoint"],
45+
},
46+
],
47+
[
48+
"arn:aws:sns:us-west-2:123456789012:myTopic",
49+
{
50+
partition: "aws",
51+
service: "sns",
52+
region: "us-west-2",
53+
accountId: "123456789012",
54+
resourceId: ["myTopic"],
55+
},
56+
],
57+
[
58+
"arn:aws:sns:::myTopic",
59+
{
60+
partition: "aws",
61+
service: "sns",
62+
region: "",
63+
accountId: "",
64+
resourceId: ["myTopic"],
65+
},
66+
],
67+
];
68+
69+
it.each(VALID_TEST_CASES)("returns for valid arn %s", (input: string, outout: EndpointARN) => {
70+
expect(parseArn(input)).toEqual(outout);
71+
});
72+
73+
it.each(["some:random:string:separated:by:colons", "arn:aws:too:short"])(
74+
"returns null for invalid arn %s",
75+
(input: string) => {
76+
expect(parseArn(input)).toBeNull();
77+
}
78+
);
79+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { EndpointARN } from "@aws-sdk/types";
2+
3+
/**
4+
* Evaluates a single string argument value, and returns an object containing
5+
* details about the parsed ARN.
6+
* If the input was not a valid ARN, the function returns null.
7+
*/
8+
export const parseArn = (value: string): EndpointARN | null => {
9+
const segments = value.split(":");
10+
11+
if (segments.length < 6 || segments[0] !== "arn") return null;
12+
13+
const [
14+
,
15+
//Skip "arn" literal
16+
partition,
17+
service,
18+
region,
19+
accountId,
20+
...resourceId
21+
] = segments;
22+
23+
return {
24+
partition,
25+
service,
26+
region,
27+
accountId,
28+
resourceId: resourceId[0].includes("/") ? resourceId[0].split("/") : resourceId,
29+
};
30+
};
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { partitions } from "./partitions.json";
2+
3+
const MOCK_DEFAULT_PARTITION = {
4+
id: "aws",
5+
regionRegex: "mockDefaultRegionRegex",
6+
regions: {
7+
"mock-region-1": {
8+
dnsSuffix: "mockRegion1DnsSuffix",
9+
dualStackDnsSuffix: "mockRegion1DualStackDnsSuffix",
10+
},
11+
"mock-region-2": {},
12+
},
13+
outputs: {
14+
dnsSuffix: "mockDefaultDnsSuffix",
15+
dualStackDnsSuffix: "mockDefaultDualStackDnsSuffix",
16+
supportsFIPS: false,
17+
supportsDualStack: false,
18+
},
19+
};
20+
21+
const MOCK_PARTITION = {
22+
id: "partitionId",
23+
regionRegex: "mockRegionRegex",
24+
regions: {},
25+
outputs: {
26+
dnsSuffix: "mockPartitionDnsSuffix",
27+
dualStackDnsSuffix: "mockPartitionDualStackDnsSuffix",
28+
supportsFIPS: true,
29+
supportsDualStack: true,
30+
},
31+
};
32+
33+
describe("partition", () => {
34+
describe("should reuturn data when default partition exists", () => {
35+
jest.isolateModules(() => {
36+
jest.mock("./partitions.json", () => ({
37+
partitions: [MOCK_DEFAULT_PARTITION, MOCK_PARTITION],
38+
}));
39+
const { partition } = require("./partition");
40+
41+
describe("should return the data when region is found", () => {
42+
it("returns region data if it exists", () => {
43+
const regionWithRegionData = "mock-region-1";
44+
expect(partition(regionWithRegionData)).toEqual({
45+
name: MOCK_DEFAULT_PARTITION.id,
46+
...MOCK_DEFAULT_PARTITION.outputs,
47+
...MOCK_DEFAULT_PARTITION.regions[regionWithRegionData],
48+
});
49+
});
50+
51+
it("returns partition data if region data does not exist", () => {
52+
const regionWithoutRegionData = "mock-region-2";
53+
expect(partition(regionWithoutRegionData)).toEqual({
54+
name: MOCK_DEFAULT_PARTITION.id,
55+
...MOCK_DEFAULT_PARTITION.outputs,
56+
});
57+
});
58+
});
59+
60+
it("should return the partition data when region is matched with regionRegex", () => {
61+
expect(partition(MOCK_DEFAULT_PARTITION.regionRegex)).toEqual({
62+
name: MOCK_DEFAULT_PARTITION.id,
63+
...MOCK_DEFAULT_PARTITION.outputs,
64+
});
65+
expect(partition(MOCK_PARTITION.regionRegex)).toEqual({
66+
name: MOCK_PARTITION.id,
67+
...MOCK_PARTITION.outputs,
68+
});
69+
});
70+
71+
it("should return the default partition when the region is not found", () => {
72+
expect(partition("non-existant-region")).toEqual({
73+
name: MOCK_DEFAULT_PARTITION.id,
74+
...MOCK_DEFAULT_PARTITION.outputs,
75+
});
76+
});
77+
});
78+
});
79+
80+
it("should throw an error when the default partition is not found, and region doesn't match in partition array or regex", () => {
81+
jest.isolateModules(() => {
82+
jest.mock("./partitions.json", () => ({
83+
partitions: [MOCK_PARTITION],
84+
}));
85+
const { partition } = require("./partition");
86+
expect(() => partition("non-existant-region")).toThrow(
87+
"Provided region was not found in the partition array or regex," +
88+
" and default partition with id 'aws' doesn't exist."
89+
);
90+
});
91+
});
92+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { EndpointPartition } from "@aws-sdk/types";
2+
3+
import { partitions } from "./partitions.json";
4+
5+
const DEFAULT_PARTITION = partitions.find((partition) => partition.id === "aws");
6+
7+
/**
8+
* Evaluates a single string argument value as a region, and matches the
9+
* string value to an AWS partition.
10+
* The matcher MUST always return a successful object describing the partition
11+
* that the region has been determined to be a part of.
12+
*/
13+
export const partition = (value: string): EndpointPartition => {
14+
// Check for explicit region listed in the regions array.
15+
for (const partition of partitions) {
16+
const { id, regions, outputs } = partition;
17+
for (const [region, regionData] of Object.entries(regions)) {
18+
if (region === value) {
19+
return {
20+
name: id,
21+
...outputs,
22+
...regionData,
23+
};
24+
}
25+
}
26+
}
27+
28+
// Check for region that matches a regionRegex pattern.
29+
for (const partition of partitions) {
30+
const { id, regionRegex, outputs } = partition;
31+
if (new RegExp(regionRegex).test(value)) {
32+
return {
33+
name: id,
34+
...outputs,
35+
};
36+
}
37+
}
38+
39+
if (!DEFAULT_PARTITION) {
40+
throw new Error(
41+
"Provided region was not found in the partition array or regex," +
42+
" and default partition with id 'aws' doesn't exist."
43+
);
44+
}
45+
46+
// Return the default partition.
47+
return {
48+
name: DEFAULT_PARTITION.id,
49+
...DEFAULT_PARTITION.outputs,
50+
};
51+
};
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{
2+
"version": "1.1",
3+
"partitions": [
4+
{
5+
"id": "aws",
6+
"regionRegex": "^(us|eu|ap|sa|ca|me|af)-\\w+-\\d+$",
7+
"regions": {
8+
"af-south-1": {},
9+
"af-east-1": {},
10+
"ap-northeast-1": {},
11+
"ap-northeast-2": {},
12+
"ap-northeast-3": {},
13+
"ap-south-1": {},
14+
"ap-southeast-1": {},
15+
"ap-southeast-2": {},
16+
"ap-southeast-3": {},
17+
"ca-central-1": {},
18+
"eu-central-1": {},
19+
"eu-north-1": {},
20+
"eu-south-1": {},
21+
"eu-west-1": {},
22+
"eu-west-2": {},
23+
"eu-west-3": {},
24+
"me-south-1": {},
25+
"sa-east-1": {},
26+
"us-east-1": {},
27+
"us-east-2": {},
28+
"us-west-1": {},
29+
"us-west-2": {},
30+
"aws-global": {}
31+
},
32+
"outputs": {
33+
"dnsSuffix": "amazonaws.com",
34+
"dualStackDnsSuffix": "api.aws",
35+
"supportsFIPS": true,
36+
"supportsDualStack": true
37+
}
38+
},
39+
{
40+
"id": "aws-us-gov",
41+
"regionRegex": "^us\\-gov\\-\\w+\\-\\d+$",
42+
"regions": {
43+
"us-gov-west-1": {},
44+
"us-gov-east-1": {},
45+
"aws-us-gov-global": {}
46+
},
47+
"outputs": {
48+
"dnsSuffix": "amazonaws.com",
49+
"dualStackDnsSuffix": "api.aws",
50+
"supportsFIPS": true,
51+
"supportsDualStack": true
52+
}
53+
},
54+
{
55+
"id": "aws-cn",
56+
"regionRegex": "^cn\\-\\w+\\-\\d+$",
57+
"regions": {
58+
"cn-north-1": {},
59+
"cn-northwest-1": {},
60+
"aws-cn-global": {}
61+
},
62+
"outputs": {
63+
"dnsSuffix": "amazonaws.com.cn",
64+
"dualStackDnsSuffix": "api.amazonwebservices.com.cn",
65+
"supportsFIPS": true,
66+
"supportsDualStack": true
67+
}
68+
},
69+
{
70+
"id": "aws-iso",
71+
"regionRegex": "^us\\-iso\\-\\w+\\-\\d+$",
72+
"outputs": {
73+
"dnsSuffix": "c2s.ic.gov",
74+
"supportsFIPS": true,
75+
"supportsDualStack": false,
76+
"dualStackDnsSuffix": "c2s.ic.gov"
77+
},
78+
"regions": {}
79+
},
80+
{
81+
"id": "aws-iso-b",
82+
"regionRegex": "^us\\-isob\\-\\w+\\-\\d+$",
83+
"outputs": {
84+
"dnsSuffix": "sc2s.sgov.gov",
85+
"supportsFIPS": true,
86+
"supportsDualStack": false,
87+
"dualStackDnsSuffix": "sc2s.sgov.gov"
88+
},
89+
"regions": {}
90+
}
91+
]
92+
}

packages/util-endpoints/src/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * as aws from "./aws";
12
export * from "./booleanEquals";
23
export * from "./getAttr";
34
export * from "./isSet";

0 commit comments

Comments
 (0)