Skip to content

Commit

Permalink
refactor(codecatalyst): migrate client to sdkv3 (#6734)
Browse files Browse the repository at this point in the history
## 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]>
  • Loading branch information
Hweinstock and aws-toolkit-automation authored Mar 11, 2025
1 parent 890bfe8 commit f4f9354
Show file tree
Hide file tree
Showing 12 changed files with 7,113 additions and 2,440 deletions.
8,828 changes: 6,709 additions & 2,119 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@
"@aws-sdk/protocol-http": "<3.696.0",
"@aws-sdk/smithy-client": "<3.696.0",
"@aws-sdk/util-arn-parser": "<3.696.0",
"@aws-sdk/client-codecatalyst": "<3.696.0",
"@aws-sdk/s3-request-presigner": "<3.696.0",
"@aws/mynah-ui": "^4.23.1",
"@gerhobbelt/gitignore-parser": "^0.2.0-9",
Expand Down
57 changes: 0 additions & 57 deletions packages/core/src/auth/sso/sdkV2Compat.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/core/src/codecatalyst/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ import { ToolkitError, errorCode } from '../shared/errors'
import { telemetry } from '../shared/telemetry/telemetry'
import { showConfirmationMessage } from '../shared/utilities/messages'
import { AccountStatus } from '../shared/telemetry/telemetryClient'
import { CreateDevEnvironmentRequest, UpdateDevEnvironmentRequest } from 'aws-sdk/clients/codecatalyst'
import { SsoConnection } from '../auth/connection'
import { isInDevEnv, isRemoteWorkspace } from '../shared/vscode/env'
import { commandPalette } from '../codewhisperer/commands/types'
import { CreateDevEnvironmentRequest, UpdateDevEnvironmentRequest } from '@aws-sdk/client-codecatalyst'

/** "List CodeCatalyst Commands" command. */
export async function listCommands(): Promise<void> {
Expand Down
36 changes: 24 additions & 12 deletions packages/core/src/shared/awsClientBuilderV3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@

import { CredentialsShim } from '../auth/deprecated/loginManager'
import { AwsContext } from './awsContext'
import { AwsCredentialIdentityProvider, Logger, RetryStrategyV2 } from '@smithy/types'
import {
AwsCredentialIdentityProvider,
Logger,
RetryStrategyV2,
TokenIdentity,
TokenIdentityProvider,
} from '@smithy/types'
import { getUserAgent } from './telemetry/util'
import { DevSettings } from './settings'
import {
Expand Down Expand Up @@ -38,7 +44,7 @@ import { once } from './utilities/functionUtils'
import { isWeb } from './extensionGlobals'

export type AwsClientConstructor<C> = new (o: AwsClientOptions) => C
export type AwsCommandConstructor<CommandInput extends object, Command extends AwsCommand> = new (
export type AwsCommandConstructor<CommandInput extends object, Command extends AwsCommand<CommandInput, object>> = new (
o: CommandInput
) => Command

Expand All @@ -48,12 +54,15 @@ export interface AwsClient {
middlewareStack: {
add: MiddlewareStack<any, MetadataBearer>['add']
}
send: (command: AwsCommand, options?: any) => Promise<any>
send<InputType extends object, OutputType extends object>(
command: AwsCommand<InputType, OutputType>,
options?: any
): Promise<OutputType>
destroy: () => void
}

export interface AwsCommand {
input: object
export interface AwsCommand<InputType extends object, OutputType extends object> {
input: InputType
middlewareStack: any
resolveMiddleware: (stack: any, configuration: any, options: any) => Handler<any, any>
}
Expand All @@ -71,6 +80,7 @@ export interface AwsClientOptions {
endpoint: string
retryStrategy: RetryStrategy | RetryStrategyV2
logger: Logger
token: TokenIdentity | TokenIdentityProvider
}

interface AwsServiceOptions<C extends AwsClient> {
Expand Down Expand Up @@ -132,7 +142,6 @@ export class AWSClientBuilderV3 {
}

public createAwsService<C extends AwsClient>(serviceOptions: AwsServiceOptions<C>): C {
const shim = this.getShim()
const opt = (serviceOptions.clientOptions ?? {}) as AwsClientOptions
const userAgent = serviceOptions.userAgent ?? true
const keepAlive = serviceOptions.keepAlive ?? true
Expand All @@ -153,13 +162,16 @@ export class AWSClientBuilderV3 {
if (!opt.requestHandler) {
opt.requestHandler = this.getHttpHandler()
}
// TODO: add tests for refresh logic.
opt.credentials = async () => {
const creds = await shim.get()
if (creds.expiration && creds.expiration.getTime() < Date.now()) {
return shim.refresh()

if (!opt.credentials && !opt.token) {
const shim = this.getShim()
opt.credentials = async () => {
const creds = await shim.get()
if (creds.expiration && creds.expiration.getTime() < Date.now()) {
return shim.refresh()
}
return creds
}
return creds
}

const service = new serviceOptions.serviceClient(opt)
Expand Down
15 changes: 7 additions & 8 deletions packages/core/src/shared/clients/clientWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import globals from '../extensionGlobals'
import { AwsClient, AwsClientConstructor, AwsCommand, AwsCommandConstructor } from '../awsClientBuilderV3'
import { PaginationConfiguration, Paginator } from '@aws-sdk/types'
import { AsyncCollection, toCollection } from '../utilities/asyncCollection'
import { isDefined } from '../utilities/tsUtils'

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

protected async makeRequest<CommandInput extends object, CommandOutput extends object, Command extends AwsCommand>(
command: AwsCommandConstructor<CommandInput, Command>,
commandOptions: CommandInput
): Promise<CommandOutput> {
protected async makeRequest<
CommandInput extends object,
CommandOutput extends object,
CommandOptions extends CommandInput,
Command extends AwsCommand<CommandInput, CommandOutput>,
>(command: AwsCommandConstructor<CommandInput, Command>, commandOptions: CommandOptions): Promise<CommandOutput> {
return await this.getClient().send(new command(commandOptions))
}

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

return collection

function isDefined<T>(i: T | undefined): i is T {
return i !== undefined
}
}

protected async getFirst<CommandInput extends object, CommandOutput extends object, Output extends object>(
Expand Down
Loading

0 comments on commit f4f9354

Please sign in to comment.