Skip to content

Commit c267457

Browse files
authored
fix(amazonq): Prevent customization override if user has manually selected a customization
## Problem If a user was part of an AB group with a customization override, they could only ever use the customization from the override regardless of personal choice. ## Solution Changes the logic for when a customization will be overridden if the user is part of an AB group that has a customization override. With this change, if the user has manually selected a customization, it will not be overridden by the AB customization.
1 parent 31200df commit c267457

File tree

4 files changed

+121
-15
lines changed

4 files changed

+121
-15
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "User-selected customizations are sometimes not being persisted."
4+
}

packages/core/src/codewhisperer/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type {
2626
SendTelemetryEventResponse,
2727
TelemetryEvent,
2828
InlineChatEvent,
29+
Customization,
2930
} from './client/codewhispereruserclient.d.ts'
3031
export type { default as CodeWhispererUserClient } from './client/codewhispereruserclient.d.ts'
3132
export { SecurityPanelViewProvider } from './views/securityPanelViewProvider'
@@ -98,6 +99,6 @@ export * as diagnosticsProvider from './service/diagnosticsProvider'
9899
export * from './ui/codeWhispererNodes'
99100
export { SecurityScanError } from '../codewhisperer/models/errors'
100101
export * as CodeWhispererConstants from '../codewhisperer/models/constants'
101-
export { getSelectedCustomization } from './util/customizationUtil'
102+
export { getSelectedCustomization, setSelectedCustomization, baseCustomization } from './util/customizationUtil'
102103
export { Container } from './service/serviceContainer'
103104
export * from './util/gitUtil'

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

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ export const baseCustomization = {
9191
),
9292
}
9393

94+
/**
95+
* Gets the customization that should be used for user requests. If a user has manually selected
96+
* a customization, always respect that choice. If not, check if the user is part of an AB
97+
* group assigned a specific customization. If so, use that customization. If not, use the
98+
* base customization.
99+
*/
94100
export const getSelectedCustomization = (): Customization => {
95101
if (
96102
!AuthUtil.instance.isCustomizationFeatureEnabled ||
@@ -105,21 +111,22 @@ export const getSelectedCustomization = (): Customization => {
105111
Object,
106112
{}
107113
)
108-
const result = selectedCustomizationArr[AuthUtil.instance.conn.label] || baseCustomization
109-
110-
// A/B case
111-
const customizationFeature = FeatureConfigProvider.getFeature(Features.customizationArnOverride)
112-
const arnOverride = customizationFeature?.value.stringValue
113-
const customizationOverrideName = customizationFeature?.variation
114-
if (arnOverride === undefined || arnOverride === '') {
115-
return result
114+
const selectedCustomization = selectedCustomizationArr[AuthUtil.instance.conn.label]
115+
116+
if (selectedCustomization && selectedCustomization.name !== baseCustomization.name) {
117+
return selectedCustomization
116118
} else {
117-
// A trick to prioritize arn from A/B over user's currently selected(for request and telemetry)
118-
// but still shows customization info of user's currently selected.
119-
return {
120-
arn: arnOverride,
121-
name: customizationOverrideName,
122-
description: result.description,
119+
const customizationFeature = FeatureConfigProvider.getFeature(Features.customizationArnOverride)
120+
const arnOverride = customizationFeature?.value.stringValue
121+
const customizationOverrideName = customizationFeature?.variation
122+
if (arnOverride === undefined) {
123+
return baseCustomization
124+
} else {
125+
return {
126+
arn: arnOverride,
127+
name: customizationOverrideName,
128+
description: baseCustomization.description,
129+
}
123130
}
124131
}
125132
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as sinon from 'sinon'
7+
import assert from 'assert'
8+
import { tryRegister } from '../testUtil'
9+
import {
10+
amazonQScopes,
11+
AuthUtil,
12+
baseCustomization,
13+
Customization,
14+
FeatureConfigProvider,
15+
getSelectedCustomization,
16+
refreshStatusBar,
17+
setSelectedCustomization,
18+
} from '../../codewhisperer'
19+
import { FeatureContext, globals } from '../../shared'
20+
import { resetCodeWhispererGlobalVariables } from '../codewhisperer/testUtil'
21+
import { createSsoProfile, createTestAuth } from '../credentials/testUtil'
22+
import { SsoConnection } from '../../auth'
23+
24+
const enterpriseSsoStartUrl = 'https://enterprise.awsapps.com/start'
25+
26+
describe('CodeWhisperer-customizationUtils', function () {
27+
let auth: ReturnType<typeof createTestAuth>
28+
let ssoConn: SsoConnection
29+
let featureCustomization: FeatureContext
30+
31+
before(async function () {
32+
createTestAuth(globals.globalState)
33+
tryRegister(refreshStatusBar)
34+
})
35+
36+
beforeEach(async function () {
37+
auth = createTestAuth(globals.globalState)
38+
ssoConn = await auth.createInvalidSsoConnection(
39+
createSsoProfile({ startUrl: enterpriseSsoStartUrl, scopes: amazonQScopes })
40+
)
41+
featureCustomization = {
42+
name: 'featureCustomizationName',
43+
value: {
44+
stringValue: 'featureCustomizationArn',
45+
},
46+
variation: 'featureCustomizationName',
47+
}
48+
sinon.stub(FeatureConfigProvider, 'getFeature').returns(featureCustomization)
49+
50+
sinon.stub(AuthUtil.instance, 'isConnectionExpired').returns(false)
51+
sinon.stub(AuthUtil.instance, 'isConnected').returns(true)
52+
sinon.stub(AuthUtil.instance, 'isCustomizationFeatureEnabled').value(true)
53+
sinon.stub(AuthUtil.instance, 'conn').value(ssoConn)
54+
55+
await resetCodeWhispererGlobalVariables()
56+
})
57+
58+
afterEach(function () {
59+
sinon.restore()
60+
})
61+
62+
it('Returns baseCustomization when not SSO', async function () {
63+
sinon.stub(AuthUtil.instance, 'isValidEnterpriseSsoInUse').returns(false)
64+
const customization = getSelectedCustomization()
65+
66+
assert.strictEqual(customization.name, baseCustomization.name)
67+
})
68+
69+
it('Returns selectedCustomization when customization manually selected', async function () {
70+
sinon.stub(AuthUtil.instance, 'isValidEnterpriseSsoInUse').returns(true)
71+
72+
const selectedCustomization: Customization = {
73+
arn: 'selectedCustomizationArn',
74+
name: 'selectedCustomizationName',
75+
description: 'selectedCustomizationDescription',
76+
}
77+
78+
await setSelectedCustomization(selectedCustomization)
79+
80+
const actualCustomization = getSelectedCustomization()
81+
82+
assert.strictEqual(actualCustomization.name, selectedCustomization.name)
83+
})
84+
85+
it('Returns AB customization', async function () {
86+
sinon.stub(AuthUtil.instance, 'isValidEnterpriseSsoInUse').returns(true)
87+
88+
await setSelectedCustomization(baseCustomization)
89+
90+
const returnedCustomization = getSelectedCustomization()
91+
92+
assert.strictEqual(returnedCustomization.name, featureCustomization.name)
93+
})
94+
})

0 commit comments

Comments
 (0)