Skip to content

Commit 9d7f445

Browse files
authored
fix(amazonq): revert 7060 show customization across profiles (#7129)
Due to throttling issue we already encounter now in PROD, we decided to revert it back and fix the throttling issue first then bring this back afterward. This reverts commit 70ba83f. ## Problem ## Solution --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 70ba83f commit 9d7f445

File tree

9 files changed

+90
-249
lines changed

9 files changed

+90
-249
lines changed

packages/amazonq/.changes/next-release/Feature-2a696d00-e8c8-44a4-ab2c-2204b4d8e31d.json

Lines changed: 0 additions & 4 deletions
This file was deleted.

packages/amazonq/test/unit/codewhisperer/region/regionProfileManager.test.ts

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('RegionProfileManager', function () {
6565
const mockClient = {
6666
listAvailableProfiles: listProfilesStub,
6767
}
68-
const createClientStub = sinon.stub(sut, '_createQClient').resolves(mockClient)
68+
const createClientStub = sinon.stub(sut, 'createQClient').resolves(mockClient)
6969

7070
const r = await sut.listRegionProfile()
7171

@@ -234,65 +234,13 @@ describe('RegionProfileManager', function () {
234234
})
235235

236236
describe('createQClient', function () {
237-
it(`should configure the endpoint and region from a profile`, async function () {
238-
await setupConnection('idc')
239-
240-
const iadClient = await sut.createQClient({
241-
name: 'foo',
242-
region: 'us-east-1',
243-
arn: 'arn',
244-
description: 'description',
245-
})
246-
247-
assert.deepStrictEqual(iadClient.config.region, 'us-east-1')
248-
assert.deepStrictEqual(iadClient.endpoint.href, 'https://q.us-east-1.amazonaws.com/')
249-
250-
const fraClient = await sut.createQClient({
251-
name: 'bar',
252-
region: 'eu-central-1',
253-
arn: 'arn',
254-
description: 'description',
255-
})
256-
257-
assert.deepStrictEqual(fraClient.config.region, 'eu-central-1')
258-
assert.deepStrictEqual(fraClient.endpoint.href, 'https://q.eu-central-1.amazonaws.com/')
259-
})
260-
261-
it(`should throw if the region is not supported or recognizable by Q`, async function () {
262-
await setupConnection('idc')
263-
264-
await assert.rejects(
265-
async () => {
266-
await sut.createQClient({
267-
name: 'foo',
268-
region: 'ap-east-1',
269-
arn: 'arn',
270-
description: 'description',
271-
})
272-
},
273-
{ message: /trying to initiatize Q client with unrecognizable region/ }
274-
)
275-
276-
await assert.rejects(
277-
async () => {
278-
await sut.createQClient({
279-
name: 'foo',
280-
region: 'unknown-somewhere',
281-
arn: 'arn',
282-
description: 'description',
283-
})
284-
},
285-
{ message: /trying to initiatize Q client with unrecognizable region/ }
286-
)
287-
})
288-
289237
it(`should configure the endpoint and region correspondingly`, async function () {
290238
await setupConnection('idc')
291239
await sut.switchRegionProfile(profileFoo, 'user')
292240
assert.deepStrictEqual(sut.activeRegionProfile, profileFoo)
293241
const conn = authUtil.conn as SsoConnection
294242

295-
const client = await sut._createQClient('eu-central-1', 'https://amazon.com/', conn)
243+
const client = await sut.createQClient('eu-central-1', 'https://amazon.com/', conn)
296244

297245
assert.deepStrictEqual(client.config.region, 'eu-central-1')
298246
assert.deepStrictEqual(client.endpoint.href, 'https://amazon.com/')

packages/core/src/codewhisperer/activation.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@ import { AuthUtil } from './util/authUtil'
7272
import { ImportAdderProvider } from './service/importAdderProvider'
7373
import { TelemetryHelper } from './util/telemetryHelper'
7474
import { openUrl } from '../shared/utilities/vsCodeUtils'
75-
import { notifyNewCustomizations, onProfileChangedListener } from './util/customizationUtil'
75+
import {
76+
getAvailableCustomizationsList,
77+
getSelectedCustomization,
78+
notifyNewCustomizations,
79+
switchToBaseCustomizationAndNotify,
80+
} from './util/customizationUtil'
7681
import { CodeWhispererCommandBackend, CodeWhispererCommandDeclarations } from './commands/gettingStartedPageCommands'
7782
import { SecurityIssueHoverProvider } from './service/securityIssueHoverProvider'
7883
import { SecurityIssueCodeActionProvider } from './service/securityIssueCodeActionProvider'
@@ -338,7 +343,26 @@ export async function activate(context: ExtContext): Promise<void> {
338343
SecurityIssueCodeActionProvider.instance
339344
),
340345
vscode.commands.registerCommand('aws.amazonq.openEditorAtRange', openEditorAtRange),
341-
auth.regionProfileManager.onDidChangeRegionProfile(onProfileChangedListener)
346+
auth.regionProfileManager.onDidChangeRegionProfile(() => {
347+
// Validate user still has access to the selected customization.
348+
const selectedCustomization = getSelectedCustomization()
349+
// No need to validate base customization which has empty arn.
350+
if (selectedCustomization.arn.length > 0) {
351+
getAvailableCustomizationsList()
352+
.then(async (customizations) => {
353+
const r = customizations.find((it) => it.arn === selectedCustomization.arn)
354+
if (!r) {
355+
await switchToBaseCustomizationAndNotify()
356+
}
357+
})
358+
.catch((e) => {
359+
getLogger().error(
360+
`encounter error while validating selected customization on profile change: %s`,
361+
(e as Error).message
362+
)
363+
})
364+
}
365+
})
342366
)
343367

344368
// run the auth startup code with context for telemetry

packages/core/src/codewhisperer/client/codewhisperer.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ import { AWSError, Credentials, Service } from 'aws-sdk'
77
import globals from '../../shared/extensionGlobals'
88
import * as CodeWhispererClient from './codewhispererclient'
99
import * as CodeWhispererUserClient from './codewhispereruserclient'
10-
import { SendTelemetryEventRequest } from './codewhispereruserclient'
10+
import { ListAvailableCustomizationsResponse, SendTelemetryEventRequest } from './codewhispereruserclient'
1111
import { ServiceOptions } from '../../shared/awsClientBuilder'
1212
import { hasVendedIamCredentials } from '../../auth/auth'
1313
import { CodeWhispererSettings } from '../util/codewhispererSettings'
1414
import { PromiseResult } from 'aws-sdk/lib/request'
1515
import { AuthUtil } from '../util/authUtil'
1616
import { isSsoConnection } from '../../auth/connection'
17+
import { pageableToCollection } from '../../shared/utilities/collectionUtils'
1718
import apiConfig = require('./service-2.json')
1819
import userApiConfig = require('./user-service-2.json')
1920
import { session } from '../util/codeWhispererSession'
2021
import { getLogger } from '../../shared/logger/logger'
22+
import { indent } from '../../shared/utilities/textUtilities'
2123
import { getClientId, getOptOutPreference, getOperatingSystem } from '../../shared/telemetry/util'
2224
import { extensionVersion, getServiceEnvVarConfig } from '../../shared/vscode/env'
2325
import { DevSettings } from '../../shared/settings'
@@ -217,6 +219,28 @@ export class DefaultCodeWhispererClient {
217219
.promise()
218220
}
219221

222+
public async listAvailableCustomizations(): Promise<ListAvailableCustomizationsResponse[]> {
223+
const client = await this.createUserSdkClient()
224+
const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile
225+
const requester = async (request: CodeWhispererUserClient.ListAvailableCustomizationsRequest) =>
226+
client.listAvailableCustomizations(request).promise()
227+
return pageableToCollection(requester, { profileArn: profile?.arn }, 'nextToken')
228+
.promise()
229+
.then((resps) => {
230+
let logStr = 'amazonq: listAvailableCustomizations API request:'
231+
for (const resp of resps) {
232+
const requestId = resp.$response.requestId
233+
logStr += `\n${indent('RequestID: ', 4)}${requestId},\n${indent('Customizations:', 4)}`
234+
for (const [index, c] of resp.customizations.entries()) {
235+
const entry = `${index.toString().padStart(2, '0')}: ${c.name?.trim()}`
236+
logStr += `\n${indent(entry, 8)}`
237+
}
238+
}
239+
getLogger().debug(logStr)
240+
return resps
241+
})
242+
}
243+
220244
public async sendTelemetryEvent(request: SendTelemetryEventRequest) {
221245
const requestWithCommonFields: SendTelemetryEventRequest = {
222246
...request,

packages/core/src/codewhisperer/index.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,7 @@ export * as diagnosticsProvider from './service/diagnosticsProvider'
9999
export * from './ui/codeWhispererNodes'
100100
export { SecurityScanError, SecurityScanTimedOutError } from '../codewhisperer/models/errors'
101101
export * as CodeWhispererConstants from '../codewhisperer/models/constants'
102-
export {
103-
getSelectedCustomization,
104-
setSelectedCustomization,
105-
baseCustomization,
106-
onProfileChangedListener,
107-
CustomizationProvider,
108-
} from './util/customizationUtil'
102+
export { getSelectedCustomization, setSelectedCustomization, baseCustomization } from './util/customizationUtil'
109103
export { Container } from './service/serviceContainer'
110104
export * from './util/gitUtil'
111105
export * from './ui/prompters'

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

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,12 @@ const endpoints = createConstantMap({
4747
* 'update' -> plugin auto select the profile on users' behalf as there is only 1 profile
4848
* 'reload' -> on plugin restart, plugin will try to reload previous selected profile
4949
*/
50-
export type ProfileSwitchIntent = 'user' | 'auth' | 'update' | 'reload' | 'customization'
51-
52-
export type ProfileChangedEvent = {
53-
profile: RegionProfile | undefined
54-
intent: ProfileSwitchIntent
55-
}
50+
export type ProfileSwitchIntent = 'user' | 'auth' | 'update' | 'reload'
5651

5752
export class RegionProfileManager {
5853
private static logger = getLogger()
5954
private _activeRegionProfile: RegionProfile | undefined
60-
private _onDidChangeRegionProfile = new vscode.EventEmitter<ProfileChangedEvent>()
55+
private _onDidChangeRegionProfile = new vscode.EventEmitter<RegionProfile | undefined>()
6156
public readonly onDidChangeRegionProfile = this._onDidChangeRegionProfile.event
6257

6358
// Store the last API results (for UI propuse) so we don't need to call service again if doesn't require "latest" result
@@ -117,7 +112,7 @@ export class RegionProfileManager {
117112
}
118113
const availableProfiles: RegionProfile[] = []
119114
for (const [region, endpoint] of endpoints.entries()) {
120-
const client = await this._createQClient(region, endpoint, conn as SsoConnection)
115+
const client = await this.createQClient(region, endpoint, conn as SsoConnection)
121116
const requester = async (request: CodeWhispererUserClient.ListAvailableProfilesRequest) =>
122117
client.listAvailableProfiles(request).promise()
123118
const request: CodeWhispererUserClient.ListAvailableProfilesRequest = {}
@@ -167,7 +162,7 @@ export class RegionProfileManager {
167162
const ssoConn = this.connectionProvider() as SsoConnection
168163

169164
// only prompt to users when users switch from A profile to B profile
170-
if (source !== 'customization' && this.activeRegionProfile !== undefined && regionProfile !== undefined) {
165+
if (this.activeRegionProfile !== undefined && regionProfile !== undefined) {
171166
const response = await showConfirmationMessage({
172167
prompt: localize(
173168
'AWS.amazonq.profile.confirmation',
@@ -209,16 +204,13 @@ export class RegionProfileManager {
209204
})
210205
}
211206

212-
await this._switchRegionProfile(regionProfile, source)
207+
await this._switchRegionProfile(regionProfile)
213208
}
214209

215-
private async _switchRegionProfile(regionProfile: RegionProfile | undefined, source: ProfileSwitchIntent) {
210+
private async _switchRegionProfile(regionProfile: RegionProfile | undefined) {
216211
this._activeRegionProfile = regionProfile
217212

218-
this._onDidChangeRegionProfile.fire({
219-
profile: regionProfile,
220-
intent: source,
221-
})
213+
this._onDidChangeRegionProfile.fire(regionProfile)
222214
// dont show if it's a default (fallback)
223215
if (regionProfile && this.profiles.length > 1) {
224216
void vscode.window.showInformationMessage(`You are using the ${regionProfile.name} profile for Q.`).then()
@@ -351,21 +343,7 @@ export class RegionProfileManager {
351343
}
352344
}
353345

354-
// TODO: Should maintain sdk client in a better way
355-
async createQClient(profile: RegionProfile): Promise<CodeWhispererUserClient> {
356-
const conn = this.connectionProvider()
357-
if (conn === undefined || !isSsoConnection(conn)) {
358-
throw new Error('No valid SSO connection')
359-
}
360-
const endpoint = endpoints.get(profile.region)
361-
if (!endpoint) {
362-
throw new Error(`trying to initiatize Q client with unrecognizable region ${profile.region}`)
363-
}
364-
return this._createQClient(profile.region, endpoint, conn)
365-
}
366-
367-
// Visible for testing only, do not use this directly, please use createQClient(profile)
368-
async _createQClient(region: string, endpoint: string, conn: SsoConnection): Promise<CodeWhispererUserClient> {
346+
async createQClient(region: string, endpoint: string, conn: SsoConnection): Promise<CodeWhispererUserClient> {
369347
const token = (await conn.getToken()).accessToken
370348
const serviceOption: ServiceOptions = {
371349
apiConfig: userApiConfig,

0 commit comments

Comments
 (0)