Skip to content

Commit c7b4bac

Browse files
committed
feat(endpoint): update existing packages for endpoints v2
1 parent a5e635b commit c7b4bac

File tree

20 files changed

+209
-98
lines changed

20 files changed

+209
-98
lines changed

clients/client-s3/src/commands/GetObjectCommand.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,6 @@ export class GetObjectCommand extends $Command<GetObjectCommandInput, GetObjectC
257257

258258
const stack = clientStack.concat(this.middlewareStack);
259259

260-
console.log(stack.identify());
261-
262260
const { logger } = configuration;
263261
const clientName = "S3Client";
264262
const commandName = "GetObjectCommand";

lib/lib-storage/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
},
2525
"license": "Apache-2.0",
2626
"dependencies": {
27+
"@aws-sdk/middleware-endpoint": "*",
2728
"@aws-sdk/smithy-client": "*",
2829
"buffer": "5.6.0",
2930
"events": "3.3.0",
@@ -37,6 +38,7 @@
3738
"devDependencies": {
3839
"@aws-sdk/abort-controller": "*",
3940
"@aws-sdk/client-s3": "*",
41+
"@aws-sdk/types": "*",
4042
"@tsconfig/recommended": "1.0.1",
4143
"@types/node": "^14.11.2",
4244
"concurrently": "7.0.0",

lib/lib-storage/src/Upload.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import {
1313
Tag,
1414
UploadPartCommand,
1515
} from "@aws-sdk/client-s3";
16+
import { EndpointResolvedConfig, getEndpointFromInstructions, toEndpointV1 } from "@aws-sdk/middleware-endpoint";
1617
import { HttpRequest } from "@aws-sdk/protocol-http";
1718
import { extendedEncodeURIComponent } from "@aws-sdk/smithy-client";
19+
import { Endpoint } from "@aws-sdk/types";
1820
import { EventEmitter } from "events";
1921

2022
import { byteLength } from "./bytelength";
@@ -101,7 +103,8 @@ export class Upload extends EventEmitter {
101103
this.isMultiPart = false;
102104
const params = { ...this.params, Body: dataPart.data };
103105

104-
const requestHandler = this.client.config.requestHandler;
106+
const clientConfig = this.client.config;
107+
const requestHandler = clientConfig.requestHandler;
105108
const eventEmitter: EventEmitter | null = requestHandler instanceof EventEmitter ? requestHandler : null;
106109
const uploadEventListener = (event: ProgressEvent) => {
107110
this.bytesUploadedSoFar = event.loaded;
@@ -120,14 +123,22 @@ export class Upload extends EventEmitter {
120123
eventEmitter.on("xhr.upload.progress", uploadEventListener);
121124
}
122125

123-
const [putResult, endpoint] = await Promise.all([
124-
this.client.send(new PutObjectCommand(params)),
125-
this.client.config?.endpoint?.(),
126-
]);
126+
const resolved = await Promise.all([this.client.send(new PutObjectCommand(params)), clientConfig?.endpoint?.()]);
127+
const putResult = resolved[0];
128+
let endpoint: Endpoint = resolved[1];
127129

128130
if (!endpoint) {
129-
// TODO(endpointsv2): handle endpoint v2
130-
throw new Error('Could not resolve endpoint from S3 "client.config.endpoint()".');
131+
endpoint = toEndpointV1(
132+
await getEndpointFromInstructions(
133+
params,
134+
PutObjectCommand,
135+
clientConfig
136+
)
137+
);
138+
}
139+
140+
if (!endpoint) {
141+
throw new Error('Could not resolve endpoint from S3 "client.config.endpoint()" nor EndpointsV2.');
131142
}
132143

133144
if (eventEmitter !== null) {

packages/middleware-endpoint/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'",
66
"build:cjs": "tsc -p tsconfig.cjs.json",
77
"build:es": "tsc -p tsconfig.es.json",
8+
"build:include:deps": "lerna run --scope $npm_package_name --include-dependencies build",
89
"build:types": "tsc -p tsconfig.types.json",
910
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
1011
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
@@ -19,6 +20,7 @@
1920
},
2021
"license": "Apache-2.0",
2122
"dependencies": {
23+
"@aws-sdk/protocol-http": "*",
2224
"@aws-sdk/signature-v4": "*",
2325
"@aws-sdk/types": "*",
2426
"@aws-sdk/util-config-provider": "*",
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { EndpointParameters, EndpointV2, HandlerExecutionContext } from "@aws-sdk/types";
2+
3+
import { EndpointResolvedConfig } from "../resolveEndpointConfig";
4+
import { EndpointParameterInstructions } from "../types";
5+
6+
/**
7+
*
8+
* This step in the endpoint resolution process is exposed as a function
9+
* to allow certain packages such as s3 signer, lib-upload to get
10+
* the V2 Endpoint associated to an instance of some (operation) command.
11+
*
12+
* @private
13+
*
14+
*/
15+
export const getEndpointFromInstructions = async (
16+
commandInput: any,
17+
instructionsSupplier: Partial<{
18+
instructions(): EndpointParameterInstructions
19+
}>,
20+
clientConfig: Partial<EndpointResolvedConfig>,
21+
context?: HandlerExecutionContext
22+
): Promise<EndpointV2> => {
23+
const endpointParams: EndpointParameters = {};
24+
const instructions: EndpointParameterInstructions = instructionsSupplier?.instructions() || {};
25+
26+
if (typeof clientConfig.endpointProvider !== "function") {
27+
throw new Error("config.endpointProvider is not set.");
28+
}
29+
30+
for (const [name, instruction] of Object.entries(instructions)) {
31+
switch (instruction.type) {
32+
case "staticContextParams":
33+
endpointParams[name] = instruction.value;
34+
break;
35+
case "contextParams":
36+
endpointParams[name] = commandInput[instruction.name];
37+
break;
38+
case "clientContextParams":
39+
case "builtInParams":
40+
endpointParams[name] = await createConfigProvider(instruction.name, clientConfig)();
41+
break;
42+
default:
43+
throw new Error("Unrecognized endpoint parameter instruction: " + JSON.stringify(instruction));
44+
}
45+
}
46+
47+
const endpoint: EndpointV2 = clientConfig.endpointProvider!(endpointParams, context);
48+
return endpoint;
49+
};
50+
51+
/**
52+
* @private
53+
*/
54+
const createConfigProvider = (configKey: string, config: EndpointResolvedConfig & any) => {
55+
return async function configProvider() {
56+
if (!(configKey in config)) {
57+
throw new Error(`The config key ${configKey} was not found in the config object.`);
58+
}
59+
const configValue = config[configKey];
60+
if (typeof configValue === "function") {
61+
return configValue();
62+
}
63+
return configValue;
64+
};
65+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Endpoint, EndpointV2 } from "@aws-sdk/types";
2+
import { parseUrl } from "@aws-sdk/url-parser";
3+
4+
export const toEndpointV1 = (endpoint: string | Endpoint | EndpointV2): Endpoint => {
5+
if (typeof endpoint === "object") {
6+
if ("url" in endpoint) {
7+
// v2
8+
return parseUrl(endpoint.url);
9+
}
10+
// v1
11+
return endpoint;
12+
}
13+
return parseUrl(endpoint);
14+
};

packages/middleware-endpoint/src/endpointMiddleware.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,54 @@
1+
import { HttpRequest } from "@aws-sdk/protocol-http";
2+
import { parseQueryString } from "@aws-sdk/querystring-parser";
13
import {
4+
EndpointV2,
5+
HandlerExecutionContext,
26
MetadataBearer,
37
SerializeHandler,
48
SerializeHandlerArguments,
59
SerializeHandlerOutput,
610
SerializeMiddleware,
711
} from "@aws-sdk/types";
812

9-
import { EndpointParameterInstruction } from "./types";
13+
import { getEndpointFromInstructions } from "./adaptors/getEndpointFromConfig";
14+
import { EndpointResolvedConfig } from "./resolveEndpointConfig";
15+
import { EndpointParameterInstructions } from "./types";
1016

11-
export const endpointMiddleware = (options: {
12-
config: any; // TODO(endpointsV2): should be ResolvedEndpointConfig interface
13-
instruction: EndpointParameterInstruction;
17+
/**
18+
* @private
19+
*/
20+
export const endpointMiddleware = ({
21+
config,
22+
instructions,
23+
}: {
24+
config: EndpointResolvedConfig;
25+
instructions: EndpointParameterInstructions;
1426
}): SerializeMiddleware<any, any> => {
15-
return <Output extends MetadataBearer>(next: SerializeHandler<any, Output>): SerializeHandler<any, Output> =>
27+
return <Output extends MetadataBearer>(
28+
next: SerializeHandler<any, Output>,
29+
context: HandlerExecutionContext
30+
): SerializeHandler<any, Output> =>
1631
async (args: SerializeHandlerArguments<any>): Promise<SerializeHandlerOutput<Output>> => {
17-
// TODO(endpointsV2): resolve async providers from client config and static values according to
18-
// instruction from input to populate endpoint parameters
32+
if (HttpRequest.isInstance(args.request)) {
33+
const { request } = args;
34+
35+
const endpoint: EndpointV2 = await getEndpointFromInstructions(args.input, instructions, config, context);
36+
37+
context.endpointV2 = endpoint;
38+
context.authSchemes = endpoint.properties?.authSchemes;
39+
40+
request.headers = Object.entries(endpoint.headers || {}).reduce((headers, [name, values]) => {
41+
headers[name] = values.join(",");
42+
return headers;
43+
}, {} as Record<string, string>);
44+
request.hostname = endpoint.url.hostname;
45+
request.path = endpoint.url.pathname;
46+
request.port = parseInt(endpoint.url.port);
47+
request.protocol = endpoint.url.protocol;
48+
request.query = parseQueryString(endpoint.url.search);
49+
}
1950
return next({
2051
...args,
2152
});
2253
};
2354
};
24-

packages/middleware-endpoint/src/getEndpointPlugin.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
import { Pluggable, SerializeHandlerOptions } from "@aws-sdk/types";
22

33
import { endpointMiddleware } from "./endpointMiddleware";
4-
import { EndpointParameterInstruction } from "./types";
4+
import { EndpointResolvedConfig } from "./resolveEndpointConfig";
5+
import { EndpointParameterInstructions } from "./types";
56

67
export const endpointMiddlewareOptions: SerializeHandlerOptions = {
78
step: "serialize",
89
tags: ["ENDPOINT_PARAMETERS", "ENDPOINT_V2", "ENDPOINT"],
9-
name: "endpointMiddleware",
10+
name: "endpointsV2Middleware",
1011
override: true,
1112
};
1213

1314
export const getEndpointPlugin = (
14-
config: any, //TODO(endpointsV2): should be ResolvedEndpointConfig interface
15-
instruction: EndpointParameterInstruction
15+
config: EndpointResolvedConfig,
16+
instructions: EndpointParameterInstructions
1617
): Pluggable<any, any> => ({
1718
applyToStack: (clientStack) => {
1819
clientStack.add(
1920
endpointMiddleware({
2021
config,
21-
instruction,
22+
instructions,
2223
}),
2324
endpointMiddlewareOptions
2425
);

packages/middleware-endpoint/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from "./adaptors/getEndpointFromConfig";
2+
export * from "./adaptors/toEndpointV1";
13
export * from "./endpointMiddleware";
24
export * from "./getEndpointPlugin";
35
export * from "./resolveEndpointConfig";

packages/middleware-endpoint/src/resolveEndpointConfig.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Endpoint, EndpointParameters, EndpointV2, Logger, Provider, UrlParser } from "@aws-sdk/types";
22
import { normalizeProvider } from "@aws-sdk/util-middleware";
33

4+
import { toEndpointV1 } from "./adaptors/toEndpointV1";
5+
46
/**
57
* Endpoint config interfaces and resolver for Endpoints v2. They live in separate package to allow per-service onboarding.
68
* When all services onboard the Endpoints v2, the resolver in config-resolver package can be removed.
@@ -102,5 +104,3 @@ export const resolveEndpointConfig = <T, P extends EndpointParameters = Endpoint
102104
useFipsEndpoint: normalizeProvider(input.useFipsEndpoint ?? false),
103105
};
104106
};
105-
106-
declare const toEndpointV1: (endpoint: string | Endpoint | EndpointV2) => Endpoint; // TODO(endpointsV2) implementation

packages/middleware-endpoint/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export interface EndpointParameterInstruction {
1+
export interface EndpointParameterInstructions {
22
[name: string]:
33
| BuiltInParamInstruction
44
| ClientContextParamInstruction

packages/middleware-signing/src/configuration.spec.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ describe("AuthConfig", () => {
2222
jest.clearAllMocks();
2323
});
2424

25-
// TODO(endpointsv2)
26-
it.skip("should memoize custom credential provider", async () => {
25+
it("should memoize custom credential provider", async () => {
2726
const { signer: signerProvider } = resolveAwsAuthConfig(inputParams);
2827
const signer = await signerProvider({ name: "sigv4", properties: {} });
2928
const request = new HttpRequest({});
@@ -34,8 +33,7 @@ describe("AuthConfig", () => {
3433
expect(inputParams.credentials).toBeCalledTimes(1);
3534
});
3635

37-
// TODO(endpointsv2)
38-
it.skip("should refresh custom credential provider if expired", async () => {
36+
it("should refresh custom credential provider if expired", async () => {
3937
const FOUR_MINUTES_AND_59_SEC = 299 * 1000;
4038
const input = {
4139
...inputParams,
@@ -75,8 +73,7 @@ describe("AuthConfig", () => {
7573
jest.clearAllMocks();
7674
});
7775

78-
// TODO(endpointsv2)
79-
it.skip("should memoize custom credential provider", async () => {
76+
it("should memoize custom credential provider", async () => {
8077
const { signer: signerProvider } = resolveSigV4AuthConfig(inputParams);
8178
const signer = await signerProvider({ name: "sigv4", properties: {} });
8279
const request = new HttpRequest({});
@@ -87,8 +84,7 @@ describe("AuthConfig", () => {
8784
expect(inputParams.credentials).toBeCalledTimes(1);
8885
});
8986

90-
// TODO(endpointsv2)
91-
it.skip("should refresh custom credential provider if expired", async () => {
87+
it("should refresh custom credential provider if expired", async () => {
9288
const FOUR_MINUTES_AND_59_SEC = 299 * 1000;
9389
const input = {
9490
...inputParams,

0 commit comments

Comments
 (0)