diff --git a/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts b/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts index 05344dc658..0e07c6d240 100644 --- a/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts +++ b/packages/core/src/login/webview/vue/amazonq/backend_amazonq.ts @@ -81,7 +81,7 @@ export class AmazonQLoginWebview extends CommonAuthWebview { credentialSourceId: conn.startUrl === builderIdStartUrl ? 'awsId' : 'iamIdentityCenter', credentialStartUrl: conn.startUrl, - region: conn.ssoRegion, + awsRegion: conn.ssoRegion, authEnabledFeatures: this.getAuthEnabledFeatures(conn), }) } @@ -135,7 +135,7 @@ export class AmazonQLoginWebview extends CommonAuthWebview { if (!auto) { this.storeMetricMetadata({ credentialStartUrl: conn.startUrl, - region: conn.ssoRegion, + awsRegion: conn.ssoRegion, authEnabledFeatures: this.getAuthEnabledFeatures(newConn), }) } @@ -171,7 +171,7 @@ export class AmazonQLoginWebview extends CommonAuthWebview { return await this.ssoSetup('startCodeWhispererEnterpriseSetup', async () => { this.storeMetricMetadata({ credentialStartUrl: startUrl, - region, + awsRegion: region, credentialSourceId: 'iamIdentityCenter', authEnabledFeatures: 'codewhisperer', isReAuth: false, diff --git a/packages/core/src/login/webview/vue/backend.ts b/packages/core/src/login/webview/vue/backend.ts index 49b48321c3..4b97d23436 100644 --- a/packages/core/src/login/webview/vue/backend.ts +++ b/packages/core/src/login/webview/vue/backend.ts @@ -201,8 +201,8 @@ export abstract class CommonAuthWebview extends VueWebview { emitAuthMetric() { // We shouldn't report startUrl or region if we aren't reporting IdC if (this.metricMetadata.credentialSourceId !== 'iamIdentityCenter') { - this.metricMetadata.region = undefined - this.metricMetadata.credentialStartUrl = undefined + delete this.metricMetadata.awsRegion + delete this.metricMetadata.credentialStartUrl } telemetry.auth_addConnection.emit({ ...this.metricMetadata, @@ -248,15 +248,19 @@ export abstract class CommonAuthWebview extends VueWebview { * Get metadata about the current auth for reauthentication telemetry. */ getMetadataForExistingConn(conn = AuthUtil.instance.conn): TelemetryMetadata { - if (isBuilderIdConnection(conn)) { + if (conn === undefined) { + return {} + } + + if (isIdcSsoConnection(conn)) { return { - credentialSourceId: 'awsId', + credentialSourceId: 'iamIdentityCenter', + credentialStartUrl: conn?.startUrl, + awsRegion: conn?.ssoRegion, } - } else if (isIdcSsoConnection(conn)) { + } else if (isBuilderIdConnection(conn)) { return { - credentialSourceId: 'iamIdentityCenter', - credentialStartUrl: (conn as SsoConnection).startUrl, - region: (conn as SsoConnection).ssoRegion, + credentialSourceId: 'awsId', } } else if (isIamConnection(conn)) { return { diff --git a/packages/core/src/login/webview/vue/login.vue b/packages/core/src/login/webview/vue/login.vue index a9cc70c404..aad1c78992 100644 --- a/packages/core/src/login/webview/vue/login.vue +++ b/packages/core/src/login/webview/vue/login.vue @@ -421,7 +421,7 @@ export default defineComponent({ } else if (this.selectedLoginOption === LoginOption.ENTERPRISE_SSO) { this.stage = 'SSO_FORM' this.$nextTick(() => document.getElementById('startUrl')!.focus()) - await client.storeMetricMetadata({ region: this.selectedRegion }) + await client.storeMetricMetadata({ awsRegion: this.selectedRegion }) } else if (this.selectedLoginOption >= LoginOption.EXISTING_LOGINS) { this.stage = 'AUTHENTICATING' const selectedConnection = @@ -509,7 +509,7 @@ export default defineComponent({ handleRegionInput(event: any) { this.handleUrlInput() // startUrl validity depends on region, see handleUriInput() for details void client.storeMetricMetadata({ - region: event.target.value, + awsRegion: event.target.value, }) void client.emitUiClick('auth_regionSelection') }, diff --git a/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts b/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts index 16d743dc59..274f4896f2 100644 --- a/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts +++ b/packages/core/src/login/webview/vue/toolkit/backend_toolkit.ts @@ -40,7 +40,7 @@ export class ToolkitLoginWebview extends CommonAuthWebview { const metadata: TelemetryMetadata = { credentialSourceId: 'iamIdentityCenter', credentialStartUrl: startUrl, - region, + awsRegion: region, } if (this.isCodeCatalystLogin) { diff --git a/packages/core/src/shared/regions/regionProvider.ts b/packages/core/src/shared/regions/regionProvider.ts index 732b0f38bf..0fe5a83516 100644 --- a/packages/core/src/shared/regions/regionProvider.ts +++ b/packages/core/src/shared/regions/regionProvider.ts @@ -11,11 +11,12 @@ const localize = nls.loadMessageBundle() import * as vscode from 'vscode' import { getLogger } from '../logger' import { Endpoints, loadEndpoints, Region } from './endpoints' -import { AWSTreeNodeBase } from '../treeview/nodes/awsTreeNodeBase' import { regionSettingKey } from '../constants' import { AwsContext } from '../awsContext' -import { getIdeProperties, isCloud9 } from '../extensionUtilities' +import { getIdeProperties, isAmazonQ, isCloud9 } from '../extensionUtilities' import { ResourceFetcher } from '../resourcefetcher/resourcefetcher' +import { isSsoConnection } from '../../auth/connection' +import { Auth } from '../../auth' export const defaultRegion = 'us-east-1' export const defaultPartition = 'aws' @@ -90,27 +91,35 @@ export class RegionProvider { } /** - * @param node node on current command. * @returns heuristic for default region based on - * last touched region in explorer, wizard response, and node passed in. + * last touched region in auth, explorer, wizard response. */ - public guessDefaultRegion(node?: AWSTreeNodeBase): string { - const explorerRegions = this.getExplorerRegions() + public guessDefaultRegion(): string | undefined { + const conn = Auth.instance.activeConnection + if (isAmazonQ() && isSsoConnection(conn)) { + // Only the current auth region makes sense for Amazon Q use cases. + return conn.ssoRegion + } + + if (conn?.type === 'sso') { + return conn.ssoRegion + } - if (node?.regionCode) { - return node.regionCode - } else if (explorerRegions.length === 1) { + const explorerRegions = this.getExplorerRegions() + if (explorerRegions.length === 1) { return explorerRegions[0] - } else if (this.lastTouchedRegion) { + } + + if (this.lastTouchedRegion) { return this.lastTouchedRegion - } else { - const lastWizardResponse = this.globalState.get('lastSelectedRegion') - if (lastWizardResponse && lastWizardResponse.id) { - return lastWizardResponse.id - } else { - return this.defaultRegionId - } } + + const lastWizardResponse = this.globalState.get('lastSelectedRegion') + if (lastWizardResponse && lastWizardResponse.id) { + return lastWizardResponse.id + } + + return undefined } public setLastTouchedRegion(region: string | undefined) { diff --git a/packages/core/src/shared/telemetry/telemetryService.ts b/packages/core/src/shared/telemetry/telemetryService.ts index 0b1058fbd4..6799190f56 100644 --- a/packages/core/src/shared/telemetry/telemetryService.ts +++ b/packages/core/src/shared/telemetry/telemetryService.ts @@ -275,7 +275,8 @@ export class DefaultTelemetryService { commonMetadata.push({ Key: computeRegionKey, Value: this.computeRegion }) } if (!event?.Metadata?.some((m: any) => m?.Key === regionKey)) { - commonMetadata.push({ Key: regionKey, Value: globals.regionProvider.guessDefaultRegion() }) + const guessedRegion = globals.regionProvider.guessDefaultRegion() + commonMetadata.push({ Key: regionKey, Value: guessedRegion ?? AccountStatus.NotSet }) } if (event.Metadata) { diff --git a/packages/core/src/shared/telemetry/vscodeTelemetry.json b/packages/core/src/shared/telemetry/vscodeTelemetry.json index 4e5e1e5f93..295f7c1568 100644 --- a/packages/core/src/shared/telemetry/vscodeTelemetry.json +++ b/packages/core/src/shared/telemetry/vscodeTelemetry.json @@ -363,7 +363,7 @@ "description": "Comma-delimited list of scopes that user has." }, { - "name": "region", + "name": "awsRegion", "type": "string", "description": "An AWS region." } @@ -1249,7 +1249,7 @@ "required": false }, { - "type": "region", + "type": "awsRegion", "required": false }, { diff --git a/packages/core/src/shared/ui/common/regionSubmenu.ts b/packages/core/src/shared/ui/common/regionSubmenu.ts index 2f6d3ed4bf..fde9f0545e 100644 --- a/packages/core/src/shared/ui/common/regionSubmenu.ts +++ b/packages/core/src/shared/ui/common/regionSubmenu.ts @@ -28,7 +28,7 @@ export class RegionSubmenu extends Prompter> { private readonly dataOptions?: ExtendedQuickPickOptions, private readonly regionOptions?: ExtendedQuickPickOptions, private readonly separatorLabel: string = 'Items', - private currentRegion = globals.regionProvider.guessDefaultRegion() + private currentRegion = globals.regionProvider.guessDefaultRegion() ?? globals.regionProvider.defaultRegionId ) { super() } diff --git a/packages/core/src/test/shared/regions/regionProvider.test.ts b/packages/core/src/test/shared/regions/regionProvider.test.ts index 6ab23343b9..3448b09023 100644 --- a/packages/core/src/test/shared/regions/regionProvider.test.ts +++ b/packages/core/src/test/shared/regions/regionProvider.test.ts @@ -5,10 +5,13 @@ import assert from 'assert' import { RegionProvider } from '../../../shared/regions/regionProvider' -import { AWSTreeNodeBase } from '../../../shared/treeview/nodes/awsTreeNodeBase' import { createRegionPrompter } from '../../../shared/ui/common/region' import { FakeMemento } from '../../fakeExtensionContext' import { createQuickPickPrompterTester } from '../ui/testUtils' +import { createSsoProfile, createTestAuth } from '../../credentials/testUtil' +import { Auth } from '../../../auth/auth' +import * as extUtils from '../../../shared/extensionUtilities' +import sinon from 'sinon' const regionCode = 'someRegion' const serviceId = 'someService' @@ -162,6 +165,10 @@ describe('RegionProvider', async function () { }) describe('guessDefaultRegion', function () { + afterEach(() => { + sinon.restore() + }) + it('sets default region to last region from prompter', async function () { const regionProvider = new RegionProvider() const originalRegion = regionProvider.guessDefaultRegion() @@ -198,19 +205,28 @@ describe('RegionProvider', async function () { assert.strictEqual(regionProvider.guessDefaultRegion(), 'us-east-2') }) - it('chooses AWS node region when more than one exists in explorer', async function () { + it('returns undefined when unable to determine last used region', function () { const regionProvider = new RegionProvider(endpoints, new FakeMemento()) - await regionProvider.updateExplorerRegions(['us-east-1', 'us-east-2']) - regionProvider.setLastTouchedRegion('us-west-1') + assert.strictEqual(regionProvider.guessDefaultRegion(), undefined) + }) + + it('returns undefined when no active amazon Q connection', function () { + const regionProvider = new RegionProvider(endpoints, new FakeMemento()) + sinon.stub(extUtils, 'isAmazonQ').returns(true) + + assert.strictEqual(regionProvider.guessDefaultRegion(), undefined) + }) + + it('returns connection region with active amazon Q connection', async function () { + const region = 'us-west-2' + const regionProvider = new RegionProvider(endpoints, new FakeMemento()) + const auth = createTestAuth() + await auth.useConnection(await auth.createConnection(createSsoProfile({ ssoRegion: region }))) - const node = new (class extends AWSTreeNodeBase { - public override readonly regionCode = 'us-west-2' - public constructor() { - super('') - } - })() + sinon.stub(Auth, 'instance').value(auth) + sinon.stub(extUtils, 'isAmazonQ').returns(true) - assert.strictEqual(regionProvider.guessDefaultRegion(node), 'us-west-2') + assert.strictEqual(regionProvider.guessDefaultRegion(), region) }) }) })