Skip to content

Commit 62153b8

Browse files
authored
perf: cleanup/reduce de/ser cycles introduced in #5970 (#5992)
Reduce the amount of Gson calls added by #5970
1 parent f57be32 commit 62153b8

File tree

3 files changed

+215
-124
lines changed

3 files changed

+215
-124
lines changed

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
@@ -581,32 +580,6 @@ class BrowserConnector(
581580
}
582581
}
583582

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

627599
val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat(
628600
SEND_CHAT_COMMAND_PROMPT,
629601
tabId,
630-
Gson().toJson(decryptedMessage),
602+
decryptedMessage,
631603
isPartialResult = false
632604
)
633605
browser.postChat(messageToChat)
@@ -641,26 +613,23 @@ class BrowserConnector(
641613
}
642614
}
643615

644-
fun parseFindingsMessages(messagesMap: Map<String, *>) {
616+
fun deserializeFindings(@Language("JSON") responsePayload: String): List<FlareAggregatedFindings> {
617+
val additionalMessages = serializer.objectMapper.readValue<FlareAdditionalMessages>(responsePayload).additionalMessages
618+
?: return emptyList()
619+
620+
return additionalMessages.filter { message ->
621+
message.messageId.endsWith(CODE_REVIEW_FINDINGS_SUFFIX) ||
622+
message.messageId.endsWith(DISPLAY_FINDINGS_SUFFIX)
623+
}
624+
}
625+
626+
fun parseFindingsMessages(@Language("JSON") responsePayload: String) {
645627
try {
646-
val additionalMessages = messagesMap["additionalMessages"] as? MutableList<Map<String, Any>>
647-
val findingsMessages = additionalMessages?.filter { message ->
648-
if (message.contains("messageId")) {
649-
(message["messageId"] as String).endsWith(CODE_REVIEW_FINDINGS_SUFFIX) ||
650-
(message["messageId"] as String).endsWith(DISPLAY_FINDINGS_SUFFIX)
651-
} else {
652-
false
653-
}
654-
}
628+
val findings = deserializeFindings(responsePayload)
655629
val scannedFiles = mutableListOf<VirtualFile>()
656-
if (findingsMessages != null) {
657-
for (findingsMessage in findingsMessages) {
658-
additionalMessages.remove(findingsMessage)
659-
val gson = Gson()
660-
val jsonFindings = gson.fromJson(findingsMessage["body"] as String, List::class.java)
661-
val mappedFindings = mutableListOf<CodeWhispererCodeScanIssue>()
662-
for (aggregatedIssueUnformatted in jsonFindings) {
663-
val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue::class.java)
630+
val mappedFindings = buildList {
631+
for (finding in findings) {
632+
for (aggregatedIssue in finding.body) {
664633
val file = LocalFileSystem.getInstance().findFileByIoFile(
665634
Path.of(aggregatedIssue.filePath).toFile()
666635
)
@@ -677,7 +646,8 @@ class BrowserConnector(
677646
if (isIssueIgnored) {
678647
continue
679648
}
680-
mappedFindings.add(
649+
650+
add(
681651
CodeWhispererCodeScanIssue(
682652
startLine = issue.startLine,
683653
startCol = 1,
@@ -705,18 +675,20 @@ class BrowserConnector(
705675
}
706676
}
707677
}
708-
709-
CodeWhispererCodeScanManager.getInstance(project)
710-
.addOnDemandIssues(
711-
mappedFindings,
712-
scannedFiles,
713-
CodeWhispererConstants.CodeAnalysisScope.AGENTIC
714-
)
715-
CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI()
716678
}
717679
}
680+
681+
if (mappedFindings.isNotEmpty()) {
682+
CodeWhispererCodeScanManager.getInstance(project)
683+
.addOnDemandIssues(
684+
mappedFindings,
685+
scannedFiles,
686+
CodeWhispererConstants.CodeAnalysisScope.AGENTIC
687+
)
688+
CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI()
689+
}
718690
} catch (e: Exception) {
719-
LOG.error { "Failed to parse findings message $e" }
691+
LOG.error(e) { "Failed to parse findings message" }
720692
}
721693
}
722694

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)