Skip to content

Commit b179b37

Browse files
authored
feat(amazonq): implement configuration/didChangeConfiguration message for data sharing (#5441)
1 parent 262a6a8 commit b179b37

File tree

7 files changed

+138
-0
lines changed

7 files changed

+138
-0
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,22 @@ import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
1313
import com.intellij.testFramework.replaceService
1414
import com.intellij.testFramework.runInEdtAndWait
1515
import com.intellij.util.xmlb.XmlSerializer
16+
import io.mockk.every
17+
import io.mockk.junit4.MockKRule
18+
import io.mockk.mockkObject
1619
import org.assertj.core.api.Assertions.assertThat
1720
import org.jdom.output.XMLOutputter
1821
import org.junit.Before
1922
import org.junit.Ignore
23+
import org.junit.Rule
2024
import org.junit.Test
2125
import org.mockito.kotlin.any
2226
import org.mockito.kotlin.never
2327
import org.mockito.kotlin.spy
2428
import org.mockito.kotlin.verify
2529
import org.mockito.kotlin.whenever
2630
import software.aws.toolkits.jetbrains.core.ToolWindowHeadlessManagerImpl
31+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
2732
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererLoginType
2833
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExploreActionState
2934
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.isCodeWhispererEnabled
@@ -40,6 +45,9 @@ class CodeWhispererSettingsTest : CodeWhispererTestBase() {
4045
private lateinit var codewhispererServiceSpy: CodeWhispererService
4146
private lateinit var toolWindowHeadlessManager: ToolWindowHeadlessManagerImpl
4247

48+
@get:Rule
49+
val mockkRule = MockKRule(this)
50+
4351
@Before
4452
override fun setUp() {
4553
super.setUp()
@@ -212,6 +220,18 @@ class CodeWhispererSettingsTest : CodeWhispererTestBase() {
212220
assertThat(actual.autoBuildSetting).hasSize(1)
213221
assertThat(actual.autoBuildSetting["project1"]).isTrue()
214222
}
223+
224+
@Test
225+
fun `toggleMetricOptIn should trigger LSP didChangeConfiguration`() {
226+
mockkObject(AmazonQLspService)
227+
every { AmazonQLspService.didChangeConfiguration(any()) } returns Unit
228+
settingsManager.toggleMetricOptIn(true)
229+
settingsManager.toggleMetricOptIn(false)
230+
231+
io.mockk.verify(atLeast = 2) {
232+
AmazonQLspService.didChangeConfiguration(any())
233+
}
234+
}
215235
}
216236

217237
class CodeWhispererSettingUnitTest {

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.eclipse.lsp4j.PublishDiagnosticsParams
1212
import org.eclipse.lsp4j.ShowMessageRequestParams
1313
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.ConnectionMetadata
1414
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.SsoProfileData
15+
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings
1516
import java.util.concurrent.CompletableFuture
1617

1718
/**
@@ -58,6 +59,17 @@ class AmazonQLanguageClientImpl : AmazonQLanguageClient {
5859

5960
return CompletableFuture.completedFuture(
6061
buildList {
62+
params.items.forEach {
63+
when (it.section) {
64+
AmazonQLspConstants.LSP_CW_CONFIGURATION_KEY -> {
65+
add(
66+
CodeWhispererLspConfiguration(
67+
shouldShareData = CodeWhispererSettings.getInstance().isMetricOptIn()
68+
)
69+
)
70+
}
71+
}
72+
}
6173
}
6274
)
6375
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
5+
6+
object AmazonQLspConstants {
7+
const val LSP_CW_CONFIGURATION_KEY = "aws.codeWhisperer"
8+
const val LSP_CW_OPT_OUT_KEY = "shareCodeWhispererContentWithAWS"
9+
}

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import kotlinx.coroutines.sync.withLock
2929
import kotlinx.coroutines.withTimeout
3030
import org.eclipse.lsp4j.ClientCapabilities
3131
import org.eclipse.lsp4j.ClientInfo
32+
import org.eclipse.lsp4j.DidChangeConfigurationParams
3233
import org.eclipse.lsp4j.FileOperationsWorkspaceCapabilities
3334
import org.eclipse.lsp4j.InitializeParams
3435
import org.eclipse.lsp4j.InitializeResult
@@ -178,6 +179,12 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS
178179

179180
fun <T> executeIfRunning(project: Project, runnable: AmazonQLspService.(AmazonQLanguageServer) -> T): T? =
180181
project.serviceIfCreated<AmazonQLspService>()?.executeSync(runnable)
182+
183+
fun didChangeConfiguration(project: Project) {
184+
executeIfRunning(project) {
185+
it.workspaceService.didChangeConfiguration(DidChangeConfigurationParams())
186+
}
187+
}
181188
}
182189
}
183190

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
5+
6+
import com.google.gson.annotations.SerializedName
7+
8+
data class CodeWhispererLspConfiguration(
9+
@SerializedName(AmazonQLspConstants.LSP_CW_OPT_OUT_KEY)
10+
val shouldShareData: Boolean? = null,
11+
)

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import com.intellij.openapi.components.Service
1010
import com.intellij.openapi.components.State
1111
import com.intellij.openapi.components.Storage
1212
import com.intellij.openapi.components.service
13+
import com.intellij.openapi.project.ProjectManager
1314
import com.intellij.util.xmlb.annotations.Property
15+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
1416
import software.aws.toolkits.jetbrains.utils.notifyInfo
1517
import software.aws.toolkits.resources.AmazonQBundle
1618

@@ -49,6 +51,13 @@ class CodeWhispererSettings : PersistentStateComponent<CodeWhispererConfiguratio
4951

5052
fun toggleMetricOptIn(value: Boolean) {
5153
state.value[CodeWhispererConfigurationType.OptInSendingMetric] = value
54+
ProjectManager.getInstance().openProjects.forEach {
55+
if (it.isDisposed) {
56+
return@forEach
57+
}
58+
59+
AmazonQLspService.didChangeConfiguration(it)
60+
}
5261
}
5362

5463
fun isMetricOptIn() = state.value.getOrDefault(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
@file:Suppress("BannedImports")
4+
5+
package software.aws.toolkits.jetbrains.services.amazonq.lsp
6+
7+
import com.google.gson.Gson
8+
import com.intellij.testFramework.ApplicationExtension
9+
import org.assertj.core.api.Assertions.assertThat
10+
import org.eclipse.lsp4j.ConfigurationItem
11+
import org.eclipse.lsp4j.ConfigurationParams
12+
import org.junit.jupiter.api.Test
13+
import org.junit.jupiter.api.extension.ExtendWith
14+
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings
15+
16+
@ExtendWith(ApplicationExtension::class)
17+
class AmazonQLanguageClientImplTest {
18+
private val sut = AmazonQLanguageClientImpl()
19+
20+
@Test
21+
fun `configuration null if no attributes requested`() {
22+
assertThat(sut.configuration(configurationParams()).get()).isNull()
23+
}
24+
25+
@Test
26+
fun `configuration for codeWhisperer respects opt-out`() {
27+
CodeWhispererSettings.getInstance().toggleMetricOptIn(false)
28+
assertThat(sut.configuration(configurationParams("aws.codeWhisperer")).get())
29+
.singleElement()
30+
.isEqualTo(
31+
CodeWhispererLspConfiguration(shouldShareData = false)
32+
)
33+
}
34+
35+
@Test
36+
fun `configuration for codeWhisperer respects opt-in`() {
37+
CodeWhispererSettings.getInstance().toggleMetricOptIn(true)
38+
assertThat(sut.configuration(configurationParams("aws.codeWhisperer")).get())
39+
.singleElement()
40+
.isEqualTo(
41+
CodeWhispererLspConfiguration(shouldShareData = true)
42+
)
43+
}
44+
45+
@Test
46+
fun `configuration empty if attributes unknown`() {
47+
CodeWhispererSettings.getInstance().toggleMetricOptIn(true)
48+
assertThat(sut.configuration(configurationParams("something random")).get()).isEmpty()
49+
}
50+
51+
@Test
52+
fun `Gson serializes CodeWhispererLspConfiguration serializes correctly`() {
53+
val sut = CodeWhispererLspConfiguration(shouldShareData = true)
54+
assertThat(Gson().toJson(sut)).isEqualToIgnoringWhitespace(
55+
"""
56+
{
57+
"shareCodeWhispererContentWithAWS": true
58+
}
59+
""".trimIndent()
60+
)
61+
}
62+
63+
private fun configurationParams(vararg attributes: String) = ConfigurationParams(
64+
attributes.map {
65+
ConfigurationItem().apply {
66+
section = it
67+
}
68+
}
69+
)
70+
}

0 commit comments

Comments
 (0)