Skip to content

Commit bfd23b9

Browse files
authored
feat(telemetry): add auth_userState to toolkit ext (aws#4860)
- also, to match the spec we rename metric field from 'enabledAuthConnections' to 'authEnabledConnections'
1 parent 81f88e4 commit bfd23b9

File tree

8 files changed

+97
-5
lines changed

8 files changed

+97
-5
lines changed

packages/amazonq/src/extensionShared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export async function activateShared(context: vscode.ExtensionContext, isWeb: bo
166166
}
167167
telemetry.record({
168168
authStatus: authState === 'connected' || authState === 'expired' ? authState : 'notConnected',
169-
enabledAuthConnections: authKinds.join(','),
169+
authEnabledConnections: authKinds.join(','),
170170
})
171171
})
172172
}

packages/core/src/auth/ui/vue/authForms/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type AuthFormId =
1111
| 'identityCenterCodeCatalyst'
1212
| 'identityCenterExplorer'
1313
| 'aggregateExplorer'
14+
| 'unknown'
1415

1516
export function isBuilderIdAuth(id: AuthFormId): boolean {
1617
return id.startsWith('builderId')
@@ -24,4 +25,5 @@ export const AuthFormDisplayName: Record<AuthFormId, string> = {
2425
identityCenterCodeWhisperer: 'Amazon Q with IAM Identity Center',
2526
identityCenterExplorer: 'AWS Explorer with IAM Identity Center',
2627
aggregateExplorer: '',
28+
unknown: '',
2729
} as const

packages/core/src/auth/ui/vue/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ export const authFormTelemetryMapping: {
3535
identityCenterCodeCatalyst: { featureType: 'codecatalyst', authType: 'iamIdentityCenter' },
3636
identityCenterExplorer: { featureType: 'awsExplorer', authType: 'iamIdentityCenter' },
3737
aggregateExplorer: { featureType: 'awsExplorer', authType: 'other' }, // this should never actually be used
38+
unknown: { featureType: 'unknown', authType: 'other' }, // this should never actually be used
3839
}

packages/core/src/auth/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,9 @@ export class ExtensionUse {
685685
}
686686

687687
/**
688-
* Simple readable ID for telemetry reporting
688+
* @deprecated
689+
* Remove in favor of AuthFormId
690+
* Simple readable ID for telemetry reporting
689691
*/
690692
export type AuthSimpleId =
691693
| 'sharedCredentials'

packages/core/src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import { isReleaseVersion } from './shared/vscode/env'
4949
import { telemetry } from './shared/telemetry/telemetry'
5050
import { Auth } from './auth/auth'
5151
import { registerSubmitFeedback } from './feedback/vue/submitFeedback'
52-
import { activateShared, deactivateShared } from './extensionShared'
52+
import { activateShared, deactivateShared, emitUserState } from './extensionShared'
5353
import { learnMoreAmazonQCommand, qExtensionPageCommand, dismissQTree } from './amazonq/explorer/amazonQChildrenNodes'
5454
import { AuthUtil, isPreviousQUser } from './codewhisperer/util/authUtil'
5555
import { installAmazonQExtension } from './codewhisperer/commands/basicCommands'
@@ -248,6 +248,8 @@ export async function activate(context: vscode.ExtensionContext) {
248248
if (!isReleaseVersion()) {
249249
globals.telemetry.assertPassiveTelemetry(globals.didReload)
250250
}
251+
252+
await emitUserState()
251253
} catch (error) {
252254
const stacktrace = (error as Error).stack?.split('\n')
253255
// truncate if the stacktrace is unusually long

packages/core/src/extensionShared.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { Commands } from './shared/vscode/commands2'
1717
import { documentationUrl, endpointsFileUrl, githubCreateIssueUrl, githubUrl } from './shared/constants'
1818
import { getIdeProperties, aboutExtension, isCloud9 } from './shared/extensionUtilities'
1919
import { logAndShowError, logAndShowWebviewError } from './shared/utilities/logAndShowUtils'
20-
import { telemetry } from './shared/telemetry/telemetry'
20+
import { AuthStatus, telemetry } from './shared/telemetry/telemetry'
2121
import { openUrl } from './shared/utilities/vsCodeUtils'
2222
import { activateViewsShared } from './awsexplorer/activationShared'
2323

@@ -42,6 +42,10 @@ import { UriHandler } from './shared/vscode/uriHandler'
4242
import { disableAwsSdkWarning } from './shared/awsClientBuilder'
4343
import { FileResourceFetcher } from './shared/resourcefetcher/fileResourceFetcher'
4444
import { ResourceFetcher } from './shared/resourcefetcher/resourcefetcher'
45+
import { ExtStartUpSources, getAuthFormIdsFromConnection } from './shared/telemetry/util'
46+
import { ExtensionUse } from './auth/utils'
47+
import { Auth } from './auth'
48+
import { AuthFormId } from './auth/ui/vue/authForms/types'
4549

4650
// In web mode everything must be in a single file, so things like the endpoints file will not be available.
4751
// The following imports the endpoints file, which causes webpack to bundle it in the final output file
@@ -234,3 +238,38 @@ function wrapWithProgressForCloud9(channel: vscode.OutputChannel): (typeof vscod
234238
})
235239
}
236240
}
241+
242+
export async function emitUserState() {
243+
await telemetry.auth_userState.run(async () => {
244+
telemetry.record({ passive: true })
245+
246+
const firstUse = ExtensionUse.instance.isFirstUse()
247+
const wasUpdated = ExtensionUse.instance.wasUpdated()
248+
249+
if (firstUse) {
250+
telemetry.record({ source: ExtStartUpSources.firstStartUp })
251+
} else if (wasUpdated) {
252+
telemetry.record({ source: ExtStartUpSources.update })
253+
} else {
254+
telemetry.record({ source: ExtStartUpSources.reload })
255+
}
256+
257+
let authStatus: AuthStatus = 'notConnected'
258+
const enabledConnections: Set<AuthFormId> = new Set()
259+
if (Auth.instance.hasConnections) {
260+
authStatus = 'expired'
261+
;(await Auth.instance.listConnections()).forEach(conn => {
262+
const state = Auth.instance.getConnectionState(conn)
263+
if (state === 'valid') {
264+
authStatus = 'connected'
265+
}
266+
267+
getAuthFormIdsFromConnection(conn).forEach(id => enabledConnections.add(id))
268+
})
269+
}
270+
telemetry.record({
271+
authStatus,
272+
authEnabledConnections: [...enabledConnections].join(','),
273+
})
274+
})
275+
}

packages/core/src/shared/telemetry/util.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ import { isExtensionInstalled, VSCODE_EXTENSION_ID } from '../utilities'
2020
import { randomUUID } from '../../common/crypto'
2121
import { activateExtension } from '../utilities/vsCodeUtils'
2222
import { ClassToInterfaceType } from '../utilities/tsUtils'
23+
import {
24+
Connection,
25+
hasScopes,
26+
isBuilderIdConnection,
27+
isIamConnection,
28+
isIdcSsoConnection,
29+
isValidCodeCatalystConnection,
30+
scopesSsoAccountAccess,
31+
} from '../../auth/connection'
32+
import { AuthFormId } from '../../auth/ui/vue/authForms/types'
33+
import { isValidAmazonQConnection } from '../../codewhisperer/util/authUtil'
2334

2435
const legacySettingsTelemetryValueDisable = 'Disable'
2536
const legacySettingsTelemetryValueEnable = 'Enable'
@@ -257,3 +268,33 @@ export const ExtStartUpSources = {
257268
} as const
258269

259270
export type ExtStartUpSource = (typeof ExtStartUpSources)[keyof typeof ExtStartUpSources]
271+
272+
/**
273+
* Returns readable auth Ids for a connection, which are used by telemetry.
274+
*/
275+
export function getAuthFormIdsFromConnection(conn: Connection): AuthFormId[] {
276+
const authIds: AuthFormId[] = []
277+
let connType: 'builderId' | 'identityCenter'
278+
279+
if (isIamConnection(conn)) {
280+
return ['credentials']
281+
} else if (isBuilderIdConnection(conn)) {
282+
connType = 'builderId'
283+
} else if (isIdcSsoConnection(conn)) {
284+
connType = 'identityCenter'
285+
if (hasScopes(conn, scopesSsoAccountAccess)) {
286+
authIds.push('identityCenterExplorer')
287+
}
288+
} else {
289+
return ['unknown']
290+
}
291+
292+
if (isValidCodeCatalystConnection(conn)) {
293+
authIds.push(`${connType}CodeCatalyst`)
294+
}
295+
if (isValidAmazonQConnection(conn)) {
296+
authIds.push(`${connType}CodeWhisperer`)
297+
}
298+
299+
return authIds
300+
}

packages/core/src/shared/telemetry/vscodeTelemetry.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@
318318
"type": "string",
319319
"description": "Comma-delimited list of features for which auth is enabled."
320320
},
321+
{
322+
"name": "authEnabledConnections",
323+
"type": "string",
324+
"description": "Comma-delimited list of enabled auths."
325+
},
321326
{
322327
"name": "region",
323328
"type": "string",
@@ -1112,7 +1117,7 @@
11121117
"required": true
11131118
},
11141119
{
1115-
"type": "enabledAuthConnections",
1120+
"type": "authEnabledConnections",
11161121
"required": true
11171122
}
11181123
],

0 commit comments

Comments
 (0)