Skip to content

Conversation

@liramon1
Copy link

@liramon1 liramon1 commented Jul 23, 2025

Problem

The agentic bundle for the CodeWhisperer LSP only supports SSO. If language clients want it to consume IAM credentials, they must start a completely separate bundle which includes the IAM implementation of the CodeWhisperer LSP. This prevents clients from seamlessly switching authentication methods at runtime.

Solution

This is part of #1981.

This PR adds abstract token methods to CodeWhispererServiceBase and StreamingClientServiceBase, so that they can be used polymorphically inside of AmazonQServiceManager.

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@liramon1 liramon1 requested a review from a team as a code owner July 23, 2025 14:17
@codecov-commenter
Copy link

codecov-commenter commented Jul 23, 2025

Codecov Report

Attention: Patch coverage is 54.27718% with 1224 lines in your changes missing coverage. Please review.

Project coverage is 61.17%. Comparing base (1491f4f) to head (dbad5aa).

Files with missing lines Patch % Lines
.../language-server/agenticChat/tools/mcp/mcpUtils.ts 39.81% 328 Missing ⚠️
...anguage-server/agenticChat/tools/mcp/mcpManager.ts 62.84% 149 Missing ⚠️
...ge-server/agenticChat/tools/mcp/mcpEventHandler.ts 18.46% 106 Missing ⚠️
...ed/codeWhispererService/codeWhispererServiceIAM.ts 43.12% 91 Missing ⚠️
chat-client/src/client/mynahUi.ts 30.15% 86 Missing and 2 partials ⚠️
.../language-server/agenticChat/tools/mcp/mcpTypes.ts 22.50% 62 Missing ⚠️
...orkspaceContext/dependency/dependencyDiscoverer.ts 2.43% 40 Missing ⚠️
server/aws-lsp-codewhisperer/src/shared/utils.ts 9.09% 40 Missing ⚠️
...anguage-server/workspaceContext/artifactManager.ts 11.62% 38 Missing ⚠️
...nguage-server/agenticChat/agenticChatController.ts 71.05% 33 Missing ⚠️
... and 24 more
Additional details and impacted files
@@                    Coverage Diff                     @@
##           feature/flare-iam-base    #1958      +/-   ##
==========================================================
- Coverage                   65.96%   61.17%   -4.79%     
==========================================================
  Files                         238      240       +2     
  Lines                       50623    52174    +1551     
  Branches                     3385     3112     -273     
==========================================================
- Hits                        33393    31920    -1473     
- Misses                      17170    20198    +3028     
+ Partials                       60       56       -4     
Flag Coverage Δ
unittests 61.17% <54.27%> (-4.79%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

| 'onOpenFileDialog'
| 'onListAvailableModels'
| 'sendSubscriptionDetails'
| 'onSubscriptionUpgrade'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this diff should not be within the scope of this pr

this.client = this.createAppropriateClient(credentialsProvider, options, sdkInitializator, logging)
}

private CreateCodeWhispererConfigurationOptions(): CodeWhispererConfigurationOptions {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create -> create

if (!creds?.token) {
throw new Error('Authorization failed, bearer token is not set')
const options = this.CreateCodeWhispererConfigurationOptions()
this.client = this.createAppropriateClient(credentialsProvider, options, sdkInitializator, logging)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createAppropriateClient -> createCodeWhispererServiceClient

nextToken: response.nextToken,
}
return this.mapCodeWhispererApiResponseToSuggestion(response, responseContext)
} else if (this.getCredentialsType() === 'iam') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use polymorphism to get rid of all the new if IAM then else then conditions?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be difficult, since CodeWhispererTokenClient and CodeWhispererSigv4Client are generated from service JSON files. Do you know if they can be changed safely?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this is a hard blocker for this PR. You need to get ride of those if else.

CodeWhispererTokenClient and CodeWhispererSigv4Client are generated from service JSON files --> You can build a type casting mechanism. We have implemented this before, see https://github.com/aws/aws-toolkit-vscode/blob/amazonq/v1.74.0/packages/core/src/codewhisperer/client/codewhisperer.ts

export class CodeWhispererServiceToken extends CodeWhispererServiceBase {
client: CodeWhispererTokenClient
export class CodeWhispererService extends CodeWhispererServiceBase {
client: CodeWhispererClient
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To use polymorphism and eliminate all the if (this.getCredentialsType() === 'iam') { ... } else { ... } checks in your CodeWhispererService class, you need to refactor your code to leverage subclassing and method overriding. This means:

Define an abstract base class (CodeWhispererServiceBase) that has all the method signatures you need.
Implement one subclass for the IAM client (CodeWhispererServiceIAM) and one for the Bearer/Token client (CodeWhispererServiceToken).
Each subclass provides its own implementation for the relevant methods, so you never need to check "what type am I?" at runtime—the correct implementation is invoked automatically.
Example Refactor

  1. Abstract Base Class
    ts
    abstract class CodeWhispererServiceBase {
    abstract generateSuggestions(request: SomeRequest): Promise;
    abstract codeModernizerCreateUploadUrl(request: ...): Promise<...>;
    // ... all other methods
    }
  2. IAM Implementation
    ts
    class CodeWhispererServiceIAM extends CodeWhispererServiceBase {
    generateSuggestions(request: SomeRequest): Promise {
    // IAM-specific implementation
    return this.client.generateRecommendations(request).promise();
    }
    codeModernizerCreateUploadUrl(request: ...): Promise<...> {
    throw new Error('Not supported for IAM');
    }
    // ... other IAM-specific overrides
    }
  3. Token Implementation
    ts
    class CodeWhispererServiceToken extends CodeWhispererServiceBase {
    generateSuggestions(request: SomeRequest): Promise {
    // Token-specific implementation
    return this.client.generateCompletions(request).promise();
    }
    codeModernizerCreateUploadUrl(request: ...): Promise<...> {
    return this.client.createUploadUrl(request).promise();
    }
    // ... other Token-specific overrides
    }
  4. Construction
    When you create an instance, you decide which subclass to instantiate based on your credentials:

ts
let service: CodeWhispererServiceBase;
if (hasIAMCreds) {
service = new CodeWhispererServiceIAM(...);
} else {
service = new CodeWhispererServiceToken(...);
}
From here on, your code can just call:

ts
await service.generateSuggestions(request);
await service.codeModernizerCreateUploadUrl(request);
5. Advantages
No if/else or switch needed in method bodies.
Each credential type’s logic is isolated and clear.
Adding new credential types or behaviors is easy—just add a new subclass.
Summary:
Polymorphism works when you let the object type pick the correct method at runtime, rather than checking the type with if/else. In your case, this means two (or more) subclasses, each handling their credential-specific behavior.

Would you like a more concrete refactor using your current code as a starting point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

await service.generateSuggestions(request); here request is a union type.

Copy link
Contributor

@leigaol leigaol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the union type to convert the bearer token types into IAM JSON generated types to avoid extra if else.

@liramon1 liramon1 changed the title refactor(amazonq): combine IAM and SSO implementations of CodeWhispererService refactor(amazonq): add token methods to CodeWhispererServiceBase Jul 24, 2025
@liramon1 liramon1 changed the title refactor(amazonq): add token methods to CodeWhispererServiceBase feat(amazonq): add token methods to CodeWhispererServiceBase Jul 24, 2025
@liramon1 liramon1 changed the base branch from feature/flare-iam-base to liramon/flare-iam-base July 24, 2025 22:46
@liramon1 liramon1 changed the title feat(amazonq): add token methods to CodeWhispererServiceBase feat(amazonq): add token methods to codewhisperer clients Jul 25, 2025
@liramon1 liramon1 force-pushed the liramon/flare-iam-base branch 2 times, most recently from 54c7086 to b35472f Compare July 25, 2025 17:39
@liramon1 liramon1 force-pushed the liramon/flare-iam-base branch from b35472f to 4f5fc2d Compare July 25, 2025 18:01
@liramon1 liramon1 force-pushed the liramon/flare-iam-base branch from 4f5fc2d to d093e42 Compare July 25, 2025 18:10
@liramon1 liramon1 requested a review from leigaol July 25, 2025 18:35
}

public async codeModernizerCreateUploadUrl(
request: CodeWhispererTokenClient.CreateUploadUrlRequest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this IAM client should not take the request input of type: CodeWhispererTokenClient.CreateUploadUrlRequest

@liramon1 liramon1 force-pushed the liramon/flare-iam-base branch 3 times, most recently from 82eeaf9 to e96e1c1 Compare July 28, 2025 20:07
@liramon1 liramon1 merged commit 060a34f into aws:liramon/flare-iam-base Jul 29, 2025
liramon1 added a commit that referenced this pull request Jul 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants