@@ -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
@@ -106,25 +107,24 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
106107 @RequiresBackgroundThread
107108 override fun listCustomizations (project : Project , passive : Boolean ): List <CustomizationUiItem >? =
108109 calculateIfIamIdentityCenterConnection(project) {
109- // 1. invoke API and get result
110- val listAvailableCustomizationsResult = try {
111- CodeWhispererClientAdaptor .getInstance(project).listAvailableCustomizations()
112- } catch (e: Exception ) {
113- val requestId = (e as ? CodeWhispererRuntimeException )?.requestId()
114- val logMessage = if (CodeWhispererConstants .Customization .noAccessToCustomizationExceptionPredicate(e)) {
115- // TODO: not required for non GP users
116- " ListAvailableCustomizations: connection ${it.id} is not allowlisted, requestId: ${requestId.orEmpty()} "
117- } else {
118- " ListAvailableCustomizations: failed due to unknown error ${e.message} , requestId: ${requestId.orEmpty()} "
119- }
120-
121- LOG .debug { logMessage }
122- null
110+ // 1. fetch all profiles, invoke fetch customizations API and get result for each profile and aggregate all the results
111+ val profiles = QRegionProfileManager .getInstance().listRegionProfiles(project)
112+ ? : error(" Attempted to fetch profiles while there does not exist" )
113+
114+ val customizations = profiles.flatMap { profile ->
115+ runCatching {
116+ CodeWhispererClientAdaptor .getInstance(project).listAvailableCustomizations(profile)
117+ }.onFailure { e ->
118+ val requestId = (e as ? CodeWhispererRuntimeException )?.requestId()
119+ val logMessage = " ListAvailableCustomizations: failed due to unknown error ${e.message} , " +
120+ " requestId: ${requestId.orEmpty()} , profileName: ${profile.profileName} "
121+ LOG .debug { logMessage }
122+ }.getOrDefault(emptyList())
123123 }
124124
125125 // 2. get diff
126126 val previousCustomizationsShapshot = connectionToCustomizationsShownLastTime.getOrElse(it.id) { emptyList() }
127- val diff = listAvailableCustomizationsResult? .filterNot { customization -> previousCustomizationsShapshot.contains(customization.arn) }? .toSet()
127+ val diff = customizations .filterNot { customization -> previousCustomizationsShapshot.contains(customization.arn) }.toSet()
128128
129129 // 3 if passive,
130130 // (1) update allowlisting
@@ -133,42 +133,45 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
133133 // if not passive,
134134 // (1) update the customization list snapshot (seen by users last time) if it will be displayed
135135 if (passive) {
136- connectionIdToIsAllowlisted[it.id] = listAvailableCustomizationsResult != null
137- if (diff? .isNotEmpty() == true && ! hasShownNewCustomizationNotification.getAndSet(true )) {
136+ connectionIdToIsAllowlisted[it.id] = customizations.isNotEmpty()
137+ if (diff.isNotEmpty() && ! hasShownNewCustomizationNotification.getAndSet(true )) {
138138 notifyNewCustomization(project)
139139 }
140140 } else {
141- listAvailableCustomizationsResult?.let { customizations ->
142- connectionToCustomizationsShownLastTime[it.id] = customizations.map { customization -> customization.arn }.toMutableList()
143- }
141+ connectionToCustomizationsShownLastTime[it.id] = customizations.map { customization -> customization.arn }.toMutableList()
144142 }
145143
146144 // 4. invalidate selected customization if
147145 // (1) the API call failed
148146 // (2) the selected customization is not in the resultset of API call
147+ // (3) the existing q region profile associated with the selected customization does not match the currently active profile
149148 activeCustomization(project)?.let { activeCustom ->
150- if (listAvailableCustomizationsResult == null ) {
149+ if (customizations.isEmpty() ) {
151150 invalidateSelectedAndNotify(project)
152- } else if (! listAvailableCustomizationsResult.any { latestCustom -> latestCustom.arn == activeCustom.arn }) {
151+ } else if (customizations.none { latestCustom -> latestCustom.arn == activeCustom.arn }) {
153152 invalidateSelectedAndNotify(project)
153+ } else {
154+ // for backward compatibility, previous schema didn't have profile arn, so backfill profile here if it's null
155+ if (activeCustom.profile == null ) {
156+ customizations.find { c -> c.arn == activeCustom.arn }?.profile?.let { p ->
157+ activeCustom.profile = p
158+ }
159+ }
160+
161+ if (activeCustom.profile != null && activeCustom.profile != QRegionProfileManager .getInstance().activeProfile(project)) {
162+ invalidateSelectedAndNotify(project)
163+ }
154164 }
155165 }
156166
157167 // 5. transform result to UI items and return
158- val customizationUiItems = if (diff != null ) {
159- listAvailableCustomizationsResult.let { customizations ->
160- val nameToCount = customizations.groupingBy { customization -> customization.name }.eachCount()
161-
162- customizations.map { customization ->
163- CustomizationUiItem (
164- customization,
165- isNew = diff.contains(customization),
166- shouldPrefixAccountId = (nameToCount[customization.name] ? : 0 ) > 1
167- )
168- }
169- }
170- } else {
171- null
168+ val nameToCount = customizations.groupingBy { customization -> customization.name }.eachCount()
169+ val customizationUiItems = customizations.map { customization ->
170+ CustomizationUiItem (
171+ customization,
172+ isNew = diff.contains(customization),
173+ shouldPrefixAccountId = (nameToCount[customization.name] ? : 0 ) > 1
174+ )
172175 }
173176 connectionToCustomizationUiItems[it.id] = customizationUiItems
174177
@@ -210,6 +213,18 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
210213
211214 LOG .debug { " Switch from customization $oldCus to $newCustomization " }
212215
216+ // Switch profile if it doesn't match the customization's profile.
217+ // Customizations are profile-scoped and must be used under the correct context.
218+ newCustomization?.profile?.let { p ->
219+ if (p.arn != QRegionProfileManager .getInstance().activeProfile(project)?.arn) {
220+ QRegionProfileManager .getInstance().switchProfile(
221+ project,
222+ p,
223+ QProfileSwitchIntent .Customization
224+ )
225+ }
226+ }
227+
213228 CodeWhispererCustomizationListener .notifyCustomUiUpdate()
214229 }
215230 if (isOverride) {
0 commit comments