@@ -65,6 +65,13 @@ private fun notifyNewCustomization(project: Project) {
6565 )
6666}
6767
68+ /* *
69+ * generate a stable composite key for profile & customization combination for diff/deduplicate/search
70+ * rule: `<profileArn>::<customizationArn>`
71+ */
72+ private fun CodeWhispererCustomization.compositeKey (): String =
73+ " ${profile?.arn.orEmpty()} ::$arn "
74+
6875@Service(Service .Level .APP )
6976@State(name = " codewhispererCustomizationStates" , storages = [Storage (" aws.xml" )])
7077class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator , PersistentStateComponent <CodeWhispererCustomizationState >, Disposable {
@@ -73,7 +80,7 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
7380 private val connectionIdToActiveCustomizationArn = Collections .synchronizedMap<String , CodeWhispererCustomization >(mutableMapOf ())
7481
7582 // Map to store connectionId to its listAvailableCustomizations result last time
76- // the customization has format profileArn::customizationArn
83+ // Format of the customization key: profileArn::customizationArn
7784 private val connectionToCustomizationsShownLastTime = mutableMapOf<String , MutableList <String >>()
7885
7986 private val connectionIdToIsAllowlisted = Collections .synchronizedMap<String , Boolean >(mutableMapOf ())
@@ -111,35 +118,32 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
111118 @RequiresBackgroundThread
112119 override fun listCustomizations (project : Project , passive : Boolean ): List <CustomizationUiItem >? =
113120 calculateIfIamIdentityCenterConnection(project) {
114- // 1. invoke API and get result
121+ // 1. fetch all profiles, invoke fetch customizations API and get result for each profile and aggregate all the results
115122 val listAvailableProfilesResult = tryOrNull { QRegionProfileManager .getInstance().listRegionProfiles(project) }.orEmpty()
116123
117- val aggregatedCustomizations = mutableListOf<CodeWhispererCustomization >()
118-
119- for (profile in listAvailableProfilesResult) {
120- try {
121- val perProfileCustomizations = CodeWhispererClientAdaptor .getInstance(project).listAvailableCustomizations(profile)
122- aggregatedCustomizations.addAll(perProfileCustomizations)
123- } catch (e: Exception ) {
124+ val aggregatedCustomizations = listAvailableProfilesResult.flatMap { profile ->
125+ runCatching {
126+ CodeWhispererClientAdaptor .getInstance(project).listAvailableCustomizations(profile)
127+ }.onFailure { e ->
124128 val requestId = (e as ? CodeWhispererRuntimeException )?.requestId()
125- val logMessage = if (CodeWhispererConstants .Customization .noAccessToCustomizationExceptionPredicate(e)) {
129+ val isNotAllowlisted = (e as ? Exception )
130+ ?.let { CodeWhispererConstants .Customization .noAccessToCustomizationExceptionPredicate(e) }
131+ ? : false
132+ val logMessage = if (isNotAllowlisted) {
126133 // TODO: not required for non GP users
127134 " ListAvailableCustomizations: connection ${it.id} is not allowlisted, requestId: ${requestId.orEmpty()} "
128135 } else {
129136 " ListAvailableCustomizations: failed due to unknown error ${e.message} , requestId: ${requestId.orEmpty()} "
130137 }
131138 LOG .debug { logMessage }
132- }
139+ }.getOrDefault(emptyList())
133140 }
134141
135142 // 2. get diff
136143 val previousCustomizationsShapshot = connectionToCustomizationsShownLastTime.getOrElse(it.id) { emptyList() }
137- // calculate new profile+customization list using profile.arn :: customization.arn as the key
138- val diff = aggregatedCustomizations.filterNot { customization ->
139- previousCustomizationsShapshot.contains(
140- " ${customization.profile?.arn} ::${customization.arn} "
141- )
142- }.toSet()
144+ // calculate new profile+customization list using compositeKey
145+ val diff = aggregatedCustomizations.filterNot { customization -> previousCustomizationsShapshot.contains(customization.compositeKey()) }.toSet()
146+
143147 // 3 if passive,
144148 // (1) update allowlisting
145149 // (2) prompt "You have New Customizations" toast notification (only show once)
@@ -152,39 +156,31 @@ class DefaultCodeWhispererModelConfigurator : CodeWhispererModelConfigurator, Pe
152156 notifyNewCustomization(project)
153157 }
154158 } else {
155- aggregatedCustomizations.let { customizations ->
156- connectionToCustomizationsShownLastTime[it.id] = customizations.map { customization,
157- ->
158- " ${customization.profile?.arn} ::${customization.arn} "
159- }.toMutableList()
160- }
159+ connectionToCustomizationsShownLastTime[it.id] = aggregatedCustomizations.map { customization -> customization.compositeKey() }.toMutableList()
161160 }
162161
163162 // 4. invalidate selected customization if
164163 // (1) the API call failed
165164 // (2) the selected customization is not in the resultset of API call
165+ // (3) the q region profile associated with the selected customization does not match the currently active profile
166166 activeCustomization(project)?.let { activeCustom ->
167167 if (aggregatedCustomizations.isEmpty()) {
168168 invalidateSelectedAndNotify(project)
169- } else if (! aggregatedCustomizations.any { latestCustom ->
170- " ${latestCustom.profile?.arn} ::${latestCustom.arn} " == " ${activeCustom.profile?.arn} ::${activeCustom.arn} "
171- } || activeCustom.profile != QRegionProfileManager .getInstance().activeProfile(project)
169+ } else if (! aggregatedCustomizations.any { latestCustom -> latestCustom.compositeKey() == activeCustom.compositeKey() } ||
170+ activeCustom.profile != QRegionProfileManager .getInstance().activeProfile(project)
172171 ) {
173172 invalidateSelectedAndNotify(project)
174173 }
175174 }
176175
177176 // 5. transform result to UI items and return
178- val customizationUiItems = aggregatedCustomizations.let { customizations ->
179- val nameToCount = customizations.groupingBy { customization -> customization.name }.eachCount()
180-
181- customizations.map { customization ->
182- CustomizationUiItem (
183- customization,
184- isNew = diff.contains(customization),
185- shouldPrefixAccountId = (nameToCount[customization.name] ? : 0 ) > 1
186- )
187- }
177+ val nameToCount = aggregatedCustomizations.groupingBy { customization -> customization.name }.eachCount()
178+ val customizationUiItems = aggregatedCustomizations.map { customization ->
179+ CustomizationUiItem (
180+ customization,
181+ isNew = diff.contains(customization),
182+ shouldPrefixAccountId = (nameToCount[customization.name] ? : 0 ) > 1
183+ )
188184 }
189185 connectionToCustomizationUiItems[it.id] = customizationUiItems
190186
0 commit comments