Skip to content

Commit 0b592d4

Browse files
authored
fix(amazonq): don't reset profile config if validation fails to list profiles (#5635)
After user selects a profile, if subsequent validation fails because of networking / etc, then we incorrectly clear their selected profile
1 parent 9109943 commit 0b592d4

File tree

3 files changed

+48
-17
lines changed

3 files changed

+48
-17
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Amazon Q: Fix issue where context menu items are not available after re-opening projects or restarting the IDE"
4+
}

plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/QRegionProfileManagerTest.kt

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -191,20 +191,6 @@ class QRegionProfileManagerTest {
191191

192192
@Test
193193
fun `validateProfile should cross validate selected profile with latest API response for current project and remove it if its not longer accessible`() {
194-
val client = clientRule.create<CodeWhispererRuntimeClient>()
195-
val mockResponse: SdkIterable<Profile> = SdkIterable<Profile> {
196-
listOf(
197-
Profile.builder().profileName("foo").arn("foo-arn-v2").build(),
198-
Profile.builder().profileName("bar").arn("bar-arn").build(),
199-
).toMutableList().iterator()
200-
}
201-
val iterable: ListAvailableProfilesIterable = mock {
202-
on { it.profiles() } doReturn mockResponse
203-
}
204-
client.stub {
205-
onGeneric { listAvailableProfilesPaginator(any<Consumer<ListAvailableProfilesRequest.Builder>>()) } doReturn iterable
206-
}
207-
208194
val activeConn =
209195
ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance()) ?: fail("connection shouldn't be null")
210196
val anotherConn = authRule.createConnection(ManagedSsoProfile(ssoRegion = "us-east-1", startUrl = "anotherUrl", scopes = Q_SCOPES))
@@ -214,6 +200,15 @@ class QRegionProfileManagerTest {
214200
this.connectionIdToActiveProfile[activeConn.id] = fooProfile
215201
this.connectionIdToActiveProfile[anotherConn.id] = barProfile
216202
}
203+
resourceCache.addEntry(
204+
activeConn.getConnectionSettings(),
205+
QProfileResources.LIST_REGION_PROFILES,
206+
listOf(
207+
QRegionProfile("foo", "foo-arn-v2"),
208+
QRegionProfile("bar", "bar-arn"),
209+
)
210+
)
211+
217212
sut.loadState(state)
218213
assertThat(sut.activeProfile(project)).isEqualTo(fooProfile)
219214

@@ -222,6 +217,24 @@ class QRegionProfileManagerTest {
222217
assertThat(sut.state.connectionIdToActiveProfile).isEqualTo(mapOf(anotherConn.id to barProfile))
223218
}
224219

220+
@Test
221+
fun `validateProfile does not clear profile if profiles cannot be listed`() {
222+
val activeConn =
223+
ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance()) ?: fail("connection shouldn't be null")
224+
val anotherConn = authRule.createConnection(ManagedSsoProfile(ssoRegion = "us-east-1", startUrl = "anotherUrl", scopes = Q_SCOPES))
225+
val fooProfile = QRegionProfile("foo", "foo-arn")
226+
val barProfile = QRegionProfile("bar", "bar-arn")
227+
val state = QProfileState().apply {
228+
this.connectionIdToActiveProfile[activeConn.id] = fooProfile
229+
this.connectionIdToActiveProfile[anotherConn.id] = barProfile
230+
}
231+
sut.loadState(state)
232+
assertThat(sut.activeProfile(project)).isEqualTo(fooProfile)
233+
234+
sut.validateProfile(project)
235+
assertThat(sut.activeProfile(project)).isEqualTo(fooProfile)
236+
}
237+
225238
@Test
226239
fun `clientSettings should return the region Q profile specify`() {
227240
MockClientManager.useRealImplementations(disposableRule.disposable)

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileManager.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
1616
import com.intellij.util.xmlb.annotations.MapAnnotation
1717
import com.intellij.util.xmlb.annotations.Property
1818
import software.amazon.awssdk.core.SdkClient
19+
import software.amazon.awssdk.services.codewhispererruntime.model.AccessDeniedException
1920
import software.aws.toolkits.core.TokenConnectionSettings
2021
import software.aws.toolkits.core.utils.debug
2122
import software.aws.toolkits.core.utils.getLogger
22-
import software.aws.toolkits.core.utils.tryOrNull
2323
import software.aws.toolkits.core.utils.warn
2424
import software.aws.toolkits.jetbrains.core.AwsClientManager
2525
import software.aws.toolkits.jetbrains.core.AwsResourceCache
@@ -59,15 +59,28 @@ class QRegionProfileManager : PersistentStateComponent<QProfileState>, Disposabl
5959
)
6060
}
6161

62-
// should be call on project startup to validate if profile is still active
62+
/**
63+
* Called on project startup to validate if selected profile is still active
64+
*/
65+
@Deprecated("This is a giant hack and we are not handling all the cases")
6366
@RequiresBackgroundThread
6467
fun validateProfile(project: Project) {
6568
val conn = getIdcConnectionOrNull(project)
6669
val selected = activeProfile(project) ?: return
67-
val profiles = tryOrNull {
70+
val profiles = try {
6871
listRegionProfiles(project)
72+
} catch (e: Exception) {
73+
if (e is AccessDeniedException) {
74+
null
75+
} else {
76+
// if we can't list profiles assume it is valid
77+
LOG.warn { "Continuing with $selected since listAvailableProfiles failed" }
78+
return
79+
}
6980
}
7081

82+
// succeeded in listing profiles, but none match selected
83+
// profiles should be null if access denied or connection is not IdC
7184
if (profiles == null || profiles.none { it.arn == selected.arn }) {
7285
invalidateProfile(selected.arn)
7386
switchProfile(project, null, intent = QProfileSwitchIntent.Reload)
@@ -83,6 +96,7 @@ class QRegionProfileManager : PersistentStateComponent<QProfileState>, Disposabl
8396

8497
fun listRegionProfiles(project: Project): List<QRegionProfile>? {
8598
val connection = getIdcConnectionOrNull(project) ?: return null
99+
86100
return try {
87101
val connectionSettings = connection.getConnectionSettings()
88102
val mappedProfiles = AwsResourceCache.getInstance().getResourceNow(

0 commit comments

Comments
 (0)