Skip to content

Commit

Permalink
chore(config-resolver): refactor EndpointsConfig into multiple files (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr committed Aug 25, 2021
1 parent 4f02b78 commit c182081
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 214 deletions.
144 changes: 0 additions & 144 deletions packages/config-resolver/src/EndpointsConfig.spec.ts

This file was deleted.

69 changes: 0 additions & 69 deletions packages/config-resolver/src/EndpointsConfig.ts

This file was deleted.

32 changes: 32 additions & 0 deletions packages/config-resolver/src/endpointsConfig/configurations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Endpoint, Provider, RegionInfoProvider, UrlParser } from "@aws-sdk/types";

export interface EndpointsInputConfig {
/**
* The fully qualified endpoint of the webservice. This is only required when using a custom endpoint (for example, when using a local version of S3).
*/
endpoint?: string | Endpoint | Provider<Endpoint>;

/**
* Whether TLS is enabled for requests.
*/
tls?: boolean;
}

export interface PreviouslyResolved {
regionInfoProvider: RegionInfoProvider;
urlParser: UrlParser;
region: Provider<string>;
}

export interface EndpointsResolvedConfig extends Required<EndpointsInputConfig> {
/**
* Resolved value for input {@link EndpointsResolvedConfig.endpoint}
*/
endpoint: Provider<Endpoint>;

/**
* Whether the endpoint is specified by caller.
* @internal
*/
isCustomEndpoint: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { getEndpointFromRegion } from "./getEndpointFromRegion";

describe(getEndpointFromRegion.name, () => {
const mockRegion = jest.fn();
const mockUrlParser = jest.fn();
const mockRegionInfoProvider = jest.fn();

const mockInput = { region: mockRegion, urlParser: mockUrlParser, regionInfoProvider: mockRegionInfoProvider };

const mockRegionValue = "mockRegion";
const mockEndpoint = {
protocol: "http:",
hostname: "localhost",
path: "/",
};
const mockRegionInfo = { hostname: "mockHostname" };

beforeEach(() => {
mockRegion.mockResolvedValue(mockRegionValue);
mockUrlParser.mockResolvedValue(mockEndpoint);
mockRegionInfoProvider.mockResolvedValue(mockRegionInfo);
});

afterEach(() => {
expect(mockRegion).toHaveBeenCalledTimes(1);
jest.clearAllMocks();
});

describe("tls", () => {
afterEach(() => {
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue);
});

it("uses protocol https when not defined", async () => {
await getEndpointFromRegion(mockInput);
expect(mockUrlParser).toHaveBeenCalledTimes(1);
expect(mockUrlParser).toHaveBeenCalledWith(`https://${mockRegionInfo.hostname}`);
});

it.each([
["http:", false],
["https:", true],
])("uses protocol %s when set to %s", async (protocol, tls) => {
await getEndpointFromRegion({ ...mockInput, tls });
expect(mockUrlParser).toHaveBeenCalledTimes(1);
expect(mockUrlParser).toHaveBeenCalledWith(`${protocol}//${mockRegionInfo.hostname}`);
});
});

describe("throws if region is invalid", () => {
const errorMsg = "Invalid region in client config";
it.each([
"",
"has_underscore",
"-starts-with-dash",
"ends-with-dash-",
"-starts-and-ends-with-dash-",
"-",
"a-",
"c0nt@in$-$ymb01$",
"a".repeat(64),
])("region: %s", async (region) => {
mockRegion.mockResolvedValue(region);
try {
await getEndpointFromRegion(mockInput);
fail(`expected Error: ${errorMsg}`);
} catch (error) {
expect(error.message).toEqual(errorMsg);
}
expect(mockRegionInfoProvider).not.toHaveBeenCalled();
expect(mockUrlParser).not.toHaveBeenCalled();
});
});

it("throws if hostname is not returned by regionInfoProvider", async () => {
mockRegionInfoProvider.mockResolvedValue({});
const errorMsg = "Cannot resolve hostname from client config";
try {
await getEndpointFromRegion(mockInput);
fail(`expected Error: ${errorMsg}`);
} catch (error) {
expect(error.message).toEqual(errorMsg);
}
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue);
expect(mockUrlParser).not.toHaveBeenCalled();
});

it("returns parsed endpoint", async () => {
const endpoint = await getEndpointFromRegion(mockInput);
expect(endpoint).toEqual(mockEndpoint);
expect(mockRegionInfoProvider).toHaveBeenCalledWith(mockRegionValue);
expect(mockUrlParser).toHaveBeenCalledWith(`https://${mockRegionInfo.hostname}`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { EndpointsInputConfig, PreviouslyResolved } from "./configurations";

export const getEndpointFromRegion = async (input: EndpointsInputConfig & PreviouslyResolved) => {
const { tls = true } = input;
const region = await input.region();

const dnsHostRegex = new RegExp(/^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/);
if (!dnsHostRegex.test(region)) {
throw new Error("Invalid region in client config");
}

const { hostname } = (await input.regionInfoProvider(region)) ?? {};
if (!hostname) {
throw new Error("Cannot resolve hostname from client config");
}

return input.urlParser(`${tls ? "https:" : "http:"}//${hostname}`);
};
2 changes: 2 additions & 0 deletions packages/config-resolver/src/endpointsConfig/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./configurations";
export * from "./resolveEndpointsConfig";

0 comments on commit c182081

Please sign in to comment.