Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(util-endpoints): internal partitions api #4455

Merged
merged 4 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/middleware-retry/src/AdaptiveRetryStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface AdaptiveRetryStrategyOptions extends StandardRetryStrategyOptio
}

/**
* @deprected use AdaptiveRetryStrategy from @aws-sdk/util-retry
* @deprecated use AdaptiveRetryStrategy from @aws-sdk/util-retry
*/
export class AdaptiveRetryStrategy extends StandardRetryStrategy {
private rateLimiter: RateLimiter;
Expand Down
2 changes: 1 addition & 1 deletion packages/middleware-retry/src/StandardRetryStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface StandardRetryStrategyOptions {
}

/**
* @deprected use StandardRetryStrategy from @aws-sdk/util-retry
* @deprecated use StandardRetryStrategy from @aws-sdk/util-retry
*/
export class StandardRetryStrategy implements RetryStrategy {
private retryDecider: RetryDecider;
Expand Down
1 change: 1 addition & 0 deletions packages/middleware-user-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@aws-sdk/protocol-http": "*",
"@aws-sdk/types": "*",
"@aws-sdk/util-endpoints": "*",
"tslib": "^2.3.1"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { HttpRequest } from "@aws-sdk/protocol-http";
import { UserAgentPair } from "@aws-sdk/types";
import { setPartitionInfo, useDefaultPartitionInfo } from "@aws-sdk/util-endpoints";

import { USER_AGENT, X_AMZ_USER_AGENT } from "./constants";
import { userAgentMiddleware } from "./user-agent-middleware";

describe("userAgentMiddleware", () => {
const mockNextHandler = jest.fn();
const mockInternalNextHandler = jest.fn();

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

afterEach(() => {
useDefaultPartitionInfo();
});

describe("should collect user agent pair from default, custom-supplied, and handler context", () => {
[
{ runtime: "node", sdkUserAgentKey: USER_AGENT, userAgentKey: X_AMZ_USER_AGENT },
Expand Down Expand Up @@ -71,6 +77,21 @@ describe("userAgentMiddleware", () => {
expect.stringContaining(expected)
);
});

it(`should include internal metadata, user agent ${ua} customization: ${expected}`, async () => {
const middleware = userAgentMiddleware({
defaultUserAgentProvider: async () => [ua],
runtime,
});

// internal variant
setPartitionInfo({} as any, "a-test-prefix");
const handler = middleware(mockInternalNextHandler, {});
await handler({ input: {}, request: new HttpRequest({ headers: {} }) });
expect(mockInternalNextHandler.mock.calls[0][0].request.headers[sdkUserAgentKey]).toEqual(
expect.stringContaining("a-test-prefix " + expected)
);
});
}
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Pluggable,
UserAgentPair,
} from "@aws-sdk/types";
import { getUserAgentPrefix } from "@aws-sdk/util-endpoints";

import { UserAgentResolvedConfig } from "./configurations";
import { SPACE, UA_ESCAPE_REGEX, USER_AGENT, X_AMZ_USER_AGENT } from "./constants";
Expand Down Expand Up @@ -39,9 +40,13 @@ export const userAgentMiddleware =
const userAgent = context?.userAgent?.map(escapeUserAgent) || [];
const defaultUserAgent = (await options.defaultUserAgentProvider()).map(escapeUserAgent);
const customUserAgent = options?.customUserAgent?.map(escapeUserAgent) || [];
const prefix = getUserAgentPrefix();

// Set value to AWS-specific user agent header
const sdkUserAgentValue = [...defaultUserAgent, ...userAgent, ...customUserAgent].join(SPACE);
const sdkUserAgentValue = (prefix ? [prefix] : [])
.concat([...defaultUserAgent, ...userAgent, ...customUserAgent])
.join(SPACE);

// Get value to be sent with non-AWS-specific user agent header.
const normalUAValue = [
...defaultUserAgent.filter((section) => section.startsWith("aws-sdk-")),
Expand Down
111 changes: 73 additions & 38 deletions packages/util-endpoints/src/lib/aws/partition.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { partitions } from "./partitions.json";
import { getUserAgentPrefix, partition, setPartitionInfo, useDefaultPartitionInfo } from "./partition";
import partitions from "./partitions.json";

const MOCK_DEFAULT_PARTITION = {
id: "aws",
Expand Down Expand Up @@ -33,57 +34,91 @@ const MOCK_PARTITION = {
};

describe("partition", () => {
describe("should reuturn data when default partition exists", () => {
jest.isolateModules(() => {
jest.mock("./partitions.json", () => ({
partitions: [MOCK_DEFAULT_PARTITION, MOCK_PARTITION],
}));
const { partition } = require("./partition");

describe("should return the data when region is found", () => {
it("returns region data if it exists", () => {
const regionWithRegionData = "mock-region-1";
expect(partition(regionWithRegionData)).toEqual({
...MOCK_DEFAULT_PARTITION.outputs,
...MOCK_DEFAULT_PARTITION.regions[regionWithRegionData],
});
});
afterEach(() => {
useDefaultPartitionInfo();
});

it("returns partition data if region data does not exist", () => {
const regionWithoutRegionData = "mock-region-2";
expect(partition(regionWithoutRegionData)).toEqual({
...MOCK_DEFAULT_PARTITION.outputs,
});
});
describe("should return data when default partition exists", () => {
beforeEach(() => {
setPartitionInfo({
partitions: [MOCK_DEFAULT_PARTITION, MOCK_PARTITION],
});
});

it("should return the partition data when region is matched with regionRegex", () => {
expect(partition(MOCK_DEFAULT_PARTITION.regionRegex)).toEqual({
describe("should return the data when region is found", () => {
it("returns region data if it exists", () => {
const regionWithRegionData = "mock-region-1";
expect(partition(regionWithRegionData)).toEqual({
...MOCK_DEFAULT_PARTITION.outputs,
});
expect(partition(MOCK_PARTITION.regionRegex)).toEqual({
...MOCK_PARTITION.outputs,
...MOCK_DEFAULT_PARTITION.regions[regionWithRegionData],
});
});

it("should return the default partition when the region is not found", () => {
expect(partition("non-existant-region")).toEqual({
it("returns partition data if region data does not exist", () => {
const regionWithoutRegionData = "mock-region-2";
expect(partition(regionWithoutRegionData)).toEqual({
...MOCK_DEFAULT_PARTITION.outputs,
});
});
});

it("should return the partition data when region is matched with regionRegex", () => {
expect(partition(MOCK_DEFAULT_PARTITION.regionRegex)).toEqual({
...MOCK_DEFAULT_PARTITION.outputs,
});
expect(partition(MOCK_PARTITION.regionRegex)).toEqual({
...MOCK_PARTITION.outputs,
});
});

it("should return the default partition when the region is not found", () => {
expect(partition("non-existant-region")).toEqual({
...MOCK_DEFAULT_PARTITION.outputs,
});
});
});

it("should throw an error when the default partition is not found, and region doesn't match in partition array or regex", () => {
jest.isolateModules(() => {
jest.mock("./partitions.json", () => ({
partitions: [MOCK_PARTITION],
}));
const { partition } = require("./partition");
expect(() => partition("non-existant-region")).toThrow(
"Provided region was not found in the partition array or regex," +
" and default partition with id 'aws' doesn't exist."
);
setPartitionInfo({
partitions: [MOCK_PARTITION],
});
expect(() => partition("non-existant-region")).toThrow(
"Provided region was not found in the partition array or regex," +
" and default partition with id 'aws' doesn't exist."
);
});

it("should allow setting a custom partitions file", async () => {
const copy = JSON.parse(JSON.stringify(partitions));
setPartitionInfo(copy);
const testRegion = "us-test-135";
copy.partitions[0].regions[testRegion] = {
description: "not a real region",
};
const result = partition(testRegion);
expect(result).toEqual({
description: "not a real region",
dnsSuffix: "amazonaws.com",
dualStackDnsSuffix: "api.aws",
name: "aws",
supportsDualStack: true,
supportsFIPS: true,
});

useDefaultPartitionInfo();
// result is matched by regex, but customization is no longer present.
expect(partition(testRegion)).toEqual({
description: void 0,
dnsSuffix: "amazonaws.com",
dualStackDnsSuffix: "api.aws",
name: "aws",
supportsDualStack: true,
supportsFIPS: true,
});
});

it("should optionally set a user agent prefix", async () => {
setPartitionInfo(null as any, "a-string-prefix");
expect(getUserAgentPrefix()).toBe("a-string-prefix");
});
});
58 changes: 56 additions & 2 deletions packages/util-endpoints/src/lib/aws/partition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,37 @@ import { EndpointPartition } from "@aws-sdk/types";

import partitionsInfo from "./partitions.json";

const { partitions } = partitionsInfo;
const DEFAULT_PARTITION = partitions.find((partition) => partition.id === "aws");
export type PartitionsInfo = {
partitions: Array<{
id: string;
outputs: {
dnsSuffix: string;
dualStackDnsSuffix: string;
name: string;
supportsDualStack: boolean;
supportsFIPS: boolean;
};
regionRegex: string;
regions: Record<
string,
| {
description?: string;
}
| undefined
>;
}>;
};

/**
* The partitions.json data to be used in resolving endpoints.
* @internal
*/
let selectedPartitionsInfo: PartitionsInfo = partitionsInfo;

/**
* @internal
*/
let selectedUserAgentPrefix = "";

/**
* Evaluates a single string argument value as a region, and matches the
Expand All @@ -12,6 +41,7 @@ const DEFAULT_PARTITION = partitions.find((partition) => partition.id === "aws")
* that the region has been determined to be a part of.
*/
export const partition = (value: string): EndpointPartition => {
const { partitions } = selectedPartitionsInfo;
// Check for explicit region listed in the regions array.
for (const partition of partitions) {
const { regions, outputs } = partition;
Expand All @@ -35,6 +65,8 @@ export const partition = (value: string): EndpointPartition => {
}
}

const DEFAULT_PARTITION = partitions.find((partition) => partition.id === "aws");

if (!DEFAULT_PARTITION) {
throw new Error(
"Provided region was not found in the partition array or regex," +
Expand All @@ -47,3 +79,25 @@ export const partition = (value: string): EndpointPartition => {
...DEFAULT_PARTITION.outputs,
};
};

/**
* Set custom partitions.json data.
* @internal
*/
export const setPartitionInfo = (partitionsInfo: PartitionsInfo, userAgentPrefix = "") => {
selectedPartitionsInfo = partitionsInfo;
selectedUserAgentPrefix = userAgentPrefix;
};

/**
* Reset to the default partitions.json data.
* @internal
*/
export const useDefaultPartitionInfo = () => {
setPartitionInfo(partitionsInfo, "");
};

/**
* @internal
*/
export const getUserAgentPrefix = () => selectedUserAgentPrefix;