Skip to content

Commit 7eaa4b1

Browse files
authored
Merge branch 'main' into lang-exp
2 parents 64ef1f0 + 354dede commit 7eaa4b1

File tree

5 files changed

+80
-3
lines changed

5 files changed

+80
-3
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: Customization now resets with a warning if unavailable in the selected profile."
4+
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/customization/CodeWhispererModelConfigurator.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import software.aws.toolkits.core.utils.debug
2222
import software.aws.toolkits.core.utils.getLogger
2323
import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
2424
import software.aws.toolkits.jetbrains.services.amazonq.calculateIfIamIdentityCenterConnection
25+
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
26+
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener
2527
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
2628
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
2729
import software.aws.toolkits.jetbrains.utils.notifyInfo
@@ -48,6 +50,7 @@ private fun notifyInvalidSelectedCustomization(project: Project) {
4850
}
4951

5052
private fun notifyNewCustomization(project: Project) {
53+
if (ApplicationManager.getApplication().isUnitTestMode) return
5154
notifyInfo(
5255
title = message("codewhisperer.custom.dialog.title"),
5356
content = message("codewhisperer.notification.custom.new_customization"),
@@ -81,6 +84,18 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
8184

8285
private var customizationArnOverrideV2: String? = null
8386

87+
init {
88+
ApplicationManager.getApplication().messageBus.connect(this).subscribe(
89+
QRegionProfileSelectedListener.TOPIC,
90+
object : QRegionProfileSelectedListener {
91+
override fun onProfileSelected(project: Project, profile: QRegionProfile?) {
92+
pluginAwareExecuteOnPooledThread {
93+
CodeWhispererModelConfigurator.getInstance().listCustomizations(project, passive = true)
94+
}
95+
}
96+
}
97+
)
98+
}
8499
override fun showConfigDialog(project: Project) {
85100
runInEdt {
86101
calculateIfIamIdentityCenterConnection(project) {

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

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import com.intellij.openapi.application.ApplicationManager
77
import com.intellij.testFramework.ApplicationRule
88
import com.intellij.testFramework.DisposableRule
99
import com.intellij.testFramework.ProjectRule
10+
import com.intellij.testFramework.registerServiceInstance
1011
import com.intellij.testFramework.replaceService
1112
import com.intellij.util.xmlb.XmlSerializer
13+
import migration.software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator
1214
import org.assertj.core.api.Assertions.assertThat
1315
import org.jdom.output.XMLOutputter
1416
import org.junit.Before
@@ -40,10 +42,14 @@ import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
4042
import software.aws.toolkits.jetbrains.core.region.MockRegionProviderRule
4143
import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
4244
import software.aws.toolkits.jetbrains.services.amazonq.FeatureContext
45+
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener
46+
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
4347
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererCustomization
4448
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererCustomizationState
4549
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.DefaultCodeWhispererModelConfigurator
4650
import software.aws.toolkits.jetbrains.utils.xmlElement
51+
import java.util.concurrent.CountDownLatch
52+
import java.util.concurrent.TimeUnit
4753
import kotlin.reflect.full.memberProperties
4854
import kotlin.reflect.jvm.isAccessible
4955

@@ -75,6 +81,7 @@ class CodeWhispererModelConfiguratorTest {
7581
private lateinit var sut: DefaultCodeWhispererModelConfigurator
7682
private lateinit var mockClient: CodeWhispererRuntimeClient
7783
private lateinit var abManager: CodeWhispererFeatureConfigService
84+
private lateinit var mockClintAdaptor: CodeWhispererClientAdaptor
7885

7986
@Before
8087
fun setup() {
@@ -83,7 +90,11 @@ class CodeWhispererModelConfiguratorTest {
8390
regionProvider.addRegion(Region.US_EAST_1)
8491
regionProvider.addRegion(Region.US_EAST_2)
8592

86-
sut = DefaultCodeWhispererModelConfigurator()
93+
sut = spy(CodeWhispererModelConfigurator.getInstance() as DefaultCodeWhispererModelConfigurator).also { spyInstance ->
94+
ApplicationManager.getApplication().replaceService(
95+
DefaultCodeWhispererModelConfigurator::class.java, spyInstance, disposableRule.disposable
96+
)
97+
}
8798

8899
(ToolkitConnectionManager.getInstance(projectRule.project) as DefaultToolkitConnectionManager).loadState(ToolkitConnectionManagerState())
89100
mockClient.stub {
@@ -110,6 +121,9 @@ class CodeWhispererModelConfiguratorTest {
110121
abManager,
111122
disposableRule.disposable
112123
)
124+
125+
mockClintAdaptor = mock()
126+
projectRule.project.registerServiceInstance(CodeWhispererClientAdaptor::class.java, mockClintAdaptor)
113127
}
114128

115129
@Test
@@ -550,4 +564,48 @@ class CodeWhispererModelConfiguratorTest {
550564
assertThat(actual.previousAvailableCustomizations).hasSize(1)
551565
assertThat(actual.previousAvailableCustomizations["fake-sso-url"]).isEqualTo(listOf("arn_1", "arn_2", "arn_3"))
552566
}
567+
568+
@Test
569+
fun `profile switch should keep using existing customization if new list still contains that arn`() {
570+
val ssoConn = spy(LegacyManagedBearerSsoConnection(region = "us-east-1", startUrl = "url 1", scopes = Q_SCOPES))
571+
ToolkitConnectionManager.getInstance(projectRule.project).switchConnection(ssoConn)
572+
val oldCustomization = CodeWhispererCustomization("oldArn", "oldName", "oldDescription")
573+
sut.switchCustomization(projectRule.project, oldCustomization)
574+
575+
assertThat(sut.activeCustomization(projectRule.project)).isEqualTo(oldCustomization)
576+
577+
val fakeCustomizations = listOf(
578+
CodeWhispererCustomization("oldArn", "oldName", "oldDescription")
579+
)
580+
mockClintAdaptor.stub { on { listAvailableCustomizations() } doReturn fakeCustomizations }
581+
582+
ApplicationManager.getApplication().messageBus
583+
.syncPublisher(QRegionProfileSelectedListener.TOPIC)
584+
.onProfileSelected(projectRule.project, null)
585+
586+
assertThat(sut.activeCustomization(projectRule.project)).isEqualTo(oldCustomization)
587+
}
588+
589+
@Test
590+
fun `profile switch should invalidate obsolete customization if it's not in the new list`() {
591+
val ssoConn = spy(LegacyManagedBearerSsoConnection(region = "us-east-1", startUrl = "url 1", scopes = Q_SCOPES))
592+
ToolkitConnectionManager.getInstance(projectRule.project).switchConnection(ssoConn)
593+
val oldCustomization = CodeWhispererCustomization("oldArn", "oldName", "oldDescription")
594+
sut.switchCustomization(projectRule.project, oldCustomization)
595+
assertThat(sut.activeCustomization(projectRule.project)).isEqualTo(oldCustomization)
596+
val fakeCustomizations = listOf(
597+
CodeWhispererCustomization("newArn", "newName", "newDescription")
598+
)
599+
mockClintAdaptor.stub { on { listAvailableCustomizations() } doReturn fakeCustomizations }
600+
601+
val latch = CountDownLatch(1)
602+
603+
ApplicationManager.getApplication().messageBus
604+
.syncPublisher(QRegionProfileSelectedListener.TOPIC)
605+
.onProfileSelected(projectRule.project, null)
606+
607+
latch.await(2, TimeUnit.SECONDS)
608+
609+
assertThat(sut.activeCustomization(projectRule.project)).isNull()
610+
}
553611
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class QRegionProfileManager : PersistentStateComponent<QProfileState>, Disposabl
144144
}
145145
}
146146

147-
project.messageBus
147+
ApplicationManager.getApplication().messageBus
148148
.syncPublisher(QRegionProfileSelectedListener.TOPIC)
149149
.onProfileSelected(project, newProfile)
150150
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import com.intellij.util.messages.Topic
88

99
interface QRegionProfileSelectedListener {
1010
companion object {
11-
@Topic.ProjectLevel
11+
@Topic.AppLevel
1212
val TOPIC = Topic.create("QRegionProfileSelected", QRegionProfileSelectedListener::class.java)
1313
}
1414

0 commit comments

Comments
 (0)