@@ -22,10 +22,11 @@ import software.aws.toolkits.core.utils.debug
2222import software.aws.toolkits.core.utils.getLogger
2323import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
2424import software.aws.toolkits.jetbrains.services.amazonq.calculateIfIamIdentityCenterConnection
25+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QProfileSwitchIntent
2526import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
27+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
2628import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener
2729import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
28- import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
2930import software.aws.toolkits.jetbrains.utils.notifyInfo
3031import software.aws.toolkits.jetbrains.utils.notifyWarn
3132import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
@@ -108,25 +109,24 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
108109 @RequiresBackgroundThread
109110 override fun listCustomizations (project : Project , passive : Boolean ): List <CustomizationUiItem >? =
110111 calculateIfIamIdentityCenterConnection(project) {
111- // 1. invoke API and get result
112- val listAvailableCustomizationsResult = try {
113- CodeWhispererClientAdaptor .getInstance(project).listAvailableCustomizations()
114- } catch (e: Exception ) {
115- val requestId = (e as ? CodeWhispererRuntimeException )?.requestId()
116- val logMessage = if (CodeWhispererConstants .Customization .noAccessToCustomizationExceptionPredicate(e)) {
117- // TODO: not required for non GP users
118- " ListAvailableCustomizations: connection ${it.id} is not allowlisted, requestId: ${requestId.orEmpty()} "
119- } else {
120- " ListAvailableCustomizations: failed due to unknown error ${e.message} , requestId: ${requestId.orEmpty()} "
121- }
122-
123- LOG .debug { logMessage }
124- null
112+ // 1. fetch all profiles, invoke fetch customizations API and get result for each profile and aggregate all the results
113+ val profiles = QRegionProfileManager .getInstance().listRegionProfiles(project)
114+ ? : error(" Attempted to fetch profiles while there does not exist" )
115+
116+ val customizations = profiles.flatMap { profile ->
117+ runCatching {
118+ CodeWhispererClientAdaptor .getInstance(project).listAvailableCustomizations(profile)
119+ }.onFailure { e ->
120+ val requestId = (e as ? CodeWhispererRuntimeException )?.requestId()
121+ val logMessage = " ListAvailableCustomizations: failed due to unknown error ${e.message} , " +
122+ " requestId: ${requestId.orEmpty()} , profileName: ${profile.profileName} "
123+ LOG .debug { logMessage }
124+ }.getOrDefault(emptyList())
125125 }
126126
127127 // 2. get diff
128128 val previousCustomizationsShapshot = connectionToCustomizationsShownLastTime.getOrElse(it.id) { emptyList() }
129- val diff = listAvailableCustomizationsResult? .filterNot { customization -> previousCustomizationsShapshot.contains(customization.arn) }? .toSet()
129+ val diff = customizations .filterNot { customization -> previousCustomizationsShapshot.contains(customization.arn) }.toSet()
130130
131131 // 3 if passive,
132132 // (1) update allowlisting
@@ -135,42 +135,45 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
135135 // if not passive,
136136 // (1) update the customization list snapshot (seen by users last time) if it will be displayed
137137 if (passive) {
138- connectionIdToIsAllowlisted[it.id] = listAvailableCustomizationsResult != null
139- if (diff? .isNotEmpty() == true && ! hasShownNewCustomizationNotification.getAndSet(true )) {
138+ connectionIdToIsAllowlisted[it.id] = customizations.isNotEmpty()
139+ if (diff.isNotEmpty() && ! hasShownNewCustomizationNotification.getAndSet(true )) {
140140 notifyNewCustomization(project)
141141 }
142142 } else {
143- listAvailableCustomizationsResult?.let { customizations ->
144- connectionToCustomizationsShownLastTime[it.id] = customizations.map { customization -> customization.arn }.toMutableList()
145- }
143+ connectionToCustomizationsShownLastTime[it.id] = customizations.map { customization -> customization.arn }.toMutableList()
146144 }
147145
148146 // 4. invalidate selected customization if
149147 // (1) the API call failed
150148 // (2) the selected customization is not in the resultset of API call
149+ // (3) the existing q region profile associated with the selected customization does not match the currently active profile
151150 activeCustomization(project)?.let { activeCustom ->
152- if (listAvailableCustomizationsResult == null ) {
151+ if (customizations.isEmpty() ) {
153152 invalidateSelectedAndNotify(project)
154- } else if (! listAvailableCustomizationsResult.any { latestCustom -> latestCustom.arn == activeCustom.arn }) {
153+ } else if (customizations.none { latestCustom -> latestCustom.arn == activeCustom.arn }) {
155154 invalidateSelectedAndNotify(project)
155+ } else {
156+ // for backward compatibility, previous schema didn't have profile arn, so backfill profile here if it's null
157+ if (activeCustom.profile == null ) {
158+ customizations.find { c -> c.arn == activeCustom.arn }?.profile?.let { p ->
159+ activeCustom.profile = p
160+ }
161+ }
162+
163+ if (activeCustom.profile != null && activeCustom.profile != QRegionProfileManager .getInstance().activeProfile(project)) {
164+ invalidateSelectedAndNotify(project)
165+ }
156166 }
157167 }
158168
159169 // 5. transform result to UI items and return
160- val customizationUiItems = if (diff != null ) {
161- listAvailableCustomizationsResult.let { customizations ->
162- val nameToCount = customizations.groupingBy { customization -> customization.name }.eachCount()
163-
164- customizations.map { customization ->
165- CustomizationUiItem (
166- customization,
167- isNew = diff.contains(customization),
168- shouldPrefixAccountId = (nameToCount[customization.name] ? : 0 ) > 1
169- )
170- }
171- }
172- } else {
173- null
170+ val nameToCount = customizations.groupingBy { customization -> customization.name }.eachCount()
171+ val customizationUiItems = customizations.map { customization ->
172+ CustomizationUiItem (
173+ customization,
174+ isNew = diff.contains(customization),
175+ shouldPrefixAccountId = (nameToCount[customization.name] ? : 0 ) > 1
176+ )
174177 }
175178 connectionToCustomizationUiItems[it.id] = customizationUiItems
176179
@@ -212,6 +215,18 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
212215
213216 LOG .debug { " Switch from customization $oldCus to $newCustomization " }
214217
218+ // Switch profile if it doesn't match the customization's profile.
219+ // Customizations are profile-scoped and must be used under the correct context.
220+ newCustomization?.profile?.let { p ->
221+ if (p.arn != QRegionProfileManager .getInstance().activeProfile(project)?.arn) {
222+ QRegionProfileManager .getInstance().switchProfile(
223+ project,
224+ p,
225+ QProfileSwitchIntent .Customization
226+ )
227+ }
228+ }
229+
215230 CodeWhispererCustomizationListener .notifyCustomUiUpdate()
216231 }
217232 if (isOverride) {
0 commit comments