@@ -22,10 +22,11 @@ import software.aws.toolkits.core.utils.debug
22
22
import software.aws.toolkits.core.utils.getLogger
23
23
import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
24
24
import software.aws.toolkits.jetbrains.services.amazonq.calculateIfIamIdentityCenterConnection
25
+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QProfileSwitchIntent
25
26
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
27
+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
26
28
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener
27
29
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
28
- import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
29
30
import software.aws.toolkits.jetbrains.utils.notifyInfo
30
31
import software.aws.toolkits.jetbrains.utils.notifyWarn
31
32
import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
@@ -108,25 +109,24 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
108
109
@RequiresBackgroundThread
109
110
override fun listCustomizations (project : Project , passive : Boolean ): List <CustomizationUiItem >? =
110
111
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())
125
125
}
126
126
127
127
// 2. get diff
128
128
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()
130
130
131
131
// 3 if passive,
132
132
// (1) update allowlisting
@@ -135,42 +135,45 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
135
135
// if not passive,
136
136
// (1) update the customization list snapshot (seen by users last time) if it will be displayed
137
137
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 )) {
140
140
notifyNewCustomization(project)
141
141
}
142
142
} 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()
146
144
}
147
145
148
146
// 4. invalidate selected customization if
149
147
// (1) the API call failed
150
148
// (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
151
150
activeCustomization(project)?.let { activeCustom ->
152
- if (listAvailableCustomizationsResult == null ) {
151
+ if (customizations.isEmpty() ) {
153
152
invalidateSelectedAndNotify(project)
154
- } else if (! listAvailableCustomizationsResult.any { latestCustom -> latestCustom.arn == activeCustom.arn }) {
153
+ } else if (customizations.none { latestCustom -> latestCustom.arn == activeCustom.arn }) {
155
154
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
+ }
156
166
}
157
167
}
158
168
159
169
// 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
+ )
174
177
}
175
178
connectionToCustomizationUiItems[it.id] = customizationUiItems
176
179
@@ -212,6 +215,18 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
212
215
213
216
LOG .debug { " Switch from customization $oldCus to $newCustomization " }
214
217
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
+
215
230
CodeWhispererCustomizationListener .notifyCustomUiUpdate()
216
231
}
217
232
if (isOverride) {
0 commit comments