Skip to content

Commit 734c3e7

Browse files
Merge main into feature/remote-chat-lsp
2 parents c2de6bc + 4e6c759 commit 734c3e7

File tree

11 files changed

+291
-139
lines changed

11 files changed

+291
-139
lines changed

plugins/amazonq/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ val prepareBundledFlare by tasks.registering(Copy::class) {
133133
}
134134

135135
tasks.withType<PrepareSandboxTask>().configureEach {
136-
from(file("contrib/QCT-Maven-6-16.jar")) {
136+
from(file("contrib/QCT-Maven-1-0-156-0.jar")) {
137137
into(intellijPlatform.projectName.map { "$it/lib" })
138138
}
139139
from(prepareBundledFlare) {

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt

Lines changed: 32 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.webview
55

66
import com.fasterxml.jackson.databind.JsonNode
77
import com.fasterxml.jackson.databind.node.ObjectNode
8+
import com.fasterxml.jackson.module.kotlin.readValue
89
import com.fasterxml.jackson.module.kotlin.treeToValue
910
import com.google.gson.Gson
1011
import com.intellij.ide.BrowserUtil
@@ -34,6 +35,7 @@ import org.cef.browser.CefBrowser
3435
import org.eclipse.lsp4j.TextDocumentIdentifier
3536
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException
3637
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode
38+
import org.intellij.lang.annotations.Language
3739
import software.aws.toolkits.core.utils.error
3840
import software.aws.toolkits.core.utils.getLogger
3941
import software.aws.toolkits.core.utils.info
@@ -115,9 +117,6 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeature
115117
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
116118
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanIssue
117119
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanManager
118-
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
119-
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
120-
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix
121120
import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
122121
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
123122
import software.aws.toolkits.jetbrains.settings.MeetQSettings
@@ -582,32 +581,6 @@ class BrowserConnector(
582581
}
583582
}
584583

585-
data class FlareCodeScanIssue(
586-
val startLine: Int,
587-
val endLine: Int,
588-
val comment: String?,
589-
val title: String,
590-
val description: Description,
591-
val detectorId: String,
592-
val detectorName: String,
593-
val findingId: String,
594-
val ruleId: String?,
595-
val relatedVulnerabilities: List<String>,
596-
val severity: String,
597-
val recommendation: Recommendation,
598-
val suggestedFixes: List<SuggestedFix>,
599-
val scanJobId: String,
600-
val language: String,
601-
val autoDetected: Boolean,
602-
val filePath: String,
603-
val findingContext: String,
604-
)
605-
606-
data class AggregatedCodeScanIssue(
607-
val filePath: String,
608-
val issues: List<FlareCodeScanIssue>,
609-
)
610-
611584
private fun showResult(
612585
result: CompletableFuture<String>,
613586
partialResultToken: String,
@@ -621,14 +594,13 @@ class BrowserConnector(
621594
throw error
622595
}
623596
chatCommunicationManager.removePartialChatMessage(partialResultToken)
624-
val decryptedMessage = Gson().fromJson(value?.let { encryptionManager?.decrypt(it) }.orEmpty(), Map::class.java)
625-
as Map<String, *>
597+
val decryptedMessage = value?.let { encryptionManager?.decrypt(it) }.orEmpty()
626598
parseFindingsMessages(decryptedMessage)
627599

628600
val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat(
629601
SEND_CHAT_COMMAND_PROMPT,
630602
tabId,
631-
Gson().toJson(decryptedMessage),
603+
decryptedMessage,
632604
isPartialResult = false
633605
)
634606
browser.postChat(messageToChat)
@@ -642,26 +614,23 @@ class BrowserConnector(
642614
}
643615
}
644616

645-
fun parseFindingsMessages(messagesMap: Map<String, *>) {
617+
fun deserializeFindings(@Language("JSON") responsePayload: String): List<FlareAggregatedFindings> {
618+
val additionalMessages = serializer.objectMapper.readValue<FlareAdditionalMessages>(responsePayload).additionalMessages
619+
?: return emptyList()
620+
621+
return additionalMessages.filter { message ->
622+
message.messageId.endsWith(CODE_REVIEW_FINDINGS_SUFFIX) ||
623+
message.messageId.endsWith(DISPLAY_FINDINGS_SUFFIX)
624+
}
625+
}
626+
627+
fun parseFindingsMessages(@Language("JSON") responsePayload: String) {
646628
try {
647-
val additionalMessages = messagesMap["additionalMessages"] as? MutableList<Map<String, Any>>
648-
val findingsMessages = additionalMessages?.filter { message ->
649-
if (message.contains("messageId")) {
650-
(message["messageId"] as String).endsWith(CODE_REVIEW_FINDINGS_SUFFIX) ||
651-
(message["messageId"] as String).endsWith(DISPLAY_FINDINGS_SUFFIX)
652-
} else {
653-
false
654-
}
655-
}
629+
val findings = deserializeFindings(responsePayload)
656630
val scannedFiles = mutableListOf<VirtualFile>()
657-
if (findingsMessages != null) {
658-
for (findingsMessage in findingsMessages) {
659-
additionalMessages.remove(findingsMessage)
660-
val gson = Gson()
661-
val jsonFindings = gson.fromJson(findingsMessage["body"] as String, List::class.java)
662-
val mappedFindings = mutableListOf<CodeWhispererCodeScanIssue>()
663-
for (aggregatedIssueUnformatted in jsonFindings) {
664-
val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue::class.java)
631+
val mappedFindings = buildList {
632+
for (finding in findings) {
633+
for (aggregatedIssue in finding.body) {
665634
val file = LocalFileSystem.getInstance().findFileByIoFile(
666635
Path.of(aggregatedIssue.filePath).toFile()
667636
)
@@ -678,7 +647,8 @@ class BrowserConnector(
678647
if (isIssueIgnored) {
679648
continue
680649
}
681-
mappedFindings.add(
650+
651+
add(
682652
CodeWhispererCodeScanIssue(
683653
startLine = issue.startLine,
684654
startCol = 1,
@@ -706,18 +676,20 @@ class BrowserConnector(
706676
}
707677
}
708678
}
709-
710-
CodeWhispererCodeScanManager.getInstance(project)
711-
.addOnDemandIssues(
712-
mappedFindings,
713-
scannedFiles,
714-
CodeWhispererConstants.CodeAnalysisScope.AGENTIC
715-
)
716-
CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI()
717679
}
718680
}
681+
682+
if (mappedFindings.isNotEmpty()) {
683+
CodeWhispererCodeScanManager.getInstance(project)
684+
.addOnDemandIssues(
685+
mappedFindings,
686+
scannedFiles,
687+
CodeWhispererConstants.CodeAnalysisScope.AGENTIC
688+
)
689+
CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI()
690+
}
719691
} catch (e: Exception) {
720-
LOG.error { "Failed to parse findings message $e" }
692+
LOG.error(e) { "Failed to parse findings message" }
721693
}
722694
}
723695

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model
5+
package software.aws.toolkits.jetbrains.services.amazonq.webview
6+
7+
import com.fasterxml.jackson.annotation.JsonSetter
8+
import com.fasterxml.jackson.annotation.Nulls
9+
import com.fasterxml.jackson.core.JsonParser
10+
import com.fasterxml.jackson.databind.DeserializationContext
11+
import com.fasterxml.jackson.databind.JsonDeserializer
12+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
13+
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
14+
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
15+
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix
16+
17+
/**
18+
* Solely used to extract the aggregated findings from the response
19+
*/
20+
data class FlareAdditionalMessages(
21+
@get:JsonDeserialize(contentUsing = FlareAggregatedFindingsDeserializer::class)
22+
@get:JsonSetter(contentNulls = Nulls.SKIP)
23+
val additionalMessages: List<FlareAggregatedFindings>?,
24+
)
25+
26+
data class FlareAggregatedFindings(
27+
val messageId: String,
28+
val body: List<AggregatedCodeScanIssue>,
29+
)
30+
31+
data class FlareCodeScanIssue(
32+
val startLine: Int,
33+
val endLine: Int,
34+
val comment: String?,
35+
val title: String,
36+
val description: Description,
37+
val detectorId: String,
38+
val detectorName: String,
39+
val findingId: String,
40+
val ruleId: String?,
41+
val relatedVulnerabilities: List<String>,
42+
val severity: String,
43+
val recommendation: Recommendation,
44+
val suggestedFixes: List<SuggestedFix>,
45+
val scanJobId: String,
46+
val language: String,
47+
val autoDetected: Boolean,
48+
val filePath: String,
49+
val findingContext: String,
50+
)
51+
52+
data class AggregatedCodeScanIssue(
53+
val filePath: String,
54+
val issues: List<FlareCodeScanIssue>,
55+
)
56+
57+
class FlareAggregatedFindingsDeserializer : JsonDeserializer<FlareAggregatedFindings>() {
58+
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): FlareAggregatedFindings? =
59+
// drop values that do not look like FlareAggregatedFindings
60+
try {
61+
ctxt.readValue(p, FlareAggregatedFindings::class.java)
62+
} catch (_: Exception) {
63+
null
64+
}
65+
}

0 commit comments

Comments
 (0)