Skip to content

Commit

Permalink
feat(config-resolver): add getRegionInfo helper functions (#2701)
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr committed Aug 24, 2021
1 parent 18469ce commit 7c6a790
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/config-resolver/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./CustomEndpointsConfig";
export * from "./EndpointsConfig";
export * from "./RegionConfig";
export * from "./regionInfo";
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { getHostnameTemplate } from "./getHostnameTemplate";
import { getResolvedPartition, PartitionHash } from "./getResolvedPartition";

jest.mock("./getResolvedPartition");

const AWS_TEMPLATE = "{signingService}.{region}.amazonaws.com";

describe(getHostnameTemplate.name, () => {
const mockRegion = "mockRegion";
const mockPartition = "mockPartition";
const mockHostname = "{region}.mockHostname.com";
const mockSigningService = "mockSigningService";

beforeEach(() => {
(getResolvedPartition as jest.Mock).mockReturnValue(mockPartition);
});

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

it("returns hostname template if present in partitionHash", () => {
const mockPartitionHash: PartitionHash = {
[mockPartition]: {
regions: [mockRegion, `${mockRegion}2`, `${mockRegion}3`],
hostname: mockHostname,
},
};

expect(
getHostnameTemplate(mockRegion, { signingService: mockSigningService, partitionHash: mockPartitionHash })
).toEqual(mockHostname);
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, { partitionHash: mockPartitionHash });
});

it("returns default hostname template if not present in partitionHash", () => {
const mockPartitionHash: PartitionHash = {};

expect(
getHostnameTemplate(mockRegion, { signingService: mockSigningService, partitionHash: mockPartitionHash })
).toEqual(AWS_TEMPLATE.replace("{signingService}", mockSigningService));
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, { partitionHash: mockPartitionHash });
});
});
14 changes: 14 additions & 0 deletions packages/config-resolver/src/regionInfo/getHostnameTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { getResolvedPartition, GetResolvedPartitionOptions } from "./getResolvedPartition";

const AWS_TEMPLATE = "{signingService}.{region}.amazonaws.com";

export interface GetHostnameTemplateOptions extends GetResolvedPartitionOptions {
/**
* The name to be used while signing the service request.
*/
signingService: string;
}

export const getHostnameTemplate = (region: string, { signingService, partitionHash }: GetHostnameTemplateOptions) =>
partitionHash[getResolvedPartition(region, { partitionHash })]?.hostname ??
AWS_TEMPLATE.replace("{signingService}", signingService);
203 changes: 203 additions & 0 deletions packages/config-resolver/src/regionInfo/getRegionInfo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { getRegionInfo } from "./getRegionInfo";
import { getResolvedHostname, RegionHash } from "./getResolvedHostname";
import { getResolvedPartition, PartitionHash } from "./getResolvedPartition";

jest.mock("./getResolvedHostname");
jest.mock("./getResolvedPartition");

describe(getRegionInfo.name, () => {
const mockPartition = "mockPartition";
const mockSigningService = "mockSigningService";

const mockRegion = "mockRegion";
const mockHostname = "{region}.mockHostname.com";
const mockEndpointRegion = "mockEndpointRegion";
const mockEndpointHostname = "{region}.mockEndpointHostname.com";

enum RegionCase {
REGION = "Region",
ENDPOINT = "Endpoint",
REGION_AND_ENDPOINT = "Region and Endpoint",
}

const getMockRegionHash = (regionCase: RegionCase): RegionHash => ({
...((regionCase === RegionCase.REGION || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
[mockRegion]: {
hostname: mockHostname,
},
}),
...((regionCase === RegionCase.ENDPOINT || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
[mockEndpointRegion]: {
hostname: mockEndpointHostname,
},
}),
});

const getMockPartitionHash = (regionCase: RegionCase): PartitionHash => ({
[mockPartition]: {
regions: [mockRegion, `${mockRegion}2`, `${mockRegion}3`],
...((regionCase === RegionCase.REGION || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
hostname: mockHostname,
}),
...((regionCase === RegionCase.ENDPOINT || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
endpoint: mockEndpointRegion,
}),
},
});

const getMockResolvedRegion = (regionCase: RegionCase): string =>
regionCase === RegionCase.REGION ? mockRegion : mockEndpointRegion;

const getMockResolvedPartitionOptions = (partitionHash) => ({ partitionHash });

const getMockResolvedHostnameOptions = (regionHash, getResolvedPartitionOptions) => ({
...getResolvedPartitionOptions,
signingService: mockSigningService,
regionHash,
});

beforeEach(() => {
(getResolvedHostname as jest.Mock).mockReturnValue(mockHostname);
(getResolvedPartition as jest.Mock).mockReturnValue(mockPartition);
});

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

describe("returns data based on options passed", () => {
it.each(Object.values(RegionCase))("%s", (regionCase) => {
const mockRegionHash = getMockRegionHash(regionCase);
const mockPartitionHash = getMockPartitionHash(regionCase);

const mockGetResolvedPartitionOptions = getMockResolvedPartitionOptions(mockPartitionHash);
const mockGetResolvedHostnameOptions = getMockResolvedHostnameOptions(
mockRegionHash,
mockGetResolvedPartitionOptions
);

expect(getRegionInfo(mockRegion, mockGetResolvedHostnameOptions)).toEqual({
signingService: mockSigningService,
hostname: mockHostname,
partition: mockPartition,
});

expect(getResolvedHostname).toHaveBeenCalledWith(
getMockResolvedRegion(regionCase),
mockGetResolvedHostnameOptions
);
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, mockGetResolvedPartitionOptions);
});
});

describe("returns signingRegion if present in regionHash", () => {
const getMockRegionHashWithSigningRegion = (
regionCase: RegionCase,
mockRegionHash: RegionHash,
mockSigningRegion: string
): RegionHash => ({
...mockRegionHash,
...((regionCase === RegionCase.REGION || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
[mockRegion]: {
...mockRegionHash[mockRegion],
signingRegion: mockSigningRegion,
},
}),
...((regionCase === RegionCase.ENDPOINT || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
[mockEndpointRegion]: {
...mockRegionHash[mockEndpointRegion],
signingRegion: mockSigningRegion,
},
}),
});

it.each(Object.values(RegionCase))("%s", (regionCase) => {
const mockSigningRegion = "mockSigningRegion";
const mockRegionHash = getMockRegionHash(regionCase);
const mockPartitionHash = getMockPartitionHash(regionCase);

const mockGetResolvedPartitionOptions = getMockResolvedPartitionOptions(mockPartitionHash);
const mockGetResolvedHostnameOptions = getMockResolvedHostnameOptions(
mockRegionHash,
mockGetResolvedPartitionOptions
);

const mockRegionHashWithSigningRegion = getMockRegionHashWithSigningRegion(
regionCase,
mockRegionHash,
mockSigningRegion
);

expect(
getRegionInfo(mockRegion, { ...mockGetResolvedHostnameOptions, regionHash: mockRegionHashWithSigningRegion })
).toEqual({
signingService: mockSigningService,
hostname: mockHostname,
partition: mockPartition,
signingRegion: mockSigningRegion,
});

expect(getResolvedHostname).toHaveBeenCalledWith(getMockResolvedRegion(regionCase), {
...mockGetResolvedHostnameOptions,
regionHash: mockRegionHashWithSigningRegion,
});
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, mockGetResolvedPartitionOptions);
});
});

describe("returns signingService if present in regionHash", () => {
const getMockRegionHashWithSigningService = (
regionCase: RegionCase,
mockRegionHash: RegionHash,
mockSigningService: string
): RegionHash => ({
...mockRegionHash,
...((regionCase === RegionCase.REGION || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
[mockRegion]: {
...mockRegionHash[mockRegion],
signingService: mockSigningService,
},
}),
...((regionCase === RegionCase.ENDPOINT || regionCase === RegionCase.REGION_AND_ENDPOINT) && {
[mockEndpointRegion]: {
...mockRegionHash[mockEndpointRegion],
signingService: mockSigningService,
},
}),
});

it.each(Object.values(RegionCase))("%s", (regionCase) => {
const mockSigningServiceInRegionHash = "mockSigningServiceInRegionHash";
const mockRegionHash = getMockRegionHash(regionCase);
const mockPartitionHash = getMockPartitionHash(regionCase);

const mockGetResolvedPartitionOptions = getMockResolvedPartitionOptions(mockPartitionHash);
const mockGetResolvedHostnameOptions = getMockResolvedHostnameOptions(
mockRegionHash,
mockGetResolvedPartitionOptions
);

const mockRegionHashWithSigningRegion = getMockRegionHashWithSigningService(
regionCase,
mockRegionHash,
mockSigningServiceInRegionHash
);

expect(
getRegionInfo(mockRegion, { ...mockGetResolvedHostnameOptions, regionHash: mockRegionHashWithSigningRegion })
).toEqual({
signingService: mockSigningServiceInRegionHash,
hostname: mockHostname,
partition: mockPartition,
});

expect(getResolvedHostname).toHaveBeenCalledWith(getMockResolvedRegion(regionCase), {
...mockGetResolvedHostnameOptions,
regionHash: mockRegionHashWithSigningRegion,
});
expect(getResolvedPartition).toHaveBeenCalledWith(mockRegion, mockGetResolvedPartitionOptions);
});
});
});
27 changes: 27 additions & 0 deletions packages/config-resolver/src/regionInfo/getRegionInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { RegionInfo } from "@aws-sdk/types";

import { getResolvedHostname, GetResolvedHostnameOptions, RegionHash } from "./getResolvedHostname";
import { getResolvedPartition, PartitionHash } from "./getResolvedPartition";

export { RegionHash, PartitionHash };

export interface GetRegionInfoOptions extends GetResolvedHostnameOptions {}

export const getRegionInfo = (
region: string,
{ signingService, regionHash, partitionHash }: GetRegionInfoOptions
): RegionInfo => {
const partition = getResolvedPartition(region, { partitionHash });
const resolvedRegion = partitionHash[partition]?.endpoint ?? region;
return {
partition,
signingService,
hostname: getResolvedHostname(resolvedRegion, { signingService, regionHash, partitionHash }),
...(regionHash[resolvedRegion]?.signingRegion && {
signingRegion: regionHash[resolvedRegion].signingRegion,
}),
...(regionHash[resolvedRegion]?.signingService && {
signingService: regionHash[resolvedRegion].signingService,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { getHostnameTemplate } from "./getHostnameTemplate";
import { getResolvedHostname, RegionHash } from "./getResolvedHostname";
import { PartitionHash } from "./getResolvedPartition";

jest.mock("./getHostnameTemplate");

describe(getResolvedHostname.name, () => {
const mockSigningService = "mockSigningService";
const mockRegion = "mockRegion";
const mockPartition = "mockPartition";
const mockHostname = "{region}.mockHostname.com";

afterEach(() => {
jest.clearAllMocks();
});

it("returns hostname if available in regionHash", () => {
const mockRegionHash: RegionHash = {
[mockRegion]: {
hostname: mockHostname,
},
};
const mockPartitionHash: PartitionHash = {};

expect(
getResolvedHostname(mockRegion, {
signingService: mockSigningService,
regionHash: mockRegionHash,
partitionHash: mockPartitionHash,
})
).toBe(mockHostname);
expect(getHostnameTemplate).not.toHaveBeenCalled();
});

it("returns hostname from hostname template when not available in regionHash", () => {
const mockRegionHash: RegionHash = {};

(getHostnameTemplate as jest.Mock).mockReturnValue(mockHostname);

const mockPartitionHash: PartitionHash = {
[mockPartition]: {
regions: [mockRegion, `${mockRegion}2`, `${mockRegion}3`],
hostname: mockHostname,
},
};

expect(
getResolvedHostname(mockRegion, {
signingService: mockSigningService,
regionHash: mockRegionHash,
partitionHash: mockPartitionHash,
})
).toBe(mockHostname.replace("{region}", mockRegion));

expect(getHostnameTemplate).toHaveBeenCalledTimes(1);
expect(getHostnameTemplate).toHaveBeenCalledWith(mockRegion, {
signingService: mockSigningService,
partitionHash: mockPartitionHash,
});
});
});
21 changes: 21 additions & 0 deletions packages/config-resolver/src/regionInfo/getResolvedHostname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { RegionInfo } from "@aws-sdk/types";

import { getHostnameTemplate, GetHostnameTemplateOptions } from "./getHostnameTemplate";
import { GetResolvedPartitionOptions } from "./getResolvedPartition";

export type RegionHash = { [key: string]: Partial<Omit<RegionInfo, "partition" | "path">> };

export interface GetResolvedHostnameOptions extends GetHostnameTemplateOptions, GetResolvedPartitionOptions {
/**
* The hash of region with the information specific to that region.
* The information can include hostname, signingService and signingRegion.
*/
regionHash: RegionHash;
}

export const getResolvedHostname = (
region: string,
{ signingService, regionHash, partitionHash }: GetResolvedHostnameOptions
) =>
regionHash[region]?.hostname ??
getHostnameTemplate(region, { signingService, partitionHash }).replace("{region}", region);

0 comments on commit 7c6a790

Please sign in to comment.