suspend fun run()

in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanSession.kt [81:250]


    suspend fun run(): CodeScanResponse {
        var codeScanResponseContext = defaultCodeScanResponseContext()
        val currentCoroutineContext = coroutineContext
        try {
            assertIsNonDispatchThread()
            currentCoroutineContext.ensureActive()
            val startTime = now()
            val (payloadContext, sourceZip) = withTimeout(Duration.ofSeconds(sessionContext.sessionConfig.createPayloadTimeoutInSeconds())) {
                sessionContext.sessionConfig.createPayload()
            }

            if (isProjectScope()) {
                LOG.debug {
                    "Total size of source payload in KB: ${payloadContext.srcPayloadSize * 1.0 / TOTAL_BYTES_IN_KB} \n" +
                        "Total size of source zip file in KB: ${payloadContext.srcZipFileSize * 1.0 / TOTAL_BYTES_IN_KB} \n" +
                        "Total number of lines reviewed: ${payloadContext.totalLines} \n" +
                        "Total number of files included in payload: ${payloadContext.totalFiles} \n" +
                        "Total time taken for creating payload: ${payloadContext.totalTimeInMilliseconds * 1.0 / TOTAL_MILLIS_IN_SECOND} seconds\n" +
                        "Payload context language: ${payloadContext.language}"
                }
            }
            codeScanResponseContext = codeScanResponseContext.copy(payloadContext = payloadContext)

            // 2 & 3. CreateUploadURL and upload the context.
            currentCoroutineContext.ensureActive()
            val artifactsUploadStartTime = now()
            val codeScanName = UUID.randomUUID().toString()

            val taskType = if (isAutoScan()) {
                CodeWhispererConstants.UploadTaskType.SCAN_FILE
            } else {
                CodeWhispererConstants.UploadTaskType.SCAN_PROJECT
            }

            val sourceZipUploadResponse =
                CodeWhispererZipUploadManager.getInstance(sessionContext.project).createUploadUrlAndUpload(
                    sourceZip,
                    "SourceCode",
                    taskType,
                    codeScanName,
                    CodeWhispererConstants.FeatureName.CODE_REVIEW
                )
            if (isProjectScope()) {
                LOG.debug {
                    "Successfully uploaded source zip to s3: " +
                        "Upload id: ${sourceZipUploadResponse.uploadId()} " +
                        "Request id: ${sourceZipUploadResponse.responseMetadata().requestId()}"
                }
            }
            urlResponse[ArtifactType.SOURCE_CODE] = sourceZipUploadResponse
            currentCoroutineContext.ensureActive()
            val artifactsUploadDuration = now() - artifactsUploadStartTime
            codeScanResponseContext = codeScanResponseContext.copy(
                serviceInvocationContext = codeScanResponseContext.serviceInvocationContext.copy(artifactsUploadDuration = artifactsUploadDuration)
            )

            // 4. Call createCodeScan to start a code scan
            currentCoroutineContext.ensureActive()
            val serviceInvocationStartTime = now()
            val createCodeScanResponse = createCodeScan(payloadContext.language.toString(), codeScanName)
            if (isProjectScope()) {
                LOG.debug {
                    "Successfully created code review with " +
                        "status: ${createCodeScanResponse.status()} " +
                        "for request id: ${createCodeScanResponse.responseMetadata().requestId()}"
                }
            }
            var codeScanStatus = createCodeScanResponse.status()
            if (codeScanStatus == CodeAnalysisStatus.FAILED) {
                if (isProjectScope()) {
                    LOG.debug {
                        "CodeWhisperer service error occurred. Something went wrong when creating a code review: $createCodeScanResponse " +
                            "Status: ${createCodeScanResponse.status()} for request id: ${createCodeScanResponse.responseMetadata().requestId()}"
                    }
                }
                val errorMessage = createCodeScanResponse.errorMessage()?.let { it } ?: message("codewhisperer.codescan.run_scan_error_telemetry")
                codeScanFailed(errorMessage)
            }
            val jobId = createCodeScanResponse.jobId()
            codeScanResponseContext = codeScanResponseContext.copy(codeScanJobId = jobId)
            if (isProjectScope()) {
                delay(PROJECT_SCAN_INITIAL_POLLING_INTERVAL_IN_SECONDS * TOTAL_MILLIS_IN_SECOND)
            } else {
                delay(FILE_SCAN_INITIAL_POLLING_INTERVAL_IN_SECONDS * TOTAL_MILLIS_IN_SECOND)
            }

            // 5. Keep polling the API GetCodeScan to wait for results for a given timeout period.
            waitUntil(
                succeedOn = { codeScanStatus == CodeAnalysisStatus.COMPLETED },
                maxDuration = Duration.ofSeconds(sessionContext.sessionConfig.overallJobTimeoutInSeconds())
            ) {
                currentCoroutineContext.ensureActive()
                val elapsedTime = (now() - startTime) * 1.0 / TOTAL_MILLIS_IN_SECOND
                if (isProjectScope()) {
                    LOG.debug { "Waiting for code review to complete. Elapsed time: $elapsedTime sec." }
                }
                val getCodeScanResponse = getCodeScan(jobId)
                codeScanStatus = getCodeScanResponse.status()
                if (isProjectScope()) {
                    LOG.debug {
                        "Get code review status: ${getCodeScanResponse.status()}, " +
                            "request id: ${getCodeScanResponse.responseMetadata().requestId()}"
                    }
                }
                delay(CODE_SCAN_POLLING_INTERVAL_IN_SECONDS * TOTAL_MILLIS_IN_SECOND)
                if (codeScanStatus == CodeAnalysisStatus.FAILED) {
                    if (isProjectScope()) {
                        LOG.debug {
                            "CodeWhisperer service error occurred. Something went wrong fetching results for code review: $getCodeScanResponse " +
                                "Status: ${getCodeScanResponse.status()} for request id: ${getCodeScanResponse.responseMetadata().requestId()}"
                        }
                    }
                    val errorMessage = getCodeScanResponse.errorMessage()?.let { it } ?: message("codewhisperer.codescan.run_scan_error_telemetry")
                    codeScanFailed(errorMessage)
                }
            }

            LOG.debug { "Code review completed successfully by Amazon Q." }

            // 6. Return the results from the ListCodeScan API.
            currentCoroutineContext.ensureActive()
            var listCodeScanFindingsResponse = listCodeScanFindings(jobId, null)
            val serviceInvocationDuration = now() - serviceInvocationStartTime
            codeScanResponseContext = codeScanResponseContext.copy(
                serviceInvocationContext = codeScanResponseContext.serviceInvocationContext.copy(serviceInvocationDuration = serviceInvocationDuration)
            )

            val documents = mutableListOf<String>()
            documents.add(listCodeScanFindingsResponse.codeAnalysisFindings())
            // coroutineContext helps to actively cancel the bigger projects quickly
            withContext(currentCoroutineContext) {
                while (listCodeScanFindingsResponse.nextToken() != null && currentCoroutineContext.isActive) {
                    listCodeScanFindingsResponse = listCodeScanFindings(jobId, listCodeScanFindingsResponse.nextToken())
                    documents.add(listCodeScanFindingsResponse.codeAnalysisFindings())
                }
            }

            if (isProjectScope()) {
                LOG.debug { "Rendering response to display code review results." }
            }
            currentCoroutineContext.ensureActive()
            val issues = mapToCodeScanIssues(documents, sessionContext.project, jobId).filter { it.isVisible }
            codeScanResponseContext = codeScanResponseContext.copy(codeScanTotalIssues = issues.count())
            codeScanResponseContext = codeScanResponseContext.copy(codeScanIssuesWithFixes = issues.count { it.suggestedFixes.isNotEmpty() })
            codeScanResponseContext = codeScanResponseContext.copy(reason = "Succeeded")
            return CodeScanResponse.Success(issues, codeScanResponseContext)
        } catch (e: Exception) {
            val exception = e as? CodeWhispererRuntimeException
            val awsError = exception?.awsErrorDetails()

            if (awsError != null) {
                if (awsError.errorCode() == "ThrottlingException" && awsError.errorMessage() != null) {
                    if (awsError.errorMessage()!!.contains(PROJECT_SCANS_THROTTLING_MESSAGE)) {
                        LOG.info { "Project reviews limit reached" }
                        notifyErrorCodeWhispererUsageLimit(sessionContext.project, true)
                    } else if (awsError.errorMessage()!!.contains(FILE_SCANS_THROTTLING_MESSAGE)) {
                        LOG.info { "File reviews limit reached" }
                        CodeWhispererExplorerActionManager.getInstance().setMonthlyQuotaForCodeScansExceeded(true)
                    }
                }
            }
            LOG.debug(e) {
                "Failed to run code review and display results. Caused by: ${e.message}, status code: ${awsError?.errorCode()}, " +
                    "exception: ${e::class.simpleName}, request ID: ${exception?.requestId()}" +
                    "Jetbrains IDE: ${ApplicationInfo.getInstance().fullApplicationName}, " +
                    "IDE version: ${ApplicationInfo.getInstance().apiVersion}, "
            }
            return CodeScanResponse.Failure(codeScanResponseContext, e)
        }
    }