Skip to content

Commit 79b5a90

Browse files
telemetry: Amazon Q indicate if it can auto connect (aws#4846)
* rename: findUsableConnection() Signed-off-by: Nikolas Komonen <[email protected]> * telemetry: Amazon Q indicate if it can auto connect In the telemetry event auth_userState we need to indicate our authStatus field as 'connected' if the Amazon Q extension does not have a connection, but we detect one exists already in the AWS Toolkit extension. This commit adds extra logic which checks if an autoconnectable connection can be found in AWS Toolkit and then indicates that we are connected. This assumes that the separate Login auto-connect logic will eventually do the auto connection. Signed-off-by: Nikolas Komonen <[email protected]> --------- Signed-off-by: Nikolas Komonen <[email protected]> Co-authored-by: Maxim Hayes <[email protected]>
1 parent 8929665 commit 79b5a90

File tree

8 files changed

+87
-16
lines changed

8 files changed

+87
-16
lines changed

packages/amazonq/src/auth/util.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
7+
import { AuthStatus } from '../../../core/dist/src/shared/telemetry/telemetry'
8+
import { AwsConnection } from 'aws-core-vscode/auth'
9+
import { activateExtension, getLogger } from 'aws-core-vscode/shared'
10+
import { VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils'
11+
12+
/** Provides the status of the Auth connection for Amazon Q, specifically for telemetry purposes. */
13+
export async function getAuthStatus(): Promise<AuthStatus> {
14+
// Get auth state from the Amazon Q extension
15+
const authState = (await AuthUtil.instance.getChatAuthState()).codewhispererChat
16+
let authStatus: AuthStatus = authState === 'connected' || authState === 'expired' ? authState : 'notConnected'
17+
18+
// If the Q extension does not have its own connection, it will fallback and check
19+
// if the Toolkit extension can provide a connection that works with Q
20+
if (authStatus === 'notConnected') {
21+
let autoConnectConn: AwsConnection | undefined = undefined
22+
try {
23+
autoConnectConn = await getAutoConnectableConnection()
24+
} catch (e) {
25+
getLogger().error(`Failed ${getAutoConnectableConnection.name}:\n\n%s`, JSON.stringify(e))
26+
}
27+
28+
// Determine the status of the Toolkit connection we will autoconnect to
29+
if (autoConnectConn) {
30+
authStatus = autoConnectConn.state === 'valid' ? 'connected' : 'expired'
31+
}
32+
}
33+
34+
return authStatus
35+
}
36+
37+
/**
38+
* Returns a connection from the standalone Toolkit extension that
39+
* the Amazon Q extension can use. Otherwise it returns undefined.
40+
*
41+
* HACK: Our frontend Login ui for Amazon Q will auto connect if required/possible,
42+
* but we cannot detect this at the end of Amazon Q extension activation.
43+
* So we reuse the same {@link findUsableQConnection}()
44+
* and assume that the frontend will have the same result and auto connect.
45+
*/
46+
async function getAutoConnectableConnection(): Promise<AwsConnection | undefined> {
47+
const extension = await activateExtension<any>(VSCODE_EXTENSION_ID.awstoolkit)
48+
if (!extension) {
49+
return undefined
50+
}
51+
const importedApis = extension.exports.getApi(VSCODE_EXTENSION_ID.awstoolkit)
52+
53+
const listConnections: () => Promise<AwsConnection[]> = importedApis?.listConnections
54+
if (!listConnections) {
55+
// Either the user has an older toolkit version w/o the API, or the API has changed
56+
// and this needs to be updated.
57+
return undefined
58+
}
59+
60+
return AuthUtil.instance.findUsableQConnection(await listConnections())
61+
}

packages/amazonq/src/extensionShared.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
activate as activateCodeWhisperer,
1212
shutdown as codewhispererShutdown,
1313
amazonQDismissedKey,
14-
AuthUtil,
1514
} from 'aws-core-vscode/codewhisperer'
1615
import {
1716
ExtContext,
@@ -36,6 +35,7 @@ import { isExtensionActive, VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils'
3635
import { registerSubmitFeedback } from 'aws-core-vscode/feedback'
3736
import { telemetry, ExtStartUpSources } from 'aws-core-vscode/telemetry'
3837
import { DevFunction, updateDevMode } from 'aws-core-vscode/dev'
38+
import { getAuthStatus } from './auth/util'
3939

4040
export async function activateShared(context: vscode.ExtensionContext, isWeb: boolean) {
4141
initialize(context, isWeb)
@@ -156,16 +156,18 @@ export async function activateShared(context: vscode.ExtensionContext, isWeb: bo
156156
telemetry.record({ source: ExtStartUpSources.reload })
157157
}
158158

159-
const authState = (await AuthUtil.instance.getChatAuthState()).codewhispererChat
160159
const authKinds: AuthUtils.AuthSimpleId[] = []
161160
if (await AuthUtils.hasBuilderId('codewhisperer')) {
162161
authKinds.push('builderIdCodeWhisperer')
163162
}
164163
if (await AuthUtils.hasSso('codewhisperer')) {
165164
authKinds.push('identityCenterCodeWhisperer')
166165
}
166+
167+
const authStatus = await getAuthStatus()
168+
167169
telemetry.record({
168-
authStatus: authState === 'connected' || authState === 'expired' ? authState : 'notConnected',
170+
authStatus,
169171
authEnabledConnections: authKinds.join(','),
170172
})
171173
})

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,21 @@ export class AuthUtil {
429429

430430
return state
431431
}
432+
433+
/**
434+
* From the given connections, returns a connection that works with Amazon Q.
435+
*/
436+
findUsableQConnection(connections: AwsConnection[]): AwsConnection | undefined {
437+
const hasQScopes = (c: AwsConnection) => amazonQScopes.every(s => c.scopes?.includes(s))
438+
const score = (c: AwsConnection) => Number(hasQScopes(c)) * 10 + Number(c.state === 'valid')
439+
connections.sort(function (a, b) {
440+
return score(b) - score(a)
441+
})
442+
if (hasQScopes(connections[0])) {
443+
return connections[0]
444+
}
445+
return undefined
446+
}
432447
}
433448

434449
/**

packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,8 @@ export class AmazonQLoginWebview extends CommonAuthWebview {
5454
* @param connections List of AWS Toolkit Connections
5555
* @returns Amazon Q connection, or undefined if none of the given connections have scopes required for Amazon Q.
5656
*/
57-
findConnection(connections: AwsConnection[]): AwsConnection | undefined {
58-
const hasQScopes = (c: AwsConnection) => amazonQScopes.every(s => c.scopes?.includes(s))
59-
const score = (c: AwsConnection) => Number(hasQScopes(c)) * 10 + Number(c.state === 'valid')
60-
connections.sort(function (a, b) {
61-
return score(b) - score(a)
62-
})
63-
if (hasQScopes(connections[0])) {
64-
return connections[0]
65-
}
66-
return undefined
57+
findUsableConnection(connections: AwsConnection[]): AwsConnection | undefined {
58+
return AuthUtil.instance.findUsableQConnection(connections)
6759
}
6860

6961
async useConnection(connectionId: string, auto: boolean): Promise<AuthError | undefined> {

packages/core/src/login/webview/vue/backend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export abstract class CommonAuthWebview extends VueWebview {
164164
*/
165165
abstract useConnection(connectionId: string, auto: boolean): Promise<AuthError | undefined>
166166

167-
abstract findConnection(connections: AwsConnection[]): AwsConnection | undefined
167+
abstract findUsableConnection(connections: AwsConnection[]): AwsConnection | undefined
168168

169169
async errorNotification(e: AuthError) {
170170
void vscode.window.showInformationMessage(`${e.text}`)

packages/core/src/login/webview/vue/login.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ export default defineComponent({
492492
// If Amazon Q has no connections while Toolkit has connections
493493
// Auto connect Q using toolkit connection.
494494
if (connections.length === 0 && sharedConnections && sharedConnections.length > 0) {
495-
const conn = await client.findConnection(sharedConnections)
495+
const conn = await client.findUsableConnection(sharedConnections)
496496
if (conn) {
497497
await client.useConnection(conn.id, true)
498498
}

packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export class ToolkitLoginWebview extends CommonAuthWebview {
146146
})
147147
}
148148

149-
findConnection(connections: AwsConnection[]): AwsConnection | undefined {
149+
findUsableConnection(connections: AwsConnection[]): AwsConnection | undefined {
150150
return undefined
151151
}
152152

packages/core/src/shared/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ export { RegionProvider } from './regions/regionProvider'
1515
export { Commands } from './vscode/commands2'
1616
export { getMachineId } from './vscode/env'
1717
export { getLogger } from './logger/logger'
18+
export { activateExtension } from './utilities/vsCodeUtils'

0 commit comments

Comments
 (0)