From cdc456498dbb0b07f765900225d9b7ebb6d40980 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 25 Aug 2025 18:43:26 -0700 Subject: [PATCH 1/2] chore: cleanup/reduce de/ser cycles introduced in #5970 --- .../amazonq/webview/BrowserConnector.kt | 68 ++++---- .../amazonq/webview/BrowserConnectorTest.kt | 147 +++++++++++------- 2 files changed, 128 insertions(+), 87 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt index d2172d1f6f9..c82583d0831 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt @@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.webview import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.node.ObjectNode +import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.treeToValue import com.google.gson.Gson import com.intellij.ide.BrowserUtil @@ -34,6 +35,7 @@ import org.cef.browser.CefBrowser import org.eclipse.lsp4j.TextDocumentIdentifier import org.eclipse.lsp4j.jsonrpc.ResponseErrorException import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode +import org.intellij.lang.annotations.Language import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info @@ -581,6 +583,21 @@ class BrowserConnector( } } + /** + * Solely used to extract the aggregated findings from the response + */ + // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model + data class FlareAdditionalMessages( + val additionalMessages: List?, + ) + + // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model + data class FlareAggregatedFindings( + val messageId: String, + val body: List, + ) + + // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model data class FlareCodeScanIssue( val startLine: Int, val endLine: Int, @@ -602,6 +619,7 @@ class BrowserConnector( val findingContext: String, ) + // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model data class AggregatedCodeScanIssue( val filePath: String, val issues: List, @@ -620,14 +638,13 @@ class BrowserConnector( throw error } chatCommunicationManager.removePartialChatMessage(partialResultToken) - val decryptedMessage = Gson().fromJson(value?.let { encryptionManager?.decrypt(it) }.orEmpty(), Map::class.java) - as Map + val decryptedMessage = value?.let { encryptionManager?.decrypt(it) }.orEmpty() parseFindingsMessages(decryptedMessage) val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat( SEND_CHAT_COMMAND_PROMPT, tabId, - Gson().toJson(decryptedMessage), + decryptedMessage, isPartialResult = false ) browser.postChat(messageToChat) @@ -641,26 +658,18 @@ class BrowserConnector( } } - fun parseFindingsMessages(messagesMap: Map) { + fun parseFindingsMessages(@Language("JSON") responsePayload: String) { try { - val additionalMessages = messagesMap["additionalMessages"] as? MutableList> + val additionalMessages = serializer.objectMapper.readValue(responsePayload).additionalMessages val findingsMessages = additionalMessages?.filter { message -> - if (message.contains("messageId")) { - (message["messageId"] as String).endsWith(CODE_REVIEW_FINDINGS_SUFFIX) || - (message["messageId"] as String).endsWith(DISPLAY_FINDINGS_SUFFIX) - } else { - false - } - } + message.messageId.endsWith(CODE_REVIEW_FINDINGS_SUFFIX) || + message.messageId.endsWith(DISPLAY_FINDINGS_SUFFIX) + } ?: return + val scannedFiles = mutableListOf() - if (findingsMessages != null) { + val mappedFindings = buildList { for (findingsMessage in findingsMessages) { - additionalMessages.remove(findingsMessage) - val gson = Gson() - val jsonFindings = gson.fromJson(findingsMessage["body"] as String, List::class.java) - val mappedFindings = mutableListOf() - for (aggregatedIssueUnformatted in jsonFindings) { - val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue::class.java) + for (aggregatedIssue in findingsMessage.body) { val file = LocalFileSystem.getInstance().findFileByIoFile( Path.of(aggregatedIssue.filePath).toFile() ) @@ -677,7 +686,8 @@ class BrowserConnector( if (isIssueIgnored) { continue } - mappedFindings.add( + + add( CodeWhispererCodeScanIssue( startLine = issue.startLine, startCol = 1, @@ -705,16 +715,18 @@ class BrowserConnector( } } } - - CodeWhispererCodeScanManager.getInstance(project) - .addOnDemandIssues( - mappedFindings, - scannedFiles, - CodeWhispererConstants.CodeAnalysisScope.AGENTIC - ) - CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI() } } + + if (mappedFindings.isNotEmpty()) { + CodeWhispererCodeScanManager.getInstance(project) + .addOnDemandIssues( + mappedFindings, + scannedFiles, + CodeWhispererConstants.CodeAnalysisScope.AGENTIC + ) + CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI() + } } catch (e: Exception) { LOG.error { "Failed to parse findings message $e" } } diff --git a/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt b/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt index 2a7f87440fa..65967386f8e 100644 --- a/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt +++ b/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt @@ -53,54 +53,76 @@ class BrowserConnectorTest : AmazonQTestBase() { } @Test - fun `parseFindingsMessages should handle empty additionalMessages`() { - val messagesMap = mapOf() - - browserConnector.parseFindingsMessages(messagesMap) + fun `parseFindingsMessages should handle no additionalMessages`() { + browserConnector.parseFindingsMessages("""""") verify(mockCodeScanManager, never()).addOnDemandIssues(any(), any(), any()) } @Test fun `parseFindingsMessages should handle null additionalMessages`() { - val messagesMap = mapOf("additionalMessages" to null) - - browserConnector.parseFindingsMessages(messagesMap) + browserConnector.parseFindingsMessages( + """ + { + "additionalMessages": null + } + """.trimIndent() + ) verify(mockCodeScanManager, never()).addOnDemandIssues(any(), any(), any()) } @Test - fun `parseFindingsMessages should filter messages with CODE_REVIEW_FINDINGS_SUFFIX`() { - val findingsMessage = mapOf( - "messageId" to "test_codeReviewFindings", - "body" to """[{"filePath": "/test/file.kt", "issues": []}]""" - ) - val otherMessage = mapOf( - "messageId" to "other_message", - "body" to "other content" + fun `parseFindingsMessages should handle empty additionalMessages`() { + browserConnector.parseFindingsMessages( + """ + { + "additionalMessages": [] + } + """.trimIndent() ) - val additionalMessages = mutableListOf>(findingsMessage, otherMessage) - val messagesMap = mapOf("additionalMessages" to additionalMessages) - browserConnector.parseFindingsMessages(messagesMap) + verify(mockCodeScanManager, never()).addOnDemandIssues(any(), any(), any()) + } - assertThat(additionalMessages.size == 1) - assertThat(additionalMessages[0] == otherMessage) + @Test + fun `parseFindingsMessages should filter messages with CODE_REVIEW_FINDINGS_SUFFIX`() { + val findingsMessage = """ + { + "additionalMessages": [ + { + "messageId": "test_codeReviewFindings", + "body": [{"filePath": "/test/file.kt", "issues": []}] + }, + { + "messageId": "other_message", + "body": "other content" + } + ] + } + """.trimIndent() + + browserConnector.parseFindingsMessages(findingsMessage) } @Test fun `parseFindingsMessages should filter messages with DISPLAY_FINDINGS_SUFFIX`() { - val findingsMessage = mapOf( - "messageId" to "test_displayFindings", - "body" to """[{"filePath": "/test/file.kt", "issues": []}]""" - ) - val additionalMessages = mutableListOf>(findingsMessage) - val messagesMap = mapOf("additionalMessages" to additionalMessages) - - browserConnector.parseFindingsMessages(messagesMap) + val findingsMessage = """ + { + "additionalMessages": [ + { + "messageId": "test_displayFindings", + "body": [{"filePath": "/test/file.kt", "issues": []}] + }, + { + "messageId": "other_message", + "body": "other content" + } + ] + } + """.trimIndent() - assertThat(additionalMessages).isEmpty() + browserConnector.parseFindingsMessages(findingsMessage) } @Test @@ -135,14 +157,18 @@ class BrowserConnectorTest : AmazonQTestBase() { ) val aggregatedIssue = BrowserConnector.AggregatedCodeScanIssue("/test/file.kt", listOf(issue)) - val findingsMessage = mapOf( - "messageId" to "test_codeReviewFindings", - "body" to jacksonObjectMapper().writeValueAsString(listOf(aggregatedIssue)) - ) - val additionalMessages = mutableListOf>(findingsMessage) - val messagesMap = mapOf("additionalMessages" to additionalMessages) - - browserConnector.parseFindingsMessages(messagesMap) + val findingsMessage = """ + { + "additionalMessages": [ + { + "messageId": "test_codeReviewFindings", + "body": ${jacksonObjectMapper().writeValueAsString(listOf(aggregatedIssue))} + } + ] + } + """.trimIndent() + + browserConnector.parseFindingsMessages(findingsMessage) val issuesCaptor = argumentCaptor>() verify(mockCodeScanManager).addOnDemandIssues( @@ -151,7 +177,6 @@ class BrowserConnectorTest : AmazonQTestBase() { eq(CodeWhispererConstants.CodeAnalysisScope.AGENTIC) ) - assertThat(additionalMessages).isEmpty() assertThat(issuesCaptor.firstValue.isNotEmpty()) assertThat(issuesCaptor.firstValue[0].title == "Test Issue") } @@ -177,38 +202,42 @@ class BrowserConnectorTest : AmazonQTestBase() { ) val aggregatedIssue = BrowserConnector.AggregatedCodeScanIssue("/test/directory", listOf(issue)) - val findingsMessage = mapOf( - "messageId" to "test_displayFindings", - "body" to jacksonObjectMapper().writeValueAsString(listOf(aggregatedIssue)) - ) - val additionalMessages = mutableListOf>(findingsMessage) - val messagesMap = mapOf("additionalMessages" to additionalMessages) + val findingsMessage = """ + { + "additionalMessages": [ + { + "messageId": "test_displayFindings", + "body": ${jacksonObjectMapper().writeValueAsString(listOf(aggregatedIssue))} + } + ] + } + """.trimIndent() - browserConnector.parseFindingsMessages(messagesMap) + browserConnector.parseFindingsMessages(findingsMessage) - val issuesCaptor = argumentCaptor>() - verify(mockCodeScanManager).addOnDemandIssues( - issuesCaptor.capture(), + verify(mockCodeScanManager, never()).addOnDemandIssues( + any(), any(), eq(CodeWhispererConstants.CodeAnalysisScope.AGENTIC) ) - - assertThat(issuesCaptor.firstValue.isEmpty()) - assertThat(additionalMessages).isEmpty() } } @Test fun `parseFindingsMessages should handle invalid JSON gracefully`() { - val findingsMessage = mapOf( - "messageId" to "test_codeReviewFindings", - "body" to "invalid json" - ) - val additionalMessages = mutableListOf>(findingsMessage) - val messagesMap = mapOf("additionalMessages" to additionalMessages) + val findingsMessage = """ + { + "additionalMessages": [ + { + "messageId": "test_codeReviewFindings", + "body": "invalid json" + } + ] + } + """.trimIndent() - browserConnector.parseFindingsMessages(messagesMap) + browserConnector.parseFindingsMessages(findingsMessage) - assertThat(additionalMessages).isEmpty() + verify(mockCodeScanManager, never()).addOnDemandIssues(any(), any(), any()) } } From c3b2b47d672e035294b01cbc107f2e22e3cfdcf3 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 25 Aug 2025 20:16:37 -0700 Subject: [PATCH 2/2] tst --- .../amazonq/webview/BrowserConnector.kt | 68 ++++--------------- .../webview/FlareAdditionalFindings.kt | 65 ++++++++++++++++++ .../amazonq/webview/BrowserConnectorTest.kt | 37 ++++++++-- 3 files changed, 110 insertions(+), 60 deletions(-) create mode 100644 plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/FlareAdditionalFindings.kt diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt index c82583d0831..585d4494a4c 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt @@ -117,9 +117,6 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeature import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanIssue import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanManager -import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description -import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation -import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants import software.aws.toolkits.jetbrains.settings.MeetQSettings @@ -583,48 +580,6 @@ class BrowserConnector( } } - /** - * Solely used to extract the aggregated findings from the response - */ - // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model - data class FlareAdditionalMessages( - val additionalMessages: List?, - ) - - // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model - data class FlareAggregatedFindings( - val messageId: String, - val body: List, - ) - - // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model - data class FlareCodeScanIssue( - val startLine: Int, - val endLine: Int, - val comment: String?, - val title: String, - val description: Description, - val detectorId: String, - val detectorName: String, - val findingId: String, - val ruleId: String?, - val relatedVulnerabilities: List, - val severity: String, - val recommendation: Recommendation, - val suggestedFixes: List, - val scanJobId: String, - val language: String, - val autoDetected: Boolean, - val filePath: String, - val findingContext: String, - ) - - // TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model - data class AggregatedCodeScanIssue( - val filePath: String, - val issues: List, - ) - private fun showResult( result: CompletableFuture, partialResultToken: String, @@ -658,18 +613,23 @@ class BrowserConnector( } } + fun deserializeFindings(@Language("JSON") responsePayload: String): List { + val additionalMessages = serializer.objectMapper.readValue(responsePayload).additionalMessages + ?: return emptyList() + + return additionalMessages.filter { message -> + message.messageId.endsWith(CODE_REVIEW_FINDINGS_SUFFIX) || + message.messageId.endsWith(DISPLAY_FINDINGS_SUFFIX) + } + } + fun parseFindingsMessages(@Language("JSON") responsePayload: String) { try { - val additionalMessages = serializer.objectMapper.readValue(responsePayload).additionalMessages - val findingsMessages = additionalMessages?.filter { message -> - message.messageId.endsWith(CODE_REVIEW_FINDINGS_SUFFIX) || - message.messageId.endsWith(DISPLAY_FINDINGS_SUFFIX) - } ?: return - + val findings = deserializeFindings(responsePayload) val scannedFiles = mutableListOf() val mappedFindings = buildList { - for (findingsMessage in findingsMessages) { - for (aggregatedIssue in findingsMessage.body) { + for (finding in findings) { + for (aggregatedIssue in finding.body) { val file = LocalFileSystem.getInstance().findFileByIoFile( Path.of(aggregatedIssue.filePath).toFile() ) @@ -728,7 +688,7 @@ class BrowserConnector( CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI() } } catch (e: Exception) { - LOG.error { "Failed to parse findings message $e" } + LOG.error(e) { "Failed to parse findings message" } } } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/FlareAdditionalFindings.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/FlareAdditionalFindings.kt new file mode 100644 index 00000000000..66ae2e10722 --- /dev/null +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/FlareAdditionalFindings.kt @@ -0,0 +1,65 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model +package software.aws.toolkits.jetbrains.services.amazonq.webview + +import com.fasterxml.jackson.annotation.JsonSetter +import com.fasterxml.jackson.annotation.Nulls +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description +import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation +import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix + +/** + * Solely used to extract the aggregated findings from the response + */ +data class FlareAdditionalMessages( + @get:JsonDeserialize(contentUsing = FlareAggregatedFindingsDeserializer::class) + @get:JsonSetter(contentNulls = Nulls.SKIP) + val additionalMessages: List?, +) + +data class FlareAggregatedFindings( + val messageId: String, + val body: List, +) + +data class FlareCodeScanIssue( + val startLine: Int, + val endLine: Int, + val comment: String?, + val title: String, + val description: Description, + val detectorId: String, + val detectorName: String, + val findingId: String, + val ruleId: String?, + val relatedVulnerabilities: List, + val severity: String, + val recommendation: Recommendation, + val suggestedFixes: List, + val scanJobId: String, + val language: String, + val autoDetected: Boolean, + val filePath: String, + val findingContext: String, +) + +data class AggregatedCodeScanIssue( + val filePath: String, + val issues: List, +) + +class FlareAggregatedFindingsDeserializer : JsonDeserializer() { + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): FlareAggregatedFindings? = + // drop values that do not look like FlareAggregatedFindings + try { + ctxt.readValue(p, FlareAggregatedFindings::class.java) + } catch (_: Exception) { + null + } +} diff --git a/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt b/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt index 65967386f8e..7d4f7cd81bc 100644 --- a/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt +++ b/plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnectorTest.kt @@ -30,6 +30,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Descripti import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants +import software.aws.toolkits.jetbrains.utils.satisfiesKt class BrowserConnectorTest : AmazonQTestBase() { private lateinit var browserConnector: BrowserConnector @@ -102,7 +103,15 @@ class BrowserConnectorTest : AmazonQTestBase() { } """.trimIndent() - browserConnector.parseFindingsMessages(findingsMessage) + assertThat(browserConnector.deserializeFindings(findingsMessage)) + .singleElement() + .satisfiesKt { + assertThat(it.messageId).isEqualTo("test_codeReviewFindings") + assertThat(it.body).singleElement() + .satisfiesKt { finding -> + assertThat(finding.filePath).isEqualTo("/test/file.kt") + } + } } @Test @@ -122,7 +131,15 @@ class BrowserConnectorTest : AmazonQTestBase() { } """.trimIndent() - browserConnector.parseFindingsMessages(findingsMessage) + assertThat(browserConnector.deserializeFindings(findingsMessage)) + .singleElement() + .satisfiesKt { + assertThat(it.messageId).isEqualTo("test_displayFindings") + assertThat(it.body).singleElement() + .satisfiesKt { finding -> + assertThat(finding.filePath).isEqualTo("/test/file.kt") + } + } } @Test @@ -145,7 +162,7 @@ class BrowserConnectorTest : AmazonQTestBase() { whenever(mockFileDocumentManager.getDocument(mockVirtualFile)) doReturn mockDocument whenever(mockCodeScanManager.isIgnoredIssue(any(), any(), any(), any())) doReturn false - val issue = BrowserConnector.FlareCodeScanIssue( + val issue = FlareCodeScanIssue( startLine = 1, endLine = 1, comment = "Test comment", title = "Test Issue", description = Description("Test description", "Test text"), detectorId = "test-detector", detectorName = "Test Detector", findingId = "test-finding-id", ruleId = "test-rule", @@ -156,13 +173,17 @@ class BrowserConnectorTest : AmazonQTestBase() { filePath = "/test/file.kt", findingContext = "test context" ) - val aggregatedIssue = BrowserConnector.AggregatedCodeScanIssue("/test/file.kt", listOf(issue)) + val aggregatedIssue = AggregatedCodeScanIssue("/test/file.kt", listOf(issue)) val findingsMessage = """ { "additionalMessages": [ { "messageId": "test_codeReviewFindings", "body": ${jacksonObjectMapper().writeValueAsString(listOf(aggregatedIssue))} + }, + { + "messageId": "other_message", + "body": "other content" } ] } @@ -191,7 +212,7 @@ class BrowserConnectorTest : AmazonQTestBase() { localFileSystemMock.`when` { LocalFileSystem.getInstance() } doReturn mockLocalFileSystem whenever(mockLocalFileSystem.findFileByIoFile(any())) doReturn mockDirectoryFile - val issue = BrowserConnector.FlareCodeScanIssue( + val issue = FlareCodeScanIssue( startLine = 1, endLine = 1, comment = null, title = "Test Issue", description = Description("Test description", "Test text"), detectorId = "test-detector", detectorName = "Test Detector", findingId = "test-finding-id", ruleId = null, @@ -201,13 +222,17 @@ class BrowserConnectorTest : AmazonQTestBase() { filePath = "/test/directory", findingContext = "test context" ) - val aggregatedIssue = BrowserConnector.AggregatedCodeScanIssue("/test/directory", listOf(issue)) + val aggregatedIssue = AggregatedCodeScanIssue("/test/directory", listOf(issue)) val findingsMessage = """ { "additionalMessages": [ { "messageId": "test_displayFindings", "body": ${jacksonObjectMapper().writeValueAsString(listOf(aggregatedIssue))} + }, + { + "messageId": "other_message", + "body": "other content" } ] }