Skip to content

Commit f4f9354

Browse files
refactor(codecatalyst): migrate client to sdkv3 (#6734)
## Problem codecatalyst is still running on v2. Migrate to v3. ## Solution - refactor `call` wrapper to work with commands, rather than requests. - Support passing `token` into V3 clients. - Remove retry logic in tests, since V3 client builder adds this by default. - `correctClockskew` is enabled by default so it can be dropped: https://github.com/aws/aws-sdk-js-v3/blob/eae65cded5e703306346bdbd1b5de9d23050054a/UPGRADING.md - In SDKv3, all properties in the response are optional. However, in SDKv2 these were required. Therefore, we build a type where these properties are required to avoid `undefined` checks in many places. This involves wrapping some type guards due the problem discussed [here](microsoft/TypeScript#9998), where type guards can't propagate up function scope. - In SDKv3, errors are shaped differently (see: aws/aws-sdk-js-v3#759). We rely on the shape to detect `AccessDeniedException` in CC, so this logic has to change. - The token provider used here: https://github.com/aws/aws-toolkit-vscode/blob/master/packages/core/src/auth/sso/sdkV2Compat.ts#L33, does not work with V3, so we need to create one that does. This also means we can delete the old one, since this is the only place it is used. ## Verification - CodeCatalyst has some E2E tests. In addition to ensuring these pass, the following flows were manually tested: - connect to existing dev project and edit files, add files, and remove files. - clone an existing CC repo into a local folder. - create new dev environment and open it, make some edits, close it. - Try all the `open` commands that deep link to console. - List environments, repositories, and projects via QuickPicks. - Install extension on remote and verify CodeCatalyst panel is there and works. - Able to list projects, repos, and envs in CodeCatalyst env, but not able to connect (which is intended). - left the window open for 10 minutes then saw a warning. - Stop dev environment from within environment. ## Future Work - Look into adding the performance logging in the original `call` function to the default middleware stack for v3 clients. - Add automatic mapping from `name` (or other) fields on service errors to make up for missing `code` property on errors in v3. --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: aws-toolkit-automation <[email protected]>
1 parent 890bfe8 commit f4f9354

File tree

12 files changed

+7113
-2440
lines changed

12 files changed

+7113
-2440
lines changed

package-lock.json

+6,709-2,119
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@
516516
"@aws-sdk/protocol-http": "<3.696.0",
517517
"@aws-sdk/smithy-client": "<3.696.0",
518518
"@aws-sdk/util-arn-parser": "<3.696.0",
519+
"@aws-sdk/client-codecatalyst": "<3.696.0",
519520
"@aws-sdk/s3-request-presigner": "<3.696.0",
520521
"@aws/mynah-ui": "^4.23.1",
521522
"@gerhobbelt/gitignore-parser": "^0.2.0-9",

packages/core/src/auth/sso/sdkV2Compat.ts

-57
This file was deleted.

packages/core/src/codecatalyst/commands.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import { ToolkitError, errorCode } from '../shared/errors'
2222
import { telemetry } from '../shared/telemetry/telemetry'
2323
import { showConfirmationMessage } from '../shared/utilities/messages'
2424
import { AccountStatus } from '../shared/telemetry/telemetryClient'
25-
import { CreateDevEnvironmentRequest, UpdateDevEnvironmentRequest } from 'aws-sdk/clients/codecatalyst'
2625
import { SsoConnection } from '../auth/connection'
2726
import { isInDevEnv, isRemoteWorkspace } from '../shared/vscode/env'
2827
import { commandPalette } from '../codewhisperer/commands/types'
28+
import { CreateDevEnvironmentRequest, UpdateDevEnvironmentRequest } from '@aws-sdk/client-codecatalyst'
2929

3030
/** "List CodeCatalyst Commands" command. */
3131
export async function listCommands(): Promise<void> {

packages/core/src/shared/awsClientBuilderV3.ts

+24-12
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55

66
import { CredentialsShim } from '../auth/deprecated/loginManager'
77
import { AwsContext } from './awsContext'
8-
import { AwsCredentialIdentityProvider, Logger, RetryStrategyV2 } from '@smithy/types'
8+
import {
9+
AwsCredentialIdentityProvider,
10+
Logger,
11+
RetryStrategyV2,
12+
TokenIdentity,
13+
TokenIdentityProvider,
14+
} from '@smithy/types'
915
import { getUserAgent } from './telemetry/util'
1016
import { DevSettings } from './settings'
1117
import {
@@ -38,7 +44,7 @@ import { once } from './utilities/functionUtils'
3844
import { isWeb } from './extensionGlobals'
3945

4046
export type AwsClientConstructor<C> = new (o: AwsClientOptions) => C
41-
export type AwsCommandConstructor<CommandInput extends object, Command extends AwsCommand> = new (
47+
export type AwsCommandConstructor<CommandInput extends object, Command extends AwsCommand<CommandInput, object>> = new (
4248
o: CommandInput
4349
) => Command
4450

@@ -48,12 +54,15 @@ export interface AwsClient {
4854
middlewareStack: {
4955
add: MiddlewareStack<any, MetadataBearer>['add']
5056
}
51-
send: (command: AwsCommand, options?: any) => Promise<any>
57+
send<InputType extends object, OutputType extends object>(
58+
command: AwsCommand<InputType, OutputType>,
59+
options?: any
60+
): Promise<OutputType>
5261
destroy: () => void
5362
}
5463

55-
export interface AwsCommand {
56-
input: object
64+
export interface AwsCommand<InputType extends object, OutputType extends object> {
65+
input: InputType
5766
middlewareStack: any
5867
resolveMiddleware: (stack: any, configuration: any, options: any) => Handler<any, any>
5968
}
@@ -71,6 +80,7 @@ export interface AwsClientOptions {
7180
endpoint: string
7281
retryStrategy: RetryStrategy | RetryStrategyV2
7382
logger: Logger
83+
token: TokenIdentity | TokenIdentityProvider
7484
}
7585

7686
interface AwsServiceOptions<C extends AwsClient> {
@@ -132,7 +142,6 @@ export class AWSClientBuilderV3 {
132142
}
133143

134144
public createAwsService<C extends AwsClient>(serviceOptions: AwsServiceOptions<C>): C {
135-
const shim = this.getShim()
136145
const opt = (serviceOptions.clientOptions ?? {}) as AwsClientOptions
137146
const userAgent = serviceOptions.userAgent ?? true
138147
const keepAlive = serviceOptions.keepAlive ?? true
@@ -153,13 +162,16 @@ export class AWSClientBuilderV3 {
153162
if (!opt.requestHandler) {
154163
opt.requestHandler = this.getHttpHandler()
155164
}
156-
// TODO: add tests for refresh logic.
157-
opt.credentials = async () => {
158-
const creds = await shim.get()
159-
if (creds.expiration && creds.expiration.getTime() < Date.now()) {
160-
return shim.refresh()
165+
166+
if (!opt.credentials && !opt.token) {
167+
const shim = this.getShim()
168+
opt.credentials = async () => {
169+
const creds = await shim.get()
170+
if (creds.expiration && creds.expiration.getTime() < Date.now()) {
171+
return shim.refresh()
172+
}
173+
return creds
161174
}
162-
return creds
163175
}
164176

165177
const service = new serviceOptions.serviceClient(opt)

packages/core/src/shared/clients/clientWrapper.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import globals from '../extensionGlobals'
77
import { AwsClient, AwsClientConstructor, AwsCommand, AwsCommandConstructor } from '../awsClientBuilderV3'
88
import { PaginationConfiguration, Paginator } from '@aws-sdk/types'
99
import { AsyncCollection, toCollection } from '../utilities/asyncCollection'
10+
import { isDefined } from '../utilities/tsUtils'
1011

1112
type SDKPaginator<C, CommandInput extends object, CommandOutput extends object> = (
1213
config: Omit<PaginationConfiguration, 'client'> & { client: C },
@@ -28,10 +29,12 @@ export abstract class ClientWrapper<C extends AwsClient> implements vscode.Dispo
2829
: globals.sdkClientBuilderV3.getAwsService(args)
2930
}
3031

31-
protected async makeRequest<CommandInput extends object, CommandOutput extends object, Command extends AwsCommand>(
32-
command: AwsCommandConstructor<CommandInput, Command>,
33-
commandOptions: CommandInput
34-
): Promise<CommandOutput> {
32+
protected async makeRequest<
33+
CommandInput extends object,
34+
CommandOutput extends object,
35+
CommandOptions extends CommandInput,
36+
Command extends AwsCommand<CommandInput, CommandOutput>,
37+
>(command: AwsCommandConstructor<CommandInput, Command>, commandOptions: CommandOptions): Promise<CommandOutput> {
3538
return await this.getClient().send(new command(commandOptions))
3639
}
3740

@@ -47,10 +50,6 @@ export abstract class ClientWrapper<C extends AwsClient> implements vscode.Dispo
4750
.map((o) => o.filter(isDefined))
4851

4952
return collection
50-
51-
function isDefined<T>(i: T | undefined): i is T {
52-
return i !== undefined
53-
}
5453
}
5554

5655
protected async getFirst<CommandInput extends object, CommandOutput extends object, Output extends object>(

0 commit comments

Comments
 (0)