Skip to content

Commit e03c974

Browse files
authored
feat: simplify and add extensive tests got config-resolver (#1401)
1 parent 59948ee commit e03c974

File tree

4 files changed

+179
-39
lines changed

4 files changed

+179
-39
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { Endpoint } from "@aws-sdk/types";
2+
3+
import { resolveEndpointsConfig } from "./EndpointsConfig";
4+
5+
describe("EndpointsConfig", () => {
6+
const region = jest.fn();
7+
const urlParser = jest.fn();
8+
const regionInfoProvider = jest.fn();
9+
10+
const input = { region, urlParser, regionInfoProvider };
11+
12+
afterEach(() => {
13+
jest.clearAllMocks();
14+
});
15+
16+
describe("tls", () => {
17+
[true, false].forEach((tls) => {
18+
it(`returns input.tls when it's ${tls}`, () => {
19+
expect(resolveEndpointsConfig({ ...input, tls }).tls).toStrictEqual(tls);
20+
});
21+
});
22+
23+
it("returns true is input.tls is undefined", () => {
24+
expect(resolveEndpointsConfig({ ...input }).tls).toStrictEqual(true);
25+
});
26+
});
27+
28+
describe("endpoint", () => {
29+
describe("if defined in input.endpoint", () => {
30+
const mockEndpoint: Endpoint = { protocol: "protocol", hostname: "hostname", path: "path" };
31+
32+
it("returns output of urlParser if endpoint is of type string", async () => {
33+
const endpoint = "endpoint";
34+
urlParser.mockReturnValueOnce(mockEndpoint);
35+
const endpointOutput = await resolveEndpointsConfig({ ...input, endpoint }).endpoint();
36+
expect(endpointOutput).toStrictEqual(mockEndpoint);
37+
expect(urlParser).toHaveBeenCalledTimes(1);
38+
expect(urlParser).toHaveBeenCalledWith(endpoint);
39+
});
40+
41+
it("returns promisified endpoint if it's of type object", async () => {
42+
const endpoint = mockEndpoint;
43+
const endpointOutput = await resolveEndpointsConfig({ ...input, endpoint }).endpoint();
44+
expect(endpointOutput).toStrictEqual(endpoint);
45+
expect(urlParser).not.toHaveBeenCalled();
46+
});
47+
48+
it("returns endpoint if it's already Provider<Endpoint>", async () => {
49+
const endpoint = () => Promise.resolve(mockEndpoint);
50+
const endpointOutput = await resolveEndpointsConfig({ ...input, endpoint }).endpoint();
51+
expect(endpointOutput).toStrictEqual(mockEndpoint);
52+
expect(urlParser).not.toHaveBeenCalled();
53+
});
54+
});
55+
56+
describe("if not defined in input.endpoint", () => {
57+
const mockRegion = "mockRegion";
58+
const mockHostname = "mockHostname";
59+
const mockEndpoint: Endpoint = { protocol: "protocol", hostname: "hostname", path: "path" };
60+
61+
describe("returns endpoint", () => {
62+
beforeEach(() => {
63+
region.mockResolvedValueOnce(mockRegion);
64+
regionInfoProvider.mockResolvedValueOnce({ hostname: mockHostname });
65+
urlParser.mockReturnValueOnce(mockEndpoint);
66+
});
67+
68+
it("passes http in urlParser if tls is false", async () => {
69+
const endpoint = await resolveEndpointsConfig({ ...input, tls: false }).endpoint();
70+
expect(endpoint).toStrictEqual(mockEndpoint);
71+
expect(urlParser).toHaveBeenCalledTimes(1);
72+
expect(urlParser).toHaveBeenCalledWith(`http://${mockHostname}`);
73+
});
74+
75+
[true, undefined].forEach((tls) => {
76+
it(`passes https in urlParser if tls is ${tls}`, async () => {
77+
const endpoint = await resolveEndpointsConfig({ ...input, tls }).endpoint();
78+
expect(endpoint).toStrictEqual(mockEndpoint);
79+
expect(urlParser).toHaveBeenCalledTimes(1);
80+
expect(urlParser).toHaveBeenCalledWith(`https://${mockHostname}`);
81+
});
82+
});
83+
});
84+
85+
describe("throws error", () => {
86+
const error = new Error("error");
87+
88+
it("if region throws error", () => {
89+
region.mockRejectedValueOnce(error);
90+
return expect(resolveEndpointsConfig(input).endpoint()).rejects.toStrictEqual(error);
91+
});
92+
93+
describe("if regionInfoProvider", () => {
94+
beforeEach(() => {
95+
region.mockResolvedValueOnce(mockRegion);
96+
});
97+
98+
it("throws error", () => {
99+
regionInfoProvider.mockRejectedValueOnce(error);
100+
return expect(resolveEndpointsConfig(input).endpoint()).rejects.toStrictEqual(error);
101+
});
102+
103+
it("returns undefined", () => {
104+
regionInfoProvider.mockResolvedValueOnce(undefined);
105+
return expect(resolveEndpointsConfig(input).endpoint()).rejects.toStrictEqual(
106+
new Error("Cannot resolve hostname from client config")
107+
);
108+
});
109+
});
110+
});
111+
});
112+
});
113+
});
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,4 @@
1-
import { Endpoint, Provider, RegionInfo, RegionInfoProvider, UrlParser } from "@aws-sdk/types";
2-
3-
export function normalizeEndpoint(
4-
endpoint?: string | Endpoint | Provider<Endpoint>,
5-
urlParser?: UrlParser
6-
): Provider<Endpoint> {
7-
if (typeof endpoint === "string") {
8-
const promisified = Promise.resolve(urlParser!(endpoint));
9-
return () => promisified;
10-
} else if (typeof endpoint === "object") {
11-
const promisified = Promise.resolve(endpoint);
12-
return () => promisified;
13-
}
14-
return endpoint!;
15-
}
1+
import { Endpoint, Provider, RegionInfoProvider, UrlParser } from "@aws-sdk/types";
162

173
export interface EndpointsInputConfig {
184
/**
@@ -25,32 +11,43 @@ export interface EndpointsInputConfig {
2511
*/
2612
tls?: boolean;
2713
}
14+
2815
interface PreviouslyResolved {
2916
regionInfoProvider: RegionInfoProvider;
3017
urlParser: UrlParser;
3118
region: Provider<string>;
3219
}
20+
3321
export interface EndpointsResolvedConfig extends Required<EndpointsInputConfig> {
3422
endpoint: Provider<Endpoint>;
3523
}
36-
export function resolveEndpointsConfig<T>(
24+
25+
export const resolveEndpointsConfig = <T>(
3726
input: T & EndpointsInputConfig & PreviouslyResolved
38-
): T & EndpointsResolvedConfig {
39-
const tls = input.tls === undefined ? true : input.tls;
40-
const endpoint: Provider<Endpoint> = input.endpoint
41-
? normalizeEndpoint(input.endpoint, input.urlParser)
42-
: () =>
43-
input.region().then(async (region) => {
44-
const hostname = ((await input.regionInfoProvider(region)) || ({} as RegionInfo)).hostname;
45-
if (!hostname) {
46-
throw new Error("Cannot resolve hostname from client config");
47-
}
48-
const endpoint = input.urlParser(`${tls ? "https:" : "http:"}//${hostname}`);
49-
return endpoint;
50-
});
51-
return {
52-
...input,
53-
endpoint,
54-
tls,
55-
};
56-
}
27+
): T & EndpointsResolvedConfig => ({
28+
...input,
29+
tls: input.tls ?? true,
30+
endpoint: input.endpoint ? normalizeEndpoint(input) : () => getEndPointFromRegion(input),
31+
});
32+
33+
const normalizeEndpoint = (input: EndpointsInputConfig & PreviouslyResolved): Provider<Endpoint> => {
34+
const { endpoint, urlParser } = input;
35+
if (typeof endpoint === "string") {
36+
const promisified = Promise.resolve(urlParser(endpoint));
37+
return () => promisified;
38+
} else if (typeof endpoint === "object") {
39+
const promisified = Promise.resolve(endpoint);
40+
return () => promisified;
41+
}
42+
return endpoint!;
43+
};
44+
45+
const getEndPointFromRegion = async (input: EndpointsInputConfig & PreviouslyResolved) => {
46+
const { tls = true } = input;
47+
const region = await input.region();
48+
const { hostname } = (await input.regionInfoProvider(region)) ?? {};
49+
if (!hostname) {
50+
throw new Error("Cannot resolve hostname from client config");
51+
}
52+
return input.urlParser(`${tls ? "https:" : "http:"}//${hostname}`);
53+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { resolveRegionConfig } from "./RegionConfig";
2+
3+
describe("RegionConfig", () => {
4+
const regionDefaultProvider = jest.fn();
5+
6+
afterEach(() => {
7+
jest.clearAllMocks();
8+
});
9+
10+
it("assigns input region if present", async () => {
11+
const region = "us-west-2";
12+
const output = await resolveRegionConfig({ region, regionDefaultProvider }).region();
13+
expect(output).toStrictEqual(region);
14+
expect(regionDefaultProvider).not.toHaveBeenCalled();
15+
});
16+
17+
it("assigns value returned by regionDefaultProvider if region not present", () => {
18+
const mockRegion = jest.fn();
19+
regionDefaultProvider.mockReturnValueOnce(mockRegion);
20+
21+
const input = { regionDefaultProvider };
22+
expect(resolveRegionConfig(input).region).toStrictEqual(mockRegion);
23+
24+
expect(regionDefaultProvider).toHaveBeenCalledTimes(1);
25+
expect(regionDefaultProvider).toHaveBeenCalledWith(input);
26+
});
27+
});

packages/config-resolver/src/RegionConfig.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,27 @@ export interface RegionInputConfig {
66
*/
77
region?: string | Provider<string>;
88
}
9+
910
interface PreviouslyResolved {
1011
regionDefaultProvider: (input: any) => Provider<string>;
1112
}
13+
1214
export interface RegionResolvedConfig {
1315
region: Provider<string>;
1416
}
15-
export function resolveRegionConfig<T>(input: T & RegionInputConfig & PreviouslyResolved): T & RegionResolvedConfig {
17+
18+
export const resolveRegionConfig = <T>(input: T & RegionInputConfig & PreviouslyResolved): T & RegionResolvedConfig => {
1619
const region = input.region || input.regionDefaultProvider(input as any);
1720
return {
1821
...input,
1922
region: normalizeRegion(region),
2023
};
21-
}
24+
};
2225

23-
function normalizeRegion(region: string | Provider<string>): Provider<string> {
26+
const normalizeRegion = (region: string | Provider<string>): Provider<string> => {
2427
if (typeof region === "string") {
2528
const promisified = Promise.resolve(region);
2629
return () => promisified;
2730
}
2831
return region as Provider<string>;
29-
}
32+
};

0 commit comments

Comments
 (0)