Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type" : "bugfix",
"description" : "Amazon Q Code Transformation: show build logs when server-side build fails"
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodeTransformBuildSystem
import software.aws.toolkits.telemetry.CodeTransformCancelSrcComponents
import software.aws.toolkits.telemetry.CodeTransformPreValidationError
import software.aws.toolkits.telemetry.CodeTransformVCSViewerSrcComponents
import java.io.File
import java.nio.file.Path
import java.time.Instant
Expand Down Expand Up @@ -630,7 +629,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
is CodeModernizerJobCompletedResult.JobPartiallySucceeded -> {
notifyStickyInfo(
message("codemodernizer.notification.info.modernize_partial_complete.title"),
message("codemodernizer.notification.info.modernize_partial_complete.content", result.targetJavaVersion.description),
message("codemodernizer.notification.info.modernize_partial_complete.content"),
project,
listOf(displaySummaryNotificationAction(result.jobId), displayFeedbackNotificationAction()),
)
Expand Down Expand Up @@ -770,12 +769,6 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
codeTransformationSession?.tryOpenTransformationPlanEditor()
}

fun showDiff() {
val job = codeTransformationSession?.getActiveJobId() ?: return
// Use "TreeViewHeader" for Hub
artifactHandler.displayDiffAction(job, CodeTransformVCSViewerSrcComponents.TreeViewHeader)
}

fun handleCredentialsChanged() {
codeTransformationSession?.dispose()
codeModernizerBottomWindowPanelManager.reset()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,10 +493,7 @@ class CodeModernizerSession(
message("codemodernizer.notification.warn.unknown_status_response")
)

result.state == TransformationStatus.PARTIALLY_COMPLETED -> CodeModernizerJobCompletedResult.JobPartiallySucceeded(
jobId,
sessionContext.targetJavaVersion
)
result.state == TransformationStatus.PARTIALLY_COMPLETED -> CodeModernizerJobCompletedResult.JobPartiallySucceeded(jobId)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were not using sessionContext.targetJavaVersion


result.state == TransformationStatus.FAILED -> {
if (!passedStart) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,11 @@ fun buildTransformStoppedChatContent() = CodeTransformChatMessageContent(
type = CodeTransformChatMessageType.FinalizedAnswer,
)

fun buildTransformFailedChatContent(failureReason: String) = CodeTransformChatMessageContent(
message = message("codemodernizer.chat.message.transform_failed", failureReason),
type = CodeTransformChatMessageType.FinalizedAnswer,
)

fun buildUserSQLConversionSelectionSummaryChatContent(moduleName: String, schema: String) = CodeTransformChatMessageContent(
type = CodeTransformChatMessageType.Prompt,
message = getUserSQLConversionSelectionFormattedMarkdown(moduleName, schema)
Expand Down Expand Up @@ -537,7 +542,7 @@ fun buildTransformResumingChatContent() = CodeTransformChatMessageContent(
type = CodeTransformChatMessageType.PendingAnswer,
)

fun buildTransformResultChatContent(result: CodeModernizerJobCompletedResult, totalPatchFiles: Int): CodeTransformChatMessageContent {
fun buildTransformResultChatContent(result: CodeModernizerJobCompletedResult, totalPatchFiles: Int? = null): CodeTransformChatMessageContent {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the transformation fails, no patch files, so this can be optional as it's only used for full or partial successes.

val resultMessage = when (result) {
is CodeModernizerJobCompletedResult.JobAbortedZipTooLarge -> {
"${message(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildSt
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformAwaitUserInputChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformBeginChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformDependencyErrorChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformFailedChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformFindingLocalAlternativeDependencyChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformInProgressChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformResultChatContent
Expand Down Expand Up @@ -183,7 +184,7 @@ class CodeTransformChatController(
private suspend fun getUserObjective(tabId: String) {
codeTransformChatHelper.addNewMessage(buildChooseTransformationObjectiveChatContent())
codeTransformChatHelper.sendChatInputEnabledMessage(tabId, true)
codeTransformChatHelper.sendUpdatePlaceholderMessage(tabId, message("codemodernizer.chat.message.choose_objective"))
codeTransformChatHelper.sendUpdatePlaceholderMessage(tabId, message("codemodernizer.chat.message.choose_objective_placeholder"))
}

private suspend fun validateAndReplyOnError(transformationType: CodeTransformType): ValidationResult? {
Expand Down Expand Up @@ -665,9 +666,21 @@ class CodeTransformChatController(
codeTransformChatHelper.addNewMessage(buildStartNewTransformFollowup())
}

private suspend fun handleCodeTransformJobFailed(failureReason: String) {
codeTransformChatHelper.updateLastPendingMessage(buildTransformFailedChatContent(failureReason))
codeTransformChatHelper.addNewMessage(buildStartNewTransformFollowup())
}

private suspend fun handleCodeTransformJobFailedPreBuild(result: CodeModernizerJobCompletedResult.JobFailedInitialBuild) =
codeTransformChatHelper.addNewMessage(
buildTransformResultChatContent(result)
)

private suspend fun handleCodeTransformResult(result: CodeModernizerJobCompletedResult) {
when (result) {
is CodeModernizerJobCompletedResult.Stopped, CodeModernizerJobCompletedResult.JobAbortedBeforeStarting -> handleCodeTransformStoppedByUser()
is CodeModernizerJobCompletedResult.JobFailed -> handleCodeTransformJobFailed(result.failureReason)
is CodeModernizerJobCompletedResult.JobFailedInitialBuild -> handleCodeTransformJobFailedPreBuild(result)
Copy link
Contributor Author

@dhasani23 dhasani23 Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These make sure that 1) when the job fails, the last chat item is updated to say so and 2) when the server-side build fails, a new chat item is sent to say so + include a button to download the build logs (this button functionality was previously implemented in buildTransformResultChatContent, but it was not running).

else -> {
if (result is CodeModernizerJobCompletedResult.ZipUploadFailed && result.failureReason is UploadFailureReason.CREDENTIALS_EXPIRED) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@

package software.aws.toolkits.jetbrains.services.codemodernizer.model

import com.intellij.openapi.projectRoots.JavaSdkVersion

sealed class CodeModernizerJobCompletedResult {
data class RetryableFailure(val jobId: JobId, val failureReason: String) : CodeModernizerJobCompletedResult()
data class UnableToCreateJob(val failureReason: String, val retryable: Boolean = false) : CodeModernizerJobCompletedResult()
data class JobFailed(val jobId: JobId, val failureReason: String) : CodeModernizerJobCompletedResult()
data class ZipUploadFailed(val failureReason: UploadFailureReason) : CodeModernizerJobCompletedResult()
data class JobCompletedSuccessfully(val jobId: JobId) : CodeModernizerJobCompletedResult()
data class JobPartiallySucceeded(val jobId: JobId, val targetJavaVersion: JavaSdkVersion) : CodeModernizerJobCompletedResult()
data class JobPartiallySucceeded(val jobId: JobId) : CodeModernizerJobCompletedResult()

data class JobPaused(val jobId: JobId, val downloadArtifactId: String) : CodeModernizerJobCompletedResult()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ package software.aws.toolkits.jetbrains.services.codemodernizer.panels

import com.intellij.icons.AllIcons
import com.intellij.openapi.project.Project
import com.intellij.serviceContainer.AlreadyDisposedException
import com.intellij.ui.JBColor
import com.intellij.ui.border.CustomLineBorder
import com.intellij.ui.components.ActionLink
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.JBInsets
import com.intellij.util.ui.JBUI
import icons.AwsIcons
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.services.codemodernizer.CodeModernizerManager
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
import software.aws.toolkits.jetbrains.services.codewhisperer.layout.CodeWhispererLayoutConfig
import software.aws.toolkits.jetbrains.services.codewhisperer.layout.CodeWhispererLayoutConfig.addHorizontalGlue
import software.aws.toolkits.jetbrains.ui.feedback.CodeTransformFeedbackDialog
Expand All @@ -42,11 +40,13 @@ class CodeModernizerBanner(val project: Project) : JPanel(BorderLayout()) {
border = BorderFactory.createEmptyBorder(0, 5, 0, 0)
}

private val infoLabelJobId = JBLabel().apply {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry just for my own understanding, this is for the ux of displaying the job id in transformation hub right

foreground = JBColor.GRAY
border = BorderFactory.createEmptyBorder(0, 5, 0, 0)
}

private val infoPanel = JPanel(GridBagLayout())

val showDiffAction = ActionLink(message("codemodernizer.toolwindow.banner.action.diff")) {
CodeModernizerManager.getInstance(project).showDiff()
}
val showPlanAction = ActionLink(message("codemodernizer.toolwindow.banner.action.plan")) {
CodeModernizerManager.getInstance(project).showTransformationPlan()
}
Expand Down Expand Up @@ -84,6 +84,7 @@ class CodeModernizerBanner(val project: Project) : JPanel(BorderLayout()) {
)
}
add(infoLabelRunningTime, CodeWhispererLayoutConfig.kebabMenuConstraints)
add(infoLabelJobId, CodeWhispererLayoutConfig.kebabMenuConstraints)
}
infoPanel.revalidate()
infoPanel.repaint()
Expand Down Expand Up @@ -114,28 +115,23 @@ class CodeModernizerBanner(val project: Project) : JPanel(BorderLayout()) {
}

fun updateRunningTime(runTime: Duration?) {
try {
if (runTime == null) {
infoLabelRunningTime.text = ""
} else {
val timeTaken = runTime.toKotlinDuration().inWholeSeconds.seconds.toString()
infoLabelRunningTime.text = message(
"codemodernizer.toolwindow.transformation.progress.running_time",
timeTaken
)
}
} catch (exception: AlreadyDisposedException) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already catch this exception in the caller.

LOG.warn { "Disposed when about to create the loading panel" }
return
infoLabelRunningTime.text = if (runTime != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again here just curious, what running time is it referring to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "42s" in the image above

message("codemodernizer.toolwindow.transformation.progress.running_time", runTime.toKotlinDuration().inWholeSeconds.seconds.toString())
} else {
""
}
}

fun updateJobId(jobId: JobId?) {
infoLabelJobId.text = if (jobId != null) {
message("codemodernizer.toolwindow.transformation.progress.job_id", jobId.id)
} else {
""
}
}

fun clearActions() {
currentlyShownOptions.clear()
buildContent()
}

companion object {
private val LOG = getLogger<CodeModernizerBanner>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ class CodeModernizerBottomWindowPanelManager(private val project: Project) : JPa
}
}

private fun updateJobId() {
try {
val jobId = CodeModernizerSessionState.getInstance(project).currentJobId
banner.updateJobId(jobId)
} catch (e: AlreadyDisposedException) {
LOG.warn { "Disposed when about to update the jobId" }
return
}
}

fun setJobStartingUI() = setUI {
add(BorderLayout.CENTER, fullSizeLoadingPanel)
banner.clearActions()
Expand Down Expand Up @@ -123,6 +133,7 @@ class CodeModernizerBottomWindowPanelManager(private val project: Project) : JPa
fun setJobRunningUI() = setUI {
add(BorderLayout.CENTER, buildProgressSplitterPanelManager)
banner.updateContent(message("codemodernizer.toolwindow.banner.job_is_running"), AllIcons.General.BalloonInformation)
updateJobId()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make job ID more visible in Transformation Hub by putting it at the top next to the job run time.

buildProgressSplitterPanelManager.apply {
reset()
statusTreePanel.setDefaultUI()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.codemodernizer

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.fail
import org.junit.Test
import software.aws.toolkits.jetbrains.services.codemodernizer.constants.buildTransformResultChatContent
import software.aws.toolkits.jetbrains.services.codemodernizer.messages.CodeTransformButtonId
import software.aws.toolkits.jetbrains.services.codemodernizer.model.CodeModernizerJobCompletedResult
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
import software.aws.toolkits.resources.message

class CodeTransformChatTest {

@Test
fun `test that transform result chat item includes view build log button and message when pre-build fails`() {
val result = CodeModernizerJobCompletedResult.JobFailedInitialBuild(JobId("dummy-job-id-123"), "Build failed in Java 8 sandbox", true)
val chatItem = buildTransformResultChatContent(result)
assertEquals(chatItem.message, message("codemodernizer.chat.message.result.fail_initial_build"))
assertNotNull(chatItem.buttons)
assertEquals(chatItem.buttons?.size ?: fail("buttons is null"), 1)
assertEquals(chatItem.buttons?.get(0)?.id ?: fail("buttons is null"), CodeTransformButtonId.ViewBuildLog.id)
}

@Test
fun `test that transform result chat item includes view summary button and view diff button with correct label when job fully succeeded with 5 patch files`() {
val result = CodeModernizerJobCompletedResult.JobCompletedSuccessfully(JobId("dummy-job-id-123"))
val chatItem = buildTransformResultChatContent(result, 5)
assertEquals(chatItem.message, message("codemodernizer.chat.message.result.success.multiple_diffs"))
assertNotNull(chatItem.buttons)
assertEquals(chatItem.buttons?.size ?: fail("buttons is null"), 2)
assertEquals(chatItem.buttons?.get(0)?.id ?: fail("buttons is null"), CodeTransformButtonId.ViewDiff.id)
assertEquals(chatItem.buttons?.get(0)?.text ?: fail("buttons is null"), "View diff 1/5")
assertEquals(chatItem.buttons?.get(1)?.id ?: fail("buttons is null"), CodeTransformButtonId.ViewSummary.id)
}

@Test
fun `test that transform result chat item includes view summary button and view diff button with correct label when job partially succeeded with 1 patch file`() {
val result = CodeModernizerJobCompletedResult.JobPartiallySucceeded(JobId("dummy-job-id-123"))
val chatItem = buildTransformResultChatContent(result, 1)
assertEquals(chatItem.message, message("codemodernizer.chat.message.result.partially_success"))
assertNotNull(chatItem.buttons)
assertEquals(chatItem.buttons?.size ?: fail("buttons is null"), 2)
assertEquals(chatItem.buttons?.get(0)?.id ?: fail("buttons is null"), CodeTransformButtonId.ViewDiff.id)
assertEquals(chatItem.buttons?.get(0)?.text ?: fail("buttons is null"), "View diff")
assertEquals(chatItem.buttons?.get(1)?.id ?: fail("buttons is null"), CodeTransformButtonId.ViewSummary.id)
}

@Test
fun `test that transform result chat item does not include any buttons when job failed with known reason`() {
val result = CodeModernizerJobCompletedResult.JobFailed(JobId("dummy-job-id-123"), message("codemodernizer.file.invalid_pom_version"))
val chatItem = buildTransformResultChatContent(result)
assertEquals(chatItem.message, message("codemodernizer.chat.message.result.fail_with_known_reason", result.failureReason))
assertNull(chatItem.buttons)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ class CodeWhispererCodeModernizerSessionTest : CodeWhispererCodeModernizerTestBa

doNothing().whenever(testSessionStateSpy).updateJobHistory(any(), any(), any())
val result = testSessionSpy.pollUntilJobCompletion(CodeTransformType.LANGUAGE_UPGRADE, jobId) { _, _ -> }
assertEquals(CodeModernizerJobCompletedResult.JobPartiallySucceeded(jobId, testSessionContextSpy.targetJavaVersion), result)
assertEquals(CodeModernizerJobCompletedResult.JobPartiallySucceeded(jobId), result)
verify(clientAdaptorSpy, times(4)).getCodeModernizationJob(any())
verify(clientAdaptorSpy, atLeastOnce()).getCodeModernizationPlan(any())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class CodeModernizerBottomWindowPanelManagerTest : PanelTestBase() {
val layout = codeModernizerBottomWindowPanelManagerMock.layout as BorderLayout
assertThat(codeModernizerBottomWindowPanelManagerMock.toolbar.component.isVisible).isTrue()
assertThat(codeModernizerBottomWindowPanelManagerMock.fullSizeLoadingPanel.isVisible).isTrue()
assertThat(codeModernizerBottomWindowPanelManagerMock.banner.isVisible).isTrue()
assertThat(BorderLayout.WEST).isEqualTo(layout.getConstraints(codeModernizerBottomWindowPanelManagerMock.toolbar.component))
assertThat(BorderLayout.NORTH).isEqualTo(layout.getConstraints(codeModernizerBottomWindowPanelManagerMock.banner))
assertThat(layout.getLayoutComponent(BorderLayout.EAST)).isNull()
Expand Down
Loading
Loading