Skip to content

Commit 50cb99b

Browse files
authored
Merge branch 'main' into auto-trigger-review
2 parents 5f91840 + aed4ab7 commit 50cb99b

File tree

18 files changed

+191
-54
lines changed

18 files changed

+191
-54
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/ChatController.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import software.aws.toolkits.core.utils.getLogger
3636
import software.aws.toolkits.core.utils.info
3737
import software.aws.toolkits.core.utils.warn
3838
import software.aws.toolkits.jetbrains.core.coroutines.EDT
39+
import software.aws.toolkits.jetbrains.services.amazonq.CHAT_IMPLICIT_PROJECT_CONTEXT_TIMEOUT
3940
import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
4041
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
4142
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
@@ -137,15 +138,15 @@ class ChatController private constructor(
137138
shouldUseWorkspaceContext = true
138139
prompt = prompt.replace("@workspace", "")
139140
val projectContextController = ProjectContextController.getInstance(context.project)
140-
queryResult = projectContextController.query(prompt)
141+
queryResult = projectContextController.query(prompt, timeout = null)
141142
if (!projectContextController.getProjectContextIndexComplete()) shouldAddIndexInProgressMessage = true
142143
logger.info { "project context relevant document count: ${queryResult.size}" }
143144
} else {
144145
sendOpenSettingsMessage(message.tabId)
145146
}
146147
} else if (CodeWhispererSettings.getInstance().isProjectContextEnabled() && isDataCollectionGroup) {
147148
val projectContextController = ProjectContextController.getInstance(context.project)
148-
queryResult = projectContextController.query(prompt)
149+
queryResult = projectContextController.query(prompt, timeout = CHAT_IMPLICIT_PROJECT_CONTEXT_TIMEOUT)
149150
}
150151

151152
handleChat(

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/inline/OpenChatInputAction.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@
33

44
package software.aws.toolkits.jetbrains.services.cwc.inline
55

6-
import com.intellij.execution.impl.ConsoleViewImpl
76
import com.intellij.openapi.actionSystem.AnAction
87
import com.intellij.openapi.actionSystem.AnActionEvent
98
import com.intellij.openapi.actionSystem.CommonDataKeys
9+
import com.intellij.openapi.util.Key
1010

1111
class OpenChatInputAction : AnAction() {
1212
override fun actionPerformed(e: AnActionEvent) {
1313
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
14-
val isConsole = editor.document.getUserData(ConsoleViewImpl.IS_CONSOLE_DOCUMENT)
14+
// FIX_WHEN_MIN_IS_241: change below to use ConsoleViewImpl.IS_CONSOLE_DOCUMENT
15+
var isConsole: Any? = null
16+
val key: Key<*>? = Key.findKeyByName("IS_CONSOLE_DOCUMENT")
17+
if (key != null) {
18+
isConsole = editor.document.getUserData(key)
19+
}
1520
if (isConsole == true) return
1621
if (!editor.document.isWritable) return
1722
val project = editor.project ?: return

plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/workspace/context/ProjectContextProviderTest.kt

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,10 @@ class ProjectContextProviderTest {
214214
}
215215

216216
@Test
217-
fun `query should send correct encrypted request to lsp`() {
218-
sut.query("foo")
217+
fun `query should send correct encrypted request to lsp`() = runTest {
218+
sut = ProjectContextProvider(project, encoderServer, this)
219+
val r = sut.query("foo", null)
220+
advanceUntilIdle()
219221

220222
val request = QueryChatRequest("foo")
221223
val requestJson = mapper.writeValueAsString(request)
@@ -269,13 +271,15 @@ class ProjectContextProviderTest {
269271
)
270272

271273
assertThrows<Exception> {
272-
sut.query("foo")
274+
sut.query("foo", null)
273275
}
274276
}
275277

276278
@Test
277279
fun `query chat should return deserialized relevantDocument`() = runTest {
278-
val r = sut.query("foo")
280+
sut = ProjectContextProvider(project, encoderServer, this)
281+
val r = sut.query("foo", null)
282+
advanceUntilIdle()
279283
assertThat(r).hasSize(2)
280284
assertThat(r[0]).isEqualTo(
281285
RelevantDocument(
@@ -377,6 +381,29 @@ class ProjectContextProviderTest {
377381
}
378382
}
379383

384+
@Test
385+
fun `queryChat should throw if time elapsed is greather than 500ms`() = runTest {
386+
assertThrows<TimeoutCancellationException> {
387+
sut = ProjectContextProvider(project, encoderServer, this)
388+
stubFor(
389+
any(urlPathEqualTo("/query")).willReturn(
390+
aResponse()
391+
.withStatus(200)
392+
.withResponseBody(
393+
Body(validQueryChatResponse)
394+
)
395+
.withFixedDelay(501)
396+
)
397+
)
398+
399+
withContext(getCoroutineBgContext()) {
400+
sut.query("foo", timeout = 500L)
401+
}
402+
403+
advanceUntilIdle()
404+
}
405+
}
406+
380407
@Test
381408
fun `test index payload is encrypted`() = runTest {
382409
whenever(encoderServer.port).thenReturn(3000)
@@ -390,12 +417,9 @@ class ProjectContextProviderTest {
390417

391418
@Test
392419
fun `test query payload is encrypted`() = runTest {
393-
whenever(encoderServer.port).thenReturn(3000)
394-
try {
395-
sut.query("what does this project do")
396-
} catch (e: ConnectException) {
397-
// no-op
398-
}
420+
sut = ProjectContextProvider(project, encoderServer, this)
421+
sut.query("what does this project do", null)
422+
advanceUntilIdle()
399423
verify(encoderServer, times(1)).encrypt(any())
400424
}
401425

plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/ArtifactHandler.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.model.DownloadFai
3939
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
4040
import software.aws.toolkits.jetbrains.services.codemodernizer.model.ParseZipFailureReason
4141
import software.aws.toolkits.jetbrains.services.codemodernizer.model.UnzipFailureReason
42+
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeModernizerSessionState
4243
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.getPathToHilArtifactDir
4344
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isValidCodeTransformConnection
4445
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.openTroubleshootingGuideNotificationAction
@@ -199,6 +200,17 @@ class ArtifactHandler(private val project: Project, private val clientAdaptor: G
199200
downloadedBuildLogPath[job] = path
200201
} else {
201202
downloadedArtifacts[job] = path
203+
if (output.artifact is CodeModernizerArtifact && output.artifact.metrics != null) {
204+
output.artifact.metrics.linesOfCodeSubmitted = CodeModernizerSessionState.getInstance(project).getLinesOfCodeSubmitted()
205+
output.artifact.metrics.programmingLanguage = CodeModernizerSessionState.getInstance(project).getTransformationLanguage()
206+
try {
207+
clientAdaptor.sendTransformTelemetryEvent(job, output.artifact.metrics)
208+
} catch (e: Exception) {
209+
// log error, but can still show diff.patch and summary.md
210+
LOG.error { e.message.toString() }
211+
telemetryErrorMessage = "Unexpected error when sending telemetry with metrics ${e.localizedMessage}"
212+
}
213+
}
202214
}
203215
output
204216
} catch (e: RuntimeException) {

plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/client/GumbyClient.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.GetTransformat
1616
import software.amazon.awssdk.services.codewhispererruntime.model.GetTransformationPlanResponse
1717
import software.amazon.awssdk.services.codewhispererruntime.model.GetTransformationRequest
1818
import software.amazon.awssdk.services.codewhispererruntime.model.GetTransformationResponse
19+
import software.amazon.awssdk.services.codewhispererruntime.model.IdeCategory
1920
import software.amazon.awssdk.services.codewhispererruntime.model.ResumeTransformationRequest
2021
import software.amazon.awssdk.services.codewhispererruntime.model.ResumeTransformationResponse
2122
import software.amazon.awssdk.services.codewhispererruntime.model.StartTransformationRequest
@@ -46,8 +47,11 @@ import software.aws.toolkits.jetbrains.services.amazonq.CONTENT_SHA256
4647
import software.aws.toolkits.jetbrains.services.amazonq.SERVER_SIDE_ENCRYPTION
4748
import software.aws.toolkits.jetbrains.services.amazonq.SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID
4849
import software.aws.toolkits.jetbrains.services.amazonq.clients.AmazonQStreamingClient
50+
import software.aws.toolkits.jetbrains.services.amazonq.codeWhispererUserContext
51+
import software.aws.toolkits.jetbrains.services.codemodernizer.model.CodeModernizerMetrics
4952
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
5053
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.calculateTotalLatency
54+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getTelemetryOptOutPreference
5155
import java.io.File
5256
import java.net.HttpURLConnection
5357
import java.time.Instant
@@ -210,6 +214,26 @@ class GumbyClient(private val project: Project) {
210214
}
211215
}
212216

217+
fun sendTransformTelemetryEvent(job: JobId, metrics: CodeModernizerMetrics) {
218+
bearerClient().sendTelemetryEvent { requestBuilder ->
219+
requestBuilder.telemetryEvent { telemetryEventBuilder ->
220+
telemetryEventBuilder.transformEvent {
221+
it.jobId(job.id)
222+
it.timestamp(Instant.now())
223+
it.ideCategory(IdeCategory.JETBRAINS)
224+
it.programmingLanguage { language ->
225+
language.languageName(metrics.programmingLanguage)
226+
}
227+
it.linesOfCodeChanged(metrics.linesOfCodeChanged)
228+
it.charsOfCodeChanged(metrics.charactersOfCodeChanged)
229+
it.linesOfCodeSubmitted(metrics.linesOfCodeSubmitted) // currently unavailable for SQL conversions
230+
}
231+
}
232+
requestBuilder.optOutPreference(getTelemetryOptOutPreference())
233+
requestBuilder.userContext(codeWhispererUserContext())
234+
}
235+
}
236+
213237
companion object {
214238
private val LOG = getLogger<GumbyClient>()
215239

plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/model/CodeModernizerArtifact.kt

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.services.codemodernizer.model
55

66
import com.fasterxml.jackson.core.JsonProcessingException
77
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
8+
import com.fasterxml.jackson.module.kotlin.readValue
89
import com.intellij.openapi.util.io.FileUtil.createTempDirectory
910
import com.intellij.openapi.vfs.LocalFileSystem
1011
import com.intellij.openapi.vfs.VirtualFile
@@ -29,17 +30,19 @@ open class CodeModernizerArtifact(
2930
private val patches: List<VirtualFile>,
3031
val summary: TransformationSummary,
3132
val summaryMarkdownFile: File,
33+
val metrics: CodeModernizerMetrics?,
3234
) : CodeTransformDownloadArtifact {
3335
val patch: VirtualFile
3436
get() = patches.first()
3537

3638
companion object {
37-
private const val maxSupportedVersion = 1.0
39+
private const val MAX_SUPPORTED_VERSION = 1.0
3840
private val tempDir = createTempDirectory("codeTransformArtifacts", null)
39-
private const val manifestPathInZip = "manifest.json"
40-
private const val summaryNameInZip = "summary.md"
41+
private const val MANIFEST_FILE_NAME = "manifest.json"
42+
private const val SUMMARY_FILE_NAME = "summary.md"
43+
private const val METRICS_FILE_NAME = "metrics.json"
4144
val LOG = getLogger<CodeModernizerArtifact>()
42-
private val MAPPER = jacksonObjectMapper()
45+
val MAPPER = jacksonObjectMapper()
4346

4447
/**
4548
* Extracts the file at [zipPath] and uses its contents to produce a [CodeModernizerArtifact].
@@ -53,39 +56,40 @@ open class CodeModernizerArtifact(
5356
throw RuntimeException("Could not unzip artifact")
5457
}
5558
val manifest = loadManifest()
56-
if (manifest.version > maxSupportedVersion) {
59+
if (manifest.version > MAX_SUPPORTED_VERSION) {
5760
// If not supported we can still try to use it, i.e. the versions should largely be backwards compatible
58-
LOG.warn { "Unsupported version: ${manifest.version}" }
61+
LOG.warn { "Unsupported manifest.json version: ${manifest.version}" }
5962
}
6063
val patches = extractPatches(manifest)
6164
val summary = extractSummary(manifest)
6265
val summaryMarkdownFile = getSummaryFile(manifest)
66+
val metrics = loadMetrics(manifest)
6367
if (patches.size != 1) throw RuntimeException("Expected 1 patch, but found ${patches.size}")
64-
return CodeModernizerArtifact(zipPath, manifest, patches, summary, summaryMarkdownFile)
68+
return CodeModernizerArtifact(zipPath, manifest, patches, summary, summaryMarkdownFile, metrics)
6569
}
6670
throw RuntimeException("Could not find artifact")
6771
}
6872

6973
private fun extractSummary(manifest: CodeModernizerManifest): TransformationSummary {
70-
val summaryFile = tempDir.toPath().resolve(manifest.summaryRoot).resolve(summaryNameInZip).toFile()
74+
val summaryFile = tempDir.toPath().resolve(manifest.summaryRoot).resolve(SUMMARY_FILE_NAME).toFile()
7175
if (!summaryFile.exists() || summaryFile.isDirectory) {
7276
throw RuntimeException("The summary in the downloaded zip had an unknown format")
7377
}
7478
return TransformationSummary(summaryFile.readText())
7579
}
7680

77-
private fun getSummaryFile(manifest: CodeModernizerManifest) = tempDir.toPath().resolve(manifest.summaryRoot).resolve(summaryNameInZip).toFile()
81+
private fun getSummaryFile(manifest: CodeModernizerManifest) = tempDir.toPath().resolve(manifest.summaryRoot).resolve(SUMMARY_FILE_NAME).toFile()
7882

7983
/**
8084
* Attempts to load the manifest from the zip file. Throws an exception if the manifest is not found or cannot be serialized.
8185
*/
8286
private fun loadManifest(): CodeModernizerManifest {
8387
val manifestFile =
8488
tempDir.listFiles()
85-
?.firstOrNull { it.name.endsWith(manifestPathInZip) }
89+
?.firstOrNull { it.name.endsWith(MANIFEST_FILE_NAME) }
8690
?: throw RuntimeException("Could not find manifest")
8791
try {
88-
val manifest = MAPPER.readValue(manifestFile, CodeModernizerManifest::class.java)
92+
val manifest = MAPPER.readValue<CodeModernizerManifest>(manifestFile)
8993
if (manifest.version == 0.0F) {
9094
throw RuntimeException(
9195
"Unable to deserialize the manifest",
@@ -97,6 +101,19 @@ open class CodeModernizerArtifact(
97101
}
98102
}
99103

104+
private fun loadMetrics(manifest: CodeModernizerManifest): CodeModernizerMetrics? {
105+
try {
106+
val metricsFile =
107+
tempDir.resolve(manifest.metricsRoot).listFiles()
108+
?.firstOrNull { it.name.endsWith(METRICS_FILE_NAME) }
109+
?: throw RuntimeException("Could not find metrics.json")
110+
return MAPPER.readValue<CodeModernizerMetrics>(metricsFile)
111+
} catch (exception: Exception) {
112+
// if metrics.json not present or parsing fails, can still show diff.patch and summary.md
113+
return null
114+
}
115+
}
116+
100117
@OptIn(ExperimentalPathApi::class)
101118
private fun extractPatches(manifest: CodeModernizerManifest): List<VirtualFile> {
102119
val fileSystem = LocalFileSystem.getInstance()

plugins/amazonq/codetransform/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codemodernizer/model/CodeModernizerManifest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ package software.aws.toolkits.jetbrains.services.codemodernizer.model
66
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
77

88
@JsonIgnoreProperties(ignoreUnknown = true)
9-
data class CodeModernizerManifest(val version: Float, val patchesRoot: String, val artifactsRoot: String, val summaryRoot: String)
9+
data class CodeModernizerManifest(val version: Float, val patchesRoot: String, val artifactsRoot: String, val summaryRoot: String, val metricsRoot: String)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codemodernizer.model
5+
6+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
7+
8+
@JsonIgnoreProperties(ignoreUnknown = true)
9+
data class CodeModernizerMetrics(
10+
val linesOfCodeChanged: Int?,
11+
val charactersOfCodeChanged: Int?,
12+
var linesOfCodeSubmitted: Int?,
13+
var programmingLanguage: String?,
14+
)

0 commit comments

Comments
 (0)