Skip to content

Commit f79defd

Browse files
authored
Merge pull request #7293 from nkomonen-amazon/profiles
fix(amazonq): Developer Profiles not being sent to Flare Auth as expected
2 parents 7fc386e + d46ad37 commit f79defd

File tree

6 files changed

+61
-18
lines changed

6 files changed

+61
-18
lines changed

packages/amazonq/src/lsp/activation.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ import vscode from 'vscode'
77
import { startLanguageServer } from './client'
88
import { AmazonQLspInstaller } from './lspInstaller'
99
import { lspSetupStage, ToolkitError, messages } from 'aws-core-vscode/shared'
10-
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
1110

1211
export async function activate(ctx: vscode.ExtensionContext) {
1312
try {
1413
await lspSetupStage('all', async () => {
1514
const installResult = await new AmazonQLspInstaller().resolve()
1615
return await lspSetupStage('launch', () => startLanguageServer(ctx, installResult.resourcePaths))
1716
})
18-
await AuthUtil.instance.restore()
1917
} catch (err) {
2018
const e = err as ToolkitError
2119
void messages.showViewLogsMessage(`Failed to launch Amazon Q language server: ${e.message}`)

packages/amazonq/src/lsp/client.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,23 @@ export async function startLanguageServer(
175175
toDispose.push(disposable)
176176

177177
await client.onReady()
178+
179+
// IMPORTANT: This sets up Auth and must be called before anything attempts to use it
178180
AuthUtil.create(new auth2.LanguageClientAuth(client, clientId, encryptionKey))
181+
// Ideally this would be part of AuthUtil.create() as it restores the existing Auth connection, but we have some
182+
// future work which will need to delay this call
183+
await AuthUtil.instance.restore()
184+
185+
await postStartLanguageServer(client, resourcePaths, toDispose)
179186

187+
return client
188+
}
189+
190+
async function postStartLanguageServer(
191+
client: LanguageClient,
192+
resourcePaths: AmazonQResourcePaths,
193+
toDispose: vscode.Disposable[]
194+
) {
180195
// Request handler for when the server wants to know about the clients auth connnection. Must be registered before the initial auth init call
181196
client.onRequest<ConnectionMetadata, Error>(auth2.notificationTypes.getConnectionMetadata.method, () => {
182197
return {
@@ -347,8 +362,6 @@ export async function startLanguageServer(
347362
// Set this inside onReady so that it only triggers on subsequent language server starts (not the first)
348363
onServerRestartHandler(client)
349364
)
350-
351-
return client
352365
}
353366

354367
/**

packages/core/src/auth/auth2.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,14 @@ export class SsoLogin implements BaseLogin {
325325
}
326326

327327
private updateConnectionState(state: AuthState) {
328-
if (this.connectionState !== state) {
329-
this.eventEmitter.fire({ id: this.profileName, state })
328+
const oldState = this.connectionState
329+
const newState = state
330+
331+
this.connectionState = newState
332+
333+
if (oldState !== newState) {
334+
this.eventEmitter.fire({ id: this.profileName, state: this.connectionState })
330335
}
331-
this.connectionState = state
332336
}
333337

334338
private ssoTokenChangedHandler(params: SsoTokenChangedParams) {

packages/core/src/codewhisperer/activation.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ import { setContext } from '../shared/vscode/setContext'
9191
import { syncSecurityIssueWebview } from './views/securityIssue/securityIssueWebview'
9292
import { detectCommentAboveLine } from '../shared/utilities/commentUtils'
9393
import { activateEditTracking } from './nextEditPrediction/activation'
94-
import { notifySelectDeveloperProfile } from './region/utils'
9594

9695
let localize: nls.LocalizeFunc
9796

@@ -351,10 +350,6 @@ export async function activate(context: ExtContext): Promise<void> {
351350
await AuthUtil.instance.notifySessionConfiguration()
352351
}
353352
}
354-
355-
if (AuthUtil.instance.regionProfileManager.requireProfileSelection()) {
356-
await notifySelectDeveloperProfile()
357-
}
358353
},
359354
{ emit: false, functionId: { name: 'activateCwCore' } }
360355
)

packages/core/src/codewhisperer/region/utils.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const localize = nls.loadMessageBundle()
77
import { AmazonQPromptSettings } from '../../shared/settings'
88
import { telemetry } from '../../shared/telemetry/telemetry'
99
import vscode from 'vscode'
10-
import { selectRegionProfileCommand } from '../commands/basicCommands'
1110
import { placeholder } from '../../shared/vscode/commands2'
1211
import { toastMessage } from '../commands/types'
1312

@@ -36,7 +35,7 @@ export async function notifySelectDeveloperProfile() {
3635
if (resp === selectProfile) {
3736
// Show Profile
3837
telemetry.record({ action: 'select' })
39-
void selectRegionProfileCommand.execute(placeholder, toastMessage)
38+
void vscode.commands.executeCommand('aws.amazonq.selectRegionProfile', placeholder, toastMessage)
4039
} else if (resp === dontShowAgain) {
4140
telemetry.record({ action: 'dontShowAgain' })
4241
await settings.disablePrompt(suppressId)

packages/core/src/codewhisperer/util/authUtil.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import { builderIdStartUrl, internalStartUrl } from '../../auth/sso/constants'
3030
import { VSCODE_EXTENSION_ID } from '../../shared/extensions'
3131
import { RegionProfileManager } from '../region/regionProfileManager'
3232
import { AuthFormId } from '../../login/webview/vue/types'
33+
import { notifySelectDeveloperProfile } from '../region/utils'
34+
import { once } from '../../shared/utilities/functionUtils'
3335

3436
const localize = nls.loadMessageBundle()
3537

@@ -91,9 +93,31 @@ export class AuthUtil implements IAuthProvider {
9193
return this.session.loginType === LoginTypes.SSO
9294
}
9395

96+
/**
97+
* HACK: Ideally we'd put {@link notifySelectDeveloperProfile} in to {@link restore}.
98+
* But because {@link refreshState} is only called if !isConnected, we cannot do it since
99+
* {@link notifySelectDeveloperProfile} needs {@link refreshState} to run so it can set
100+
* the Bearer Token in the LSP first.
101+
*/
102+
didStartSignedIn = false
103+
94104
async restore() {
95105
await this.session.restore()
96-
if (!this.isConnected()) {
106+
this.didStartSignedIn = this.isConnected()
107+
108+
// HACK: We noticed that if calling `refreshState()` here when the user was already signed in, something broke.
109+
// So as a solution we only call it if they were not already signed in.
110+
//
111+
// But in the case where a user was already signed in, we allow `session.restore()` to trigger `refreshState()` through
112+
// event emitters.
113+
// This is unoptimal since `refreshState()` should be able to be called multiple times and still work.
114+
//
115+
// Because of this edge case, when `restore()` is called we cannot assume all Auth is setup when this function returns,
116+
// since we may still be waiting on the event emitter to trigger the expected functions.
117+
//
118+
// TODO: Figure out why removing the if statement below causes things to break. Maybe we just need to
119+
// promisify the call and any subsequent callers will not make a redundant call.
120+
if (!this.didStartSignedIn) {
97121
await this.refreshState()
98122
}
99123
}
@@ -257,20 +281,24 @@ export class AuthUtil implements IAuthProvider {
257281

258282
private async refreshState(state = this.getAuthState()) {
259283
if (state === 'expired' || state === 'notConnected') {
284+
this.lspAuth.deleteBearerToken()
260285
if (this.isIdcConnection()) {
261286
await this.regionProfileManager.invalidateProfile(this.regionProfileManager.activeRegionProfile?.arn)
262287
await this.regionProfileManager.clearCache()
263288
}
264-
this.lspAuth.deleteBearerToken()
265289
}
266290
if (state === 'connected') {
291+
const bearerTokenParams = (await this.session.getToken()).updateCredentialsParams
292+
await this.lspAuth.updateBearerToken(bearerTokenParams)
293+
267294
if (this.isIdcConnection()) {
268295
await this.regionProfileManager.restoreProfileSelection()
269296
}
270-
const bearerTokenParams = (await this.session.getToken()).updateCredentialsParams
271-
await this.lspAuth.updateBearerToken(bearerTokenParams)
272297
}
273298

299+
// regardless of state, send message at startup if user needs to select a Developer Profile
300+
void this.tryNotifySelectDeveloperProfile()
301+
274302
vsCodeState.isFreeTierLimitReached = false
275303
await this.setVscodeContextProps(state)
276304
await Promise.all([
@@ -283,6 +311,12 @@ export class AuthUtil implements IAuthProvider {
283311
}
284312
}
285313

314+
private tryNotifySelectDeveloperProfile = once(async () => {
315+
if (this.regionProfileManager.requireProfileSelection() && this.didStartSignedIn) {
316+
await notifySelectDeveloperProfile()
317+
}
318+
})
319+
286320
async getTelemetryMetadata(): Promise<TelemetryMetadata> {
287321
if (!this.isConnected()) {
288322
return {

0 commit comments

Comments
 (0)