Skip to content

Commit 16e9a04

Browse files
authored
Merge branch 'feature/q-lsp-chat' into rli/paidtier
2 parents 21b5d4a + ae4af35 commit 16e9a04

File tree

12 files changed

+95
-72
lines changed

12 files changed

+95
-72
lines changed

buildspec/linuxTests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ phases:
4040
fi
4141
4242
- chmod +x gradlew
43-
- su codebuild-user -c "./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME check coverageReport --info --console plain --continue"
43+
- su codebuild-user -c "./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME check coverageReport --info --stacktrace --console plain --continue"
4444
- su codebuild-user -c "./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME buildPlugin"
4545
- VCS_COMMIT_ID="${CODEBUILD_RESOLVED_SOURCE_VERSION}"
4646
- CI_BUILD_URL=$(echo $CODEBUILD_BUILD_URL | sed 's/#/%23/g') # Encode `#` in the URL because otherwise the url is clipped in the Codecov.io site

plugins/amazonq/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ val prepareBundledFlare by tasks.registering(Copy::class) {
115115

116116
val dest = layout.buildDirectory.dir("tmp/extractFlare")
117117
into(dest)
118-
119118
from(downloadFlareArtifacts.map { it.outputFiles.filterNot { file -> file.name.endsWith(".zip") } })
119+
120120
doLast {
121121
copy {
122122
into(dest)

plugins/amazonq/shared/jetbrains-community/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,12 @@ dependencies {
2424

2525
testFixturesApi(testFixtures(project(":plugin-core:jetbrains-community")))
2626
}
27+
28+
// hack because our test structure currently doesn't make complete sense
29+
tasks.prepareTestSandbox {
30+
val pluginXmlJar = project(":plugin-amazonq").tasks.jar
31+
32+
dependsOn(pluginXmlJar)
33+
intoChild(intellijPlatform.projectName.map { "$it/lib" })
34+
.from(pluginXmlJar)
35+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
88
import org.eclipse.lsp4j.services.LanguageClient
99
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LSPAny
1010
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_OPEN_TAB
11+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_OPTIONS_UPDATE_NOTIFICATION
1112
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_CONTEXT_COMMANDS
1213
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_UPDATE
1314
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CopyFileParams
@@ -67,4 +68,7 @@ interface AmazonQLanguageClient : LanguageClient {
6768

6869
@JsonNotification(DID_CREATE_DIRECTORY)
6970
fun createDirectory(params: FileParams)
71+
72+
@JsonNotification(CHAT_OPTIONS_UPDATE_NOTIFICATION)
73+
fun sendChatOptionsUpdate(params: LSPAny)
7074
}

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommun
4141
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage
4242
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LSPAny
4343
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_OPEN_TAB
44+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_OPTIONS_UPDATE_NOTIFICATION
4445
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_CONTEXT_COMMANDS
4546
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_UPDATE
4647
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CopyFileParams
@@ -149,14 +150,14 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
149150
return CompletableFuture.completedFuture(ShowDocumentResult(false))
150151
}
151152

152-
// The filepath sent by the server contains unicode characters which need to be
153-
// decoded for JB file handling APIs to be handle to handle file operations
154-
val fileToOpen = URLDecoder.decode(params.uri, StandardCharsets.UTF_8.name())
155153
if (params.external == true) {
156-
BrowserUtil.open(fileToOpen)
154+
BrowserUtil.open(params.uri)
157155
return CompletableFuture.completedFuture(ShowDocumentResult(true))
158156
}
159157

158+
// The filepath sent by the server contains unicode characters which need to be
159+
// decoded for JB file handling APIs to be handle to handle file operations
160+
val fileToOpen = URLDecoder.decode(params.uri, StandardCharsets.UTF_8.name())
160161
ApplicationManager.getApplication().invokeLater {
161162
try {
162163
val virtualFile = VirtualFileManager.getInstance().findFileByUrl(fileToOpen)
@@ -413,6 +414,16 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
413414
return refreshVfs(params.newPath)
414415
}
415416

417+
override fun sendChatOptionsUpdate(params: LSPAny) {
418+
val chatManager = ChatCommunicationManager.getInstance(project)
419+
chatManager.notifyUi(
420+
FlareUiMessage(
421+
command = CHAT_OPTIONS_UPDATE_NOTIFICATION,
422+
params = params,
423+
)
424+
)
425+
}
426+
416427
private fun refreshVfs(path: String) {
417428
val currPath = Paths.get(path)
418429
if (currPath.startsWith(localHistoryPath)) return

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,21 @@ private class AmazonQServerInstance(private val project: Project, private val cs
371371
// make assumption that all requests will resolve to the same CA
372372
// also terrible assumption that default endpoint is reachable
373373
val qUri = URI(QDefaultServiceConfig.ENDPOINT)
374-
val rtsTrustChain = TrustChainUtil.getTrustChain(qUri)
375-
val extraCaCerts = Files.createTempFile("q-extra-ca", ".pem").apply {
376-
writeText(
377-
TrustChainUtil.certsToPem(rtsTrustChain)
378-
)
374+
val extraCaCerts = try {
375+
val rtsTrustChain = TrustChainUtil.getTrustChain(qUri)
376+
377+
Files.createTempFile("q-extra-ca", ".pem").apply {
378+
writeText(
379+
TrustChainUtil.certsToPem(rtsTrustChain)
380+
)
381+
}
382+
} catch (e: Exception) {
383+
LOG.info(e) { "Could not resolve trust chain for $qUri, skipping NODE_EXTRA_CA_CERTS" }
384+
null
379385
}
380386

381387
val node = if (SystemInfo.isWindows) "node.exe" else "node"
382-
var nodePath = getNodeRuntimePath(artifact.resolve(node))
388+
val nodePath = getNodeRuntimePath(artifact.resolve(node))
383389

384390
val cmd = NodeExePatcher.patch(nodePath)
385391
.withParameters(
@@ -388,7 +394,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs
388394
"--set-credentials-encryption-key",
389395
).withEnvironment(
390396
buildMap {
391-
put("NODE_EXTRA_CA_CERTS", extraCaCerts.toAbsolutePath().toString())
397+
extraCaCerts?.let { put("NODE_EXTRA_CA_CERTS", it.toAbsolutePath().toString()) }
392398

393399
val proxy = JdkProxyProvider.getInstance().proxySelector.select(qUri)
394400
// log if only socks proxy available

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

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import org.jetbrains.annotations.VisibleForTesting
1616
import software.aws.toolkits.core.utils.error
1717
import software.aws.toolkits.core.utils.getLogger
1818
import software.aws.toolkits.core.utils.info
19+
import software.aws.toolkits.core.utils.warn
20+
import software.aws.toolkits.jetbrains.AwsPlugin
21+
import software.aws.toolkits.jetbrains.AwsToolkit
1922
import java.nio.file.Path
2023

2124
@Service
@@ -54,36 +57,43 @@ class ArtifactManager @NonInjectable internal constructor(private val manifestFe
5457
return mutex.withLock {
5558
coroutineScope {
5659
async {
57-
val manifest = manifestFetcher.fetch() ?: throw LspException(
58-
"Language Support is not available, as manifest is missing.",
59-
LspException.ErrorCode.MANIFEST_FETCH_FAILED
60-
)
61-
val lspVersions = getLSPVersionsFromManifestWithSpecifiedRange(manifest)
62-
63-
artifactHelper.removeDelistedVersions(lspVersions.deListedVersions)
64-
65-
if (lspVersions.inRangeVersions.isEmpty()) {
66-
// No versions are found which are in the given range. Fallback to local lsp artifacts.
67-
val localLspArtifacts = artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE)
68-
if (localLspArtifacts.isNotEmpty()) {
69-
return@async localLspArtifacts.first().first
60+
try {
61+
val manifest = manifestFetcher.fetch() ?: throw LspException(
62+
"Language Support is not available, as manifest is missing.",
63+
LspException.ErrorCode.MANIFEST_FETCH_FAILED
64+
)
65+
val lspVersions = getLSPVersionsFromManifestWithSpecifiedRange(manifest)
66+
67+
artifactHelper.removeDelistedVersions(lspVersions.deListedVersions)
68+
69+
if (lspVersions.inRangeVersions.isEmpty()) {
70+
// No versions are found which are in the given range. Fallback to local lsp artifacts.
71+
val localLspArtifacts = artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE)
72+
if (localLspArtifacts.isNotEmpty()) {
73+
return@async localLspArtifacts.first().first
74+
}
75+
throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
7076
}
71-
throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
72-
}
7377

74-
val targetVersion = lspVersions.inRangeVersions.first()
78+
val targetVersion = lspVersions.inRangeVersions.first()
7579

76-
// If there is an LSP Manifest with the same version
77-
val target = getTargetFromLspManifest(targetVersion)
78-
// Get Local LSP files and check if we can re-use existing LSP Artifacts
79-
val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) {
80-
artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first
81-
} else {
82-
artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target)
83-
?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED)
80+
// If there is an LSP Manifest with the same version
81+
val target = getTargetFromLspManifest(targetVersion)
82+
// Get Local LSP files and check if we can re-use existing LSP Artifacts
83+
val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) {
84+
artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first
85+
} else {
86+
artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target)
87+
?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED)
88+
}
89+
artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE)
90+
return@async artifactPath
91+
} catch (e: Exception) {
92+
logger.warn(e) { "Failed to resolve assets from Flare CDN" }
93+
val path = AwsToolkit.PLUGINS_INFO[AwsPlugin.Q]?.path?.resolve("flare") ?: error("not even bundled")
94+
logger.info { "Falling back to bundled assets at $path" }
95+
return@async path
8496
}
85-
artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE)
86-
return@async artifactPath
8797
}
8898
}.also {
8999
artifactDeferred = it

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const val CHAT_INSERT_TO_CURSOR_NOTIFICATION = "aws/chat/insertToCursorPosition"
2020
const val CHAT_LINK_CLICK = "aws/chat/linkClick"
2121
const val CHAT_LIST_CONVERSATIONS = "aws/chat/listConversations"
2222
const val CHAT_OPEN_TAB = "aws/chat/openTab"
23+
const val CHAT_OPTIONS_UPDATE_NOTIFICATION = "aws/chat/chatOptionsUpdate"
2324
const val CHAT_PROMPT_OPTION_ACKNOWLEDGED = "chatPromptOptionAcknowledged"
2425
const val CHAT_QUICK_ACTION = "aws/chat/sendChatQuickAction"
2526
const val CHAT_READY = "aws/chat/ready"

plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManagerTest.kt

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts
55

6+
import com.intellij.ide.plugins.PluginManagerCore
7+
import com.intellij.openapi.extensions.PluginId
68
import com.intellij.testFramework.ProjectExtension
79
import com.intellij.util.text.SemVer
810
import io.mockk.Runs
@@ -17,7 +19,6 @@ import kotlinx.coroutines.test.runTest
1719
import org.assertj.core.api.Assertions.assertThat
1820
import org.junit.jupiter.api.BeforeEach
1921
import org.junit.jupiter.api.Test
20-
import org.junit.jupiter.api.assertThrows
2122
import org.junit.jupiter.api.extension.RegisterExtension
2223
import org.junit.jupiter.api.io.TempDir
2324
import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactManager.SupportedManifestVersionRange
@@ -51,29 +52,27 @@ class ArtifactManagerTest {
5152
}
5253

5354
@Test
54-
fun `fetch artifact fetcher throws exception if manifest is null`() = runTest {
55+
fun `fetch artifact fetcher returns bundled if manifest is null`() = runTest {
5556
every { manifestFetcher.fetch() }.returns(null)
5657

57-
val exception = assertThrows<LspException> {
58-
artifactManager.fetchArtifact(projectExtension.project)
59-
}
60-
assertThat(exception)
61-
.hasFieldOrPropertyWithValue("errorCode", LspException.ErrorCode.MANIFEST_FETCH_FAILED)
58+
assertThat(artifactManager.fetchArtifact(projectExtension.project))
59+
.isEqualTo(
60+
PluginManagerCore.getPlugin(PluginId.getId("amazon.q"))?.pluginPath?.resolve("flare")
61+
)
6262
}
6363

6464
@Test
65-
fun `fetch artifact does not have any valid lsp versions`() = runTest {
65+
fun `fetch artifact does not have any valid lsp versions returns bundled`() = runTest {
6666
every { manifestFetcher.fetch() }.returns(Manifest())
6767

6868
every { artifactManager.getLSPVersionsFromManifestWithSpecifiedRange(any()) }.returns(
6969
ArtifactManager.LSPVersions(deListedVersions = emptyList(), inRangeVersions = emptyList())
7070
)
7171

72-
val exception = assertThrows<LspException> {
73-
artifactManager.fetchArtifact(projectExtension.project)
74-
}
75-
assertThat(exception)
76-
.hasFieldOrPropertyWithValue("errorCode", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
72+
assertThat(artifactManager.fetchArtifact(projectExtension.project))
73+
.isEqualTo(
74+
PluginManagerCore.getPlugin(PluginId.getId("amazon.q"))?.pluginPath?.resolve("flare")
75+
)
7776
}
7877

7978
@Test

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/AwsToolkit.kt

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
package software.aws.toolkits.jetbrains
55

66
import com.intellij.ide.plugins.PluginManagerCore
7-
import com.intellij.openapi.application.ApplicationManager
87
import com.intellij.openapi.extensions.PluginDescriptor
98
import com.intellij.openapi.extensions.PluginId
109
import java.nio.file.Path
11-
import java.nio.file.Paths
1210
import java.util.EnumMap
1311

1412
object AwsToolkit {
@@ -37,12 +35,7 @@ data class PluginInfo(val id: String, val name: String) {
3735
val version: String?
3836
get() = descriptor?.version
3937
val path: Path?
40-
get() =
41-
if (ApplicationManager.getApplication().isUnitTestMode) {
42-
Paths.get(System.getProperty("plugin.path"))
43-
} else {
44-
descriptor?.pluginPath
45-
}
38+
get() = descriptor?.pluginPath
4639
}
4740

4841
enum class AwsPlugin {

0 commit comments

Comments
 (0)