Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -264,23 +264,6 @@ class QWebviewBrowser(val project: Project, private val parentDisposable: Dispos
// TODO: pass "REAUTH" if connection expires
// Perform the potentially blocking AWS call outside the EDT to fetch available region profiles.
ApplicationManager.getApplication().executeOnPooledThread {
var errorMessage: String? = null
val profiles: List<QRegionProfile> = try {
QRegionProfileManager.getInstance().listRegionProfiles(project).orEmpty()
} catch (e: Exception) {
errorMessage = e.message
LOG.warn { "Failed to call listRegionProfiles API" }
val qConn = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance())
Telemetry.amazonq.didSelectProfile.use { span ->
span.source(QProfileSwitchIntent.Auth.value)
.amazonQProfileRegion(QRegionProfileManager.getInstance().activeProfile(project)?.region ?: "not-set")
.ssoRegion((qConn as? AwsBearerTokenConnection)?.region)
.credentialStartUrl((qConn as? AwsBearerTokenConnection)?.startUrl)
.result(MetricResult.Failed)
.reason(e.message)
}
emptyList()
}

val stage = if (isQExpired(project)) {
"REAUTH"
Expand All @@ -290,6 +273,27 @@ class QWebviewBrowser(val project: Project, private val parentDisposable: Dispos
"START"
}

var errorMessage: String? = null
var profiles: List<QRegionProfile> = emptyList()

if (stage == "PROFILE_SELECT") {
try {
profiles = QRegionProfileManager.getInstance().listRegionProfiles(project).orEmpty()
} catch (e: Exception) {
errorMessage = e.message
LOG.warn { "Failed to call listRegionProfiles API" }
val qConn = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance())
Telemetry.amazonq.didSelectProfile.use { span ->
span.source(QProfileSwitchIntent.Auth.value)
.amazonQProfileRegion(QRegionProfileManager.getInstance().activeProfile(project)?.region ?: "not-set")
.ssoRegion((qConn as? AwsBearerTokenConnection)?.region)
.credentialStartUrl((qConn as? AwsBearerTokenConnection)?.startUrl)
.result(MetricResult.Failed)
.reason(e.message)
}
}
}

val jsonData = """
{
stage: '$stage',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.profile

import software.amazon.awssdk.services.codewhispererruntime.CodeWhispererRuntimeClient
import software.aws.toolkits.core.ClientConnectionSettings
import software.aws.toolkits.jetbrains.core.AwsClientManager
import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider
import software.aws.toolkits.jetbrains.core.Resource
import java.time.Duration

/**
* Save Amazon Q Profile Resource Cache
*/
object QProfileResources {
/**
* save available Q Profile list as cache with default duration 60 s。
*/
val LIST_REGION_PROFILES = object : Resource.Cached<List<QRegionProfile>>() {
override val id: String = "amazonq.allProfiles"

override fun fetch(connectionSettings: ClientConnectionSettings<*>): List<QRegionProfile> {
val mappedProfiles = QEndpoints.listRegionEndpoints().flatMap { (regionKey, _) ->
val awsRegion = AwsRegionProvider.getInstance()[regionKey] ?: return@flatMap emptyList()
val client = AwsClientManager
.getInstance()
.getClient(CodeWhispererRuntimeClient::class, connectionSettings.withRegion(awsRegion))

client.listAvailableProfilesPaginator {}
.profiles()
.map { p -> QRegionProfile(arn = p.arn(), profileName = p.profileName() ?: "<no name>") }
}
return mappedProfiles
}

override fun expiry(): Duration = Duration.ofSeconds(60)
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this be too low?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for bringing this up! Do you have a value in mind that you think would be more appropriate?

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import com.intellij.openapi.project.Project
import com.intellij.util.xmlb.annotations.MapAnnotation
import com.intellij.util.xmlb.annotations.Property
import software.amazon.awssdk.core.SdkClient
import software.amazon.awssdk.services.codewhispererruntime.CodeWhispererRuntimeClient
import software.aws.toolkits.core.TokenConnectionSettings
import software.aws.toolkits.core.utils.debug
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.tryOrNull
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.core.AwsClientManager
import software.aws.toolkits.jetbrains.core.awsClient
import software.aws.toolkits.jetbrains.core.AwsResourceCache
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
Expand All @@ -31,6 +30,7 @@ import software.aws.toolkits.jetbrains.utils.notifyInfo
import software.aws.toolkits.resources.AmazonQBundle.message
import software.aws.toolkits.telemetry.MetricResult
import software.aws.toolkits.telemetry.Telemetry
import java.time.Duration
import java.util.Collections
import kotlin.reflect.KClass

Expand Down Expand Up @@ -66,16 +66,14 @@ class QRegionProfileManager : PersistentStateComponent<QProfileState>, Disposabl
fun listRegionProfiles(project: Project): List<QRegionProfile>? {
val connection = getIdcConnectionOrNull(project) ?: return null
return try {
val mappedProfiles = QEndpoints.listRegionEndpoints()
.flatMap { (regionKey, _) ->
val awsRegion = AwsRegionProvider.getInstance()[regionKey] ?: return@flatMap emptyList()
connection.getConnectionSettings()
.withRegion(awsRegion)
.awsClient<CodeWhispererRuntimeClient>()
.listAvailableProfilesPaginator {}
.profiles()
.map { p -> QRegionProfile(arn = p.arn(), profileName = p.profileName()) }
}
val connectionSettings = connection.getConnectionSettings()
val mappedProfiles = AwsResourceCache.getInstance().getResourceNow(
resource = QProfileResources.LIST_REGION_PROFILES,
connectionSettings = connectionSettings,
timeout = Duration.ofSeconds(30),
useStale = true,
forceFetch = false
)
if (mappedProfiles.size == 1) {
switchProfile(project, mappedProfiles.first(), intent = QProfileSwitchIntent.Update)
}
Expand Down
Loading