Skip to content

Commit

Permalink
Merge master into feature/emr
Browse files Browse the repository at this point in the history
  • Loading branch information
aws-toolkit-automation authored Nov 6, 2024
2 parents fb11f9f + 2c356e3 commit 7c1189a
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 143 deletions.
67 changes: 3 additions & 64 deletions packages/amazonq/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/

import {
AuthUtils,
CredentialsStore,
LoginManager,
getTelemetryMetadataForConn,
initializeAuth,
isAnySsoConnection,
} from 'aws-core-vscode/auth'
import {
AuthState,
AuthUtil,
activate as activateCodeWhisperer,
shutdown as shutdownCodeWhisperer,
} from 'aws-core-vscode/codewhisperer'
import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth'
import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer'
import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode'
import { CommonAuthWebview } from 'aws-core-vscode/login'
import {
Expand All @@ -38,15 +26,13 @@ import {
globals,
initialize,
initializeComputeRegion,
isNetworkError,
messages,
placeholder,
setContext,
setupUninstallHandler,
maybeShowMinVscodeWarning,
isSageMaker,
} from 'aws-core-vscode/shared'
import { ExtStartUpSources, telemetry } from 'aws-core-vscode/telemetry'
import { ExtStartUpSources } from 'aws-core-vscode/telemetry'
import { VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils'
import { join } from 'path'
import * as semver from 'semver'
Expand Down Expand Up @@ -161,53 +147,6 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is
void focusAmazonQPanel.execute(placeholder, 'firstStartUp')
}, 1000)
}

await telemetry.auth_userState
.run(async () => {
telemetry.record({ passive: true })

const firstUse = AuthUtils.ExtensionUse.instance.isFirstUse()
const wasUpdated = AuthUtils.ExtensionUse.instance.wasUpdated()

if (firstUse) {
telemetry.record({ source: ExtStartUpSources.firstStartUp })
} else if (wasUpdated) {
telemetry.record({ source: ExtStartUpSources.update })
} else {
telemetry.record({ source: ExtStartUpSources.reload })
}

let authState: AuthState = 'disconnected'
try {
// May call connection validate functions that try to refresh the token.
// This could result in network errors.
authState = (await AuthUtil.instance.getChatAuthState(false)).codewhispererChat
} catch (err) {
if (
isNetworkError(err) &&
AuthUtil.instance.conn &&
AuthUtil.instance.auth.getConnectionState(AuthUtil.instance.conn) === 'valid'
) {
authState = 'connectedWithNetworkError'
} else {
throw err
}
}
const currConn = AuthUtil.instance.conn
if (currConn !== undefined && !(isAnySsoConnection(currConn) || isSageMaker())) {
getLogger().error(`Current Amazon Q connection is not SSO, type is: %s`, currConn?.type)
}

telemetry.record({
authStatus:
authState === 'connected' || authState === 'expired' || authState === 'connectedWithNetworkError'
? authState
: 'notConnected',
authEnabledConnections: AuthUtils.getAuthFormIdsFromConnection(currConn).join(','),
...(await getTelemetryMetadataForConn(currConn)),
})
})
.catch((err) => getLogger().error('Error collecting telemetry for auth_userState: %s', err))
}

export async function deactivateCommon() {
Expand Down
51 changes: 49 additions & 2 deletions packages/amazonq/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ import * as vscode from 'vscode'
import { activateAmazonQCommon, amazonQContextPrefix, deactivateCommon } from './extension'
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
import { activate as activateQGumby } from 'aws-core-vscode/amazonqGumby'
import { ExtContext, globals, CrashMonitoring } from 'aws-core-vscode/shared'
import { ExtContext, globals, CrashMonitoring, getLogger, isNetworkError, isSageMaker } from 'aws-core-vscode/shared'
import { filetypes, SchemaService } from 'aws-core-vscode/sharedNode'
import { updateDevMode } from 'aws-core-vscode/dev'
import { CommonAuthViewProvider } from 'aws-core-vscode/login'
import { isExtensionActive, VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils'
import { registerSubmitFeedback } from 'aws-core-vscode/feedback'
import { DevOptions } from 'aws-core-vscode/dev'
import { Auth } from 'aws-core-vscode/auth'
import { Auth, AuthUtils, getTelemetryMetadataForConn, isAnySsoConnection } from 'aws-core-vscode/auth'
import api from './api'
import { activate as activateCWChat } from './app/chat/activation'
import { beta } from 'aws-core-vscode/dev'
import { activate as activateNotifications } from 'aws-core-vscode/notifications'
import { AuthState, AuthUtil } from 'aws-core-vscode/codewhisperer'
import { telemetry, AuthUserState } from 'aws-core-vscode/telemetry'

export async function activate(context: vscode.ExtensionContext) {
// IMPORTANT: No other code should be added to this function. Place it in one of the following 2 functions where appropriate.
Expand Down Expand Up @@ -61,6 +64,50 @@ async function activateAmazonQNode(context: vscode.ExtensionContext) {

await setupDevMode(context)
await beta.activate(context)

// TODO: Should probably emit for web as well.
// Will the web metric look the same?
const authState = await getAuthState()
telemetry.auth_userState.emit({
passive: true,
result: 'Succeeded',
source: AuthUtils.ExtensionUse.instance.sourceForTelemetry(),
...authState,
})

await activateNotifications(context, authState, getAuthState)
}

async function getAuthState(): Promise<Omit<AuthUserState, 'source'>> {
let authState: AuthState = 'disconnected'
try {
// May call connection validate functions that try to refresh the token.
// This could result in network errors.
authState = (await AuthUtil.instance.getChatAuthState(false)).codewhispererChat
} catch (err) {
if (
isNetworkError(err) &&
AuthUtil.instance.conn &&
AuthUtil.instance.auth.getConnectionState(AuthUtil.instance.conn) === 'valid'
) {
authState = 'connectedWithNetworkError'
} else {
throw err
}
}
const currConn = AuthUtil.instance.conn
if (currConn !== undefined && !(isAnySsoConnection(currConn) || isSageMaker())) {
getLogger().error(`Current Amazon Q connection is not SSO, type is: %s`, currConn?.type)
}

return {
authStatus:
authState === 'connected' || authState === 'expired' || authState === 'connectedWithNetworkError'
? authState
: 'notConnected',
authEnabledConnections: AuthUtils.getAuthFormIdsFromConnection(currConn).join(','),
...(await getTelemetryMetadataForConn(currConn)),
}
}

/**
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/auth/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import { EcsCredentialsProvider } from './providers/ecsCredentialsProvider'
import { EnvVarsCredentialsProvider } from './providers/envVarsCredentialsProvider'
import { showMessageWithUrl } from '../shared/utilities/messages'
import { credentialHelpUrl } from '../shared/constants'
import { ExtStartUpSource } from '../shared/telemetry/util'

// iam-only excludes Builder ID and IAM Identity Center from the list of valid connections
// TODO: Understand if "iam" should include these from the list at all
Expand Down Expand Up @@ -734,6 +735,19 @@ export class ExtensionUse {
return this.wasExtensionUpdated
}

/**
* Returns a {@link ExtStartUpSource} based on the current state of the extension.
*/
sourceForTelemetry(): ExtStartUpSource {
if (this.isFirstUse()) {
return ExtStartUpSources.firstStartUp
} else if (this.wasUpdated()) {
return ExtStartUpSources.update
} else {
return ExtStartUpSources.reload
}
}

private updateMemento(key: 'isExtensionFirstUse' | 'lastExtensionVersion', val: any) {
globals.globalState.tryUpdate(key, val)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/awsexplorer/activationShared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ export function registerToolView(viewNode: ToolView, context: vscode.ExtensionCo
telemetry.cdk_appExpanded.emit()
}
})

return toolView
}
55 changes: 1 addition & 54 deletions packages/core/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Commands } from './shared/vscode/commands2'
import { endpointsFileUrl, githubCreateIssueUrl, githubUrl } from './shared/constants'
import { getIdeProperties, aboutExtension, isCloud9, getDocUrl } from './shared/extensionUtilities'
import { logAndShowError, logAndShowWebviewError } from './shared/utilities/logAndShowUtils'
import { AuthStatus, telemetry } from './shared/telemetry/telemetry'
import { telemetry } from './shared/telemetry/telemetry'
import { openUrl } from './shared/utilities/vsCodeUtils'
import { activateViewsShared } from './awsexplorer/activationShared'
import fs from './shared/fs/fs'
Expand All @@ -45,11 +45,6 @@ import { UriHandler } from './shared/vscode/uriHandler'
import { disableAwsSdkWarning } from './shared/awsClientBuilder'
import { FileResourceFetcher } from './shared/resourcefetcher/fileResourceFetcher'
import { ResourceFetcher } from './shared/resourcefetcher/resourcefetcher'
import { ExtStartUpSources } from './shared/telemetry/util'
import { ExtensionUse, getAuthFormIdsFromConnection } from './auth/utils'
import { Auth } from './auth'
import { AuthFormId } from './login/webview/vue/types'
import { getTelemetryMetadataForConn, isSsoConnection } from './auth/connection'
import { registerCommands } from './commands'

// In web mode everything must be in a single file, so things like the endpoints file will not be available.
Expand Down Expand Up @@ -258,51 +253,3 @@ function wrapWithProgressForCloud9(channel: vscode.OutputChannel): (typeof vscod
})
}
}

export async function emitUserState() {
await telemetry.auth_userState.run(async () => {
telemetry.record({ passive: true })

const firstUse = ExtensionUse.instance.isFirstUse()
const wasUpdated = ExtensionUse.instance.wasUpdated()

if (firstUse) {
telemetry.record({ source: ExtStartUpSources.firstStartUp })
} else if (wasUpdated) {
telemetry.record({ source: ExtStartUpSources.update })
} else {
telemetry.record({ source: ExtStartUpSources.reload })
}

let authStatus: AuthStatus = 'notConnected'
const enabledConnections: Set<AuthFormId> = new Set()
const enabledScopes: Set<string> = new Set()
if (Auth.instance.hasConnections) {
authStatus = 'expired'
;(await Auth.instance.listConnections()).forEach((conn) => {
const state = Auth.instance.getConnectionState(conn)
if (state === 'valid') {
authStatus = 'connected'
}

getAuthFormIdsFromConnection(conn).forEach((id) => enabledConnections.add(id))
if (isSsoConnection(conn)) {
conn.scopes?.forEach((s) => enabledScopes.add(s))
}
})
}

// There may be other SSO connections in toolkit, but there is no use case for
// displaying registration info for non-active connections at this time.
const activeConn = Auth.instance.activeConnection
if (activeConn?.type === 'sso') {
telemetry.record(await getTelemetryMetadataForConn(activeConn))
}

telemetry.record({
authStatus,
authEnabledConnections: [...enabledConnections].join(','),
authScopes: [...enabledScopes].join(','),
})
})
}
54 changes: 50 additions & 4 deletions packages/core/src/extensionNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,27 @@ import * as beta from './dev/beta'
import { activate as activateApplicationComposer } from './applicationcomposer/activation'
import { activate as activateRedshift } from './awsService/redshift/activation'
import { activate as activateIamPolicyChecks } from './awsService/accessanalyzer/activation'
import { activate as activateNotifications } from './notifications/activation'
import { SchemaService } from './shared/schemas'
import { AwsResourceManager } from './dynamicResources/awsResourceManager'
import globals from './shared/extensionGlobals'
import { Experiments, Settings, showSettingsFailedMsg } from './shared/settings'
import { isReleaseVersion } from './shared/vscode/env'
import { telemetry } from './shared/telemetry/telemetry'
import { AuthStatus, AuthUserState, telemetry } from './shared/telemetry/telemetry'
import { Auth, SessionSeparationPrompt } from './auth/auth'
import { getTelemetryMetadataForConn } from './auth/connection'
import { registerSubmitFeedback } from './feedback/vue/submitFeedback'
import { activateCommon, deactivateCommon, emitUserState } from './extension'
import { activateCommon, deactivateCommon } from './extension'
import { learnMoreAmazonQCommand, qExtensionPageCommand, dismissQTree } from './amazonq/explorer/amazonQChildrenNodes'
import { AuthUtil, codeWhispererCoreScopes, isPreviousQUser } from './codewhisperer/util/authUtil'
import { installAmazonQExtension } from './codewhisperer/commands/basicCommands'
import { isExtensionInstalled, VSCODE_EXTENSION_ID } from './shared/utilities'
import { ExtensionUse, initializeCredentialsProviderManager } from './auth/utils'
import { ExtensionUse, getAuthFormIdsFromConnection, initializeCredentialsProviderManager } from './auth/utils'
import { ExtStartUpSources } from './shared/telemetry'
import { activate as activateThreatComposerEditor } from './threatComposer/activation'
import { isSsoConnection, hasScopes } from './auth/connection'
import { CrashMonitoring, setContext } from './shared'
import { AuthFormId } from './login/webview/vue/types'

let localize: nls.LocalizeFunc

Expand Down Expand Up @@ -233,7 +236,17 @@ export async function activate(context: vscode.ExtensionContext) {
globals.telemetry.assertPassiveTelemetry(globals.didReload)
}

await emitUserState()
// TODO: Should probably emit for web as well.
// Will the web metric look the same?
const authState = await getAuthState()
telemetry.auth_userState.emit({
passive: true,
result: 'Succeeded',
source: ExtensionUse.instance.sourceForTelemetry(),
...authState,
})

await activateNotifications(context, authState, getAuthState)
} catch (error) {
const stacktrace = (error as Error).stack?.split('\n')
// truncate if the stacktrace is unusually long
Expand Down Expand Up @@ -324,3 +337,36 @@ function recordToolkitInitialization(activationStartedOn: number, settingsValid:
logger?.error(err as Error)
}
}

async function getAuthState(): Promise<Omit<AuthUserState, 'source'>> {
let authStatus: AuthStatus = 'notConnected'
const enabledConnections: Set<AuthFormId> = new Set()
const enabledScopes: Set<string> = new Set()
if (Auth.instance.hasConnections) {
authStatus = 'expired'
;(await Auth.instance.listConnections()).forEach((conn) => {
const state = Auth.instance.getConnectionState(conn)
if (state === 'valid') {
authStatus = 'connected'
}

getAuthFormIdsFromConnection(conn).forEach((id) => enabledConnections.add(id))
if (isSsoConnection(conn)) {
conn.scopes?.forEach((s) => enabledScopes.add(s))
}
})
}

// There may be other SSO connections in toolkit, but there is no use case for
// displaying registration info for non-active connections at this time.
const activeConn = Auth.instance.activeConnection
if (activeConn?.type === 'sso') {
telemetry.record(await getTelemetryMetadataForConn(activeConn))
}

return {
authStatus,
authEnabledConnections: [...enabledConnections].sort().join(','),
authScopes: [...enabledScopes].sort().join(','),
}
}
Loading

0 comments on commit 7c1189a

Please sign in to comment.