Skip to content

Commit

Permalink
fix(CodeTransform): show link to docs in error notifications (#4155)
Browse files Browse the repository at this point in the history
* fix(CodeTransform): show link to docs in error notifications

* fix detekt issue

* detekt issues

* more detekt issue

* detekt

* detekt issues fixed

* small comment fix

* small string change

* better message

* small messaging changes

* detekt issue

* nit: small string change

---------

Co-authored-by: David Hasani <[email protected]>
  • Loading branch information
dhasani23 and David Hasani authored Feb 29, 2024
1 parent ea25ebf commit 2e69ea8
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type" : "feature",
"description" : "Amazon Q CodeTransform: show link to docs in error notifications"
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeModernizerSessionState
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeTransformTelemetryState
import software.aws.toolkits.jetbrains.services.codemodernizer.summary.CodeModernizerSummaryEditorProvider
import software.aws.toolkits.jetbrains.utils.notifyInfo
import software.aws.toolkits.jetbrains.utils.notifyWarn
import software.aws.toolkits.jetbrains.utils.notifyStickyInfo
import software.aws.toolkits.jetbrains.utils.notifyStickyWarn
import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodeTransformPatchViewerCancelSrcComponents
import software.aws.toolkits.telemetry.CodeTransformVCSViewerSrcComponents
Expand All @@ -52,7 +52,7 @@ class ArtifactHandler(private val project: Project, private val clientAdaptor: G
}

private fun notifyDownloadStart() {
notifyInfo(
notifyStickyInfo(
message("codemodernizer.notification.info.download.started.title"),
message("codemodernizer.notification.info.download.started.content"),
project,
Expand Down Expand Up @@ -111,9 +111,11 @@ class ArtifactHandler(private val project: Project, private val clientAdaptor: G
codeTransformRunTimeLatency = calculateTotalLatency(downloadStartTime, Instant.now()),
codeTransformJobId = job.id,
codeTransformTotalByteSize = totalDownloadBytes,
codeTransformRuntimeError = telemetryErrorMessage
codeTransformRuntimeError = telemetryErrorMessage,
)
}
} catch (e: Exception) {
return DownloadArtifactResult(null, "")
} finally {
isCurrentlyDownloading.set(false)
}
Expand Down Expand Up @@ -142,51 +144,51 @@ class ArtifactHandler(private val project: Project, private val clientAdaptor: G

CodetransformTelemetry.vcsDiffViewerVisible(
codeTransformSessionId = CodeTransformTelemetryState.instance.getSessionId(),
codeTransformJobId = jobId.id
codeTransformJobId = jobId.id,
)

if (dialog.showAndGet()) {
CodetransformTelemetry.vcsViewerSubmitted(
codeTransformSessionId = CodeTransformTelemetryState.instance.getSessionId(),
codeTransformJobId = jobId.id,
codeTransformStatus = CodeModernizerSessionState.getInstance(project).currentJobStatus.toString()
codeTransformStatus = CodeModernizerSessionState.getInstance(project).currentJobStatus.toString(),
)
} else {
CodetransformTelemetry.vcsViewerCanceled(
codeTransformPatchViewerCancelSrcComponents = CodeTransformPatchViewerCancelSrcComponents.CancelButton,
codeTransformSessionId = CodeTransformTelemetryState.instance.getSessionId(),
codeTransformJobId = jobId.id,
codeTransformStatus = CodeModernizerSessionState.getInstance(project).currentJobStatus.toString()
codeTransformStatus = CodeModernizerSessionState.getInstance(project).currentJobStatus.toString(),
)
}
}
}

fun notifyUnableToApplyPatch(patchPath: String) {
LOG.error { "Unable to find patch for file: $patchPath" }
notifyWarn(
notifyStickyWarn(
message("codemodernizer.notification.warn.view_diff_failed.title"),
message("codemodernizer.notification.warn.view_diff_failed.content"),
project,
listOf(),
listOf(openTroubleshootingGuideNotificationAction(TROUBLESHOOTING_URL_DOWNLOAD_DIFF)),
)
}

fun notifyUnableToShowSummary() {
LOG.error { "Unable to display summary" }
notifyWarn(
notifyStickyWarn(
message("codemodernizer.notification.warn.view_summary_failed.title"),
message("codemodernizer.notification.warn.view_summary_failed.content"),
project,
listOf(),
listOf(openTroubleshootingGuideNotificationAction(TROUBLESHOOTING_URL_DOWNLOAD_DIFF)),
)
}

fun displayDiffAction(jobId: JobId) = runReadAction {
CodetransformTelemetry.vcsViewerClicked(
codeTransformVCSViewerSrcComponents = CodeTransformVCSViewerSrcComponents.ToastNotification,
codeTransformSessionId = CodeTransformTelemetryState.instance.getSessionId(),
codeTransformJobId = jobId.id
codeTransformJobId = jobId.id,
)
projectCoroutineScope(project).launch {
displayDiff(jobId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.toolwindow.CodeMo
import software.aws.toolkits.jetbrains.services.codemodernizer.ui.components.BuildErrorDialog
import software.aws.toolkits.jetbrains.services.codemodernizer.ui.components.PreCodeTransformUserDialog
import software.aws.toolkits.jetbrains.services.codemodernizer.ui.components.ValidationErrorDialog
import software.aws.toolkits.jetbrains.utils.actions.OpenBrowserAction
import software.aws.toolkits.jetbrains.utils.isRunningOnRemoteBackend
import software.aws.toolkits.jetbrains.utils.notifyError
import software.aws.toolkits.jetbrains.utils.notifyInfo
import software.aws.toolkits.jetbrains.utils.notifyStickyError
import software.aws.toolkits.jetbrains.utils.notifyStickyInfo
import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodeTransformCancelSrcComponents
Expand Down Expand Up @@ -286,7 +284,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
}

internal fun notifyTransformationStopped() {
notifyInfo(
notifyStickyInfo(
message("codemodernizer.notification.info.transformation_stop.title"),
message("codemodernizer.notification.info.transformation_stop.content"),
project,
Expand All @@ -302,23 +300,23 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
}

internal fun notifyTransformationStartStopping() {
notifyInfo(
notifyStickyInfo(
message("codemodernizer.notification.info.transformation_start_stopping.title"),
message("codemodernizer.notification.info.transformation_start_stopping.content"),
project,
)
}

internal fun notifyTransformationStartCannotYetStop() {
notifyError(
notifyStickyError(
message("codemodernizer.notification.info.transformation_start_stopping.failed_title"),
message("codemodernizer.notification.info.transformation_start_stopping.failed_as_job_not_started"),
project,
)
}

internal fun notifyTransformationFailedToStop(message: String) {
notifyError(
notifyStickyError(
message("codemodernizer.notification.info.transformation_start_stopping.failed_title"),
message("codemodernizer.notification.info.transformation_start_stopping.failed_content", message),
project,
Expand Down Expand Up @@ -385,7 +383,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo

is CodeModernizerStartJobResult.UnableToStartJob -> {
CodeModernizerJobCompletedResult.UnableToCreateJob(
message("codemodernizer.notification.warn.unable_to_start_job", result.exception), // TODO maybe not display the entire message
message("codemodernizer.notification.warn.unable_to_start_job", result.exception),
true,
)
}
Expand Down Expand Up @@ -533,11 +531,6 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
artifactHandler.displayDiffAction(jobId)
}

private fun openTroubleshootingGuideNotificationAction() = OpenBrowserAction(
message("codemodernizer.notification.info.view_troubleshooting_guide"),
url = "https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#transform-issues"
)

private fun displaySummaryNotificationAction(jobId: JobId) =
NotificationAction.createSimple(message("codemodernizer.notification.info.modernize_complete.view_summary")) {
artifactHandler.showTransformationSummary(jobId)
Expand All @@ -549,7 +542,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
codeTransformResultStatusMessage = result.toString(),
codeTransformRunTimeLatency = calculateTotalLatency(CodeTransformTelemetryState.instance.getStartTime(), Instant.now()),
codeTransformLocalJavaVersion = getJavaVersionFromProjectSetting(project),
codeTransformLocalMavenVersion = getMavenVersions(project),
codeTransformLocalMavenVersion = getMavenVersion(project),
)
when (result) {
is CodeModernizerJobCompletedResult.UnableToCreateJob -> notifyJobFailure(
Expand Down Expand Up @@ -593,13 +586,13 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
message("codemodernizer.notification.warn.maven_failed.title"),
message("codemodernizer.notification.warn.maven_failed.content"),
project,
listOf(openTroubleshootingGuideNotificationAction()),
listOf(openTroubleshootingGuideNotificationAction(TROUBLESHOOTING_URL_MAVEN_COMMANDS)),
)
is CodeModernizerJobCompletedResult.JobAbortedZipTooLarge -> notifyStickyInfo(
message("codemodernizer.notification.warn.zip_too_large.title"),
message("codemodernizer.notification.warn.zip_too_large.content"),
project,
listOf(openTroubleshootingGuideNotificationAction()),
listOf(openTroubleshootingGuideNotificationAction(TROUBLESHOOTING_URL_PREREQUISITES)),
)
}
}
Expand Down Expand Up @@ -636,7 +629,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
codeTransformResultStatusMessage = "JobCancelled",
codeTransformRunTimeLatency = calculateTotalLatency(CodeTransformTelemetryState.instance.getStartTime(), Instant.now()),
codeTransformLocalJavaVersion = getJavaVersionFromProjectSetting(project),
codeTransformLocalMavenVersion = getMavenVersions(project),
codeTransformLocalMavenVersion = getMavenVersion(project),
)
}
} catch (e: Exception) {
Expand All @@ -647,7 +640,7 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
codeTransformResultStatusMessage = "JobCancelled",
codeTransformRunTimeLatency = calculateTotalLatency(CodeTransformTelemetryState.instance.getStartTime(), Instant.now()),
codeTransformLocalJavaVersion = getJavaVersionFromProjectSetting(project),
codeTransformLocalMavenVersion = getMavenVersions(project),
codeTransformLocalMavenVersion = getMavenVersion(project),
)
}
}
Expand Down Expand Up @@ -680,6 +673,12 @@ class CodeModernizerManager(private val project: Project) : PersistentStateCompo
ApplicationManager.getApplication().invokeLater {
runInEdt { ValidationErrorDialog.create(maybeUnknownReason) }
}
notifyStickyInfo(
message("codemodernizer.validationerrordialog.description.title"),
message("codemodernizer.validationerrordialog.description.main"),
project,
listOf(openTroubleshootingGuideNotificationAction(TROUBLESHOOTING_URL_PREREQUISITES)),
)
}

fun getTransformationPlan(): TransformationPlan? = codeTransformationSession?.getTransformationPlan()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class CodeModernizerSession(
it.setRequestProperty(CodeWhispererCodeScanSession.SERVER_SIDE_ENCRYPTION_AWS_KMS_KEY_ID, kmsArn)
}
}
.connect { request ->
.connect { request -> // default connect timeout is 10s
val connection = request.connection as HttpURLConnection
connection.setFixedLengthStreamingMode(fileToUpload.length())
fileToUpload.inputStream().use { inputStream ->
Expand Down Expand Up @@ -416,7 +416,6 @@ class CodeModernizerSession(
TransformationStatus.FAILED,
)
) {
// val response = TODO()
val summary = TransformationSummary(
"""
# Transformation summary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,15 @@

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

import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.ProcessNotCreatedException
import com.intellij.execution.util.ExecUtil
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.serviceContainer.AlreadyDisposedException
import org.jetbrains.idea.maven.project.MavenProjectsManager
import org.jetbrains.idea.maven.utils.MavenUtil
import org.jetbrains.plugins.gradle.settings.GradleSettings
import org.slf4j.LoggerFactory
import software.amazon.awssdk.awscore.exception.AwsServiceException
import software.amazon.awssdk.core.exception.SdkClientException
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException
Expand All @@ -33,9 +27,7 @@ import software.aws.toolkits.core.TokenConnectionSettings
import software.aws.toolkits.core.utils.WaiterUnrecoverableException
import software.aws.toolkits.core.utils.Waiters.waitUntil
import software.aws.toolkits.core.utils.createParentDirectories
import software.aws.toolkits.core.utils.error
import software.aws.toolkits.core.utils.exists
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProvider
Expand All @@ -47,6 +39,8 @@ import software.aws.toolkits.jetbrains.services.codemodernizer.client.GumbyClien
import software.aws.toolkits.jetbrains.services.codemodernizer.model.JobId
import software.aws.toolkits.jetbrains.services.codemodernizer.model.MAVEN_CONFIGURATION_FILE_NAME
import software.aws.toolkits.jetbrains.services.codemodernizer.state.CodeTransformTelemetryState
import software.aws.toolkits.jetbrains.utils.actions.OpenBrowserAction
import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodetransformTelemetry
import java.io.File
import java.io.FileOutputStream
Expand Down Expand Up @@ -94,7 +88,12 @@ val TERMINAL_STATES = setOf(
TransformationStatus.COMPLETED,
)

private val LOG = LoggerFactory.getLogger("CodeTransformUtils")
const val TROUBLESHOOTING_URL_DOWNLOAD_DIFF =
"https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#w24aac14c20c19c11"
const val TROUBLESHOOTING_URL_MAVEN_COMMANDS =
"https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/troubleshooting-code-transformation.html#w24aac14c20c19b7"
const val TROUBLESHOOTING_URL_PREREQUISITES =
"https://docs.aws.amazon.com/amazonq/latest/aws-builder-use-ug/code-transformation.html#prerequisites"

fun String.toVirtualFile() = VirtualFileManager.getInstance().findFileByUrl(VfsUtilCore.pathToUrl(this))
fun Project.moduleFor(path: String) = ModuleUtil.findModuleForFile(
Expand Down Expand Up @@ -287,52 +286,13 @@ fun isGradleProject(project: Project) = !GradleSettings.getInstance(project).lin

fun getJavaVersionFromProjectSetting(project: Project): String? = project.tryGetJdk()?.toString()

fun getMavenVersions(project: Project): String {
fun getVersion(mavenCommand: String): String? {
try {
val commandLine = GeneralCommandLine(listOf(mavenCommand, "-v"))
.withWorkDirectory(project.basePath)
.withRedirectErrorStream(true)
val output = ExecUtil.execAndGetOutput(commandLine)
if (output.exitCode == 0) {
return parseMavenVersion(output.stdout)
} else {
LOG.error { "Failed to fetch $mavenCommand version: ${output.stdout}" }
}
} catch (e: ProcessNotCreatedException) {
LOG.warn { "$mavenCommand not set up" }
} catch (e: Exception) {
LOG.error(e) { "Failed to fetch $mavenCommand version" }
}
return null
}

// Get local maven version
val localMavenVersion: String? = getVersion("mvn")

// Get wrapper maven version
val mvnw = if (SystemInfo.isWindows) "./mvnw.cmd" else "./mvnw"
val wrapperMavenVersion: String? = getVersion(mvnw)

// Get user's Maven setting (using bundled vs local vs wrapper)
fun getMavenVersion(project: Project): String {
val mavenSettings = MavenProjectsManager.getInstance(project).getGeneralSettings()
val mavenHome = mavenSettings.getMavenHome()
// Need to detect bundled Maven version that come with IDEA
// The utility returns "Use Maven wrapper" if using wrapper, "Bundled (Maven 3)" if using Bundled Maven, otherwise the local maven version.
val userMavenSetting = MavenUtil.getMavenVersion(mavenHome) ?: mavenHome

return "$wrapperMavenVersion (mvnw) -- $localMavenVersion (mvn) -- user setting: $userMavenSetting"
// should be set to "Bundled (Maven X)" if setup instructions were followed
return mavenSettings.getMavenHome() ?: "Unknown"
}

private fun parseMavenVersion(output: String?): String? {
if (output == null) return null
val mavenVersionIndex = output.indexOf("Apache Maven")
if (mavenVersionIndex == -1) return null
return try {
val mavenVersionString = output.slice(IntRange(mavenVersionIndex + 13, output.length - 1))
mavenVersionString.slice(IntRange(0, output.indexOf(' ') - 1))
} catch (e: StringIndexOutOfBoundsException) {
LOG.error(e) { "Failed to parse Maven version from output: $output" }
null
}
}
fun openTroubleshootingGuideNotificationAction(targetUrl: String) = OpenBrowserAction(
message("codemodernizer.notification.info.view_troubleshooting_guide"),
url = targetUrl
)
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ open class CodeModernizerArtifact(
val manifest = loadManifest()
if (manifest.version > maxSupportedVersion) {
// If not supported we can still try to use it, i.e. the versions should largely be backwards compatible
// TODO change to notify that user should consider upgrading the toolkit version.
// Can also notify user to consider upgrading the toolkit version.
LOG.warn { "Unsupported version: ${manifest.version}" }
}
val patches = extractPatches(manifest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ data class CodeModernizerSessionContext(
}

/**
* TODO use an approach based on walkTopDown instead of VfsUtil.collectChildrenRecursively(root) in createZipWithModuleFiles.
* Can eventually modify to use an approach based on walkTopDown instead of VfsUtil.collectChildrenRecursively(root) in createZipWithModuleFiles.
* We now recurse the file tree twice and then filter which hurts performance for large projects.
*/
private fun findDirectoriesToExclude(sourceFolder: File): List<File> {
Expand Down
Loading

0 comments on commit 2e69ea8

Please sign in to comment.