IJPL-177442 Refactored plugin manager to call MarketplaceRequests and not vice versa

(cherry picked from commit 815ff76b709869f9882cbba350365e59ea61095a)

IJ-MR-163725

GitOrigin-RevId: 201927733f84b5f1631bde2a1011e0e88fa2755c
This commit is contained in:
Denis Zaichenko
2025-06-05 13:48:29 +00:00
committed by intellij-monorepo-bot
parent 1d7ac70529
commit fedea49ae7
11 changed files with 341 additions and 290 deletions
@@ -11,7 +11,8 @@ import com.intellij.ide.impl.ProjectUtil;
import com.intellij.ide.plugins.certificates.PluginCertificateManager;
import com.intellij.ide.plugins.enums.PluginsGroupType;
import com.intellij.ide.plugins.enums.SortBy;
import com.intellij.ide.plugins.marketplace.MarketplaceRequests;
import com.intellij.ide.plugins.marketplace.PluginSearchResult;
import com.intellij.ide.plugins.newui.UiPluginManager;
import com.intellij.ide.plugins.marketplace.ranking.MarketplaceLocalRanker;
import com.intellij.ide.plugins.marketplace.statistics.PluginManagerUsageCollector;
import com.intellij.ide.plugins.newui.*;
@@ -117,7 +118,6 @@ public final class PluginManagerConfigurable
new PluginsGroup(IdeBundle.message("plugins.configurable.bundled.updates"), PluginsGroupType.BUNDLED_UPDATE);
private Runnable myMarketplaceRunnable;
private final MarketplaceRequests myMarketplaceRequests = MarketplaceRequests.getInstance();
private SearchResultPanel myMarketplaceSearchPanel;
private SearchResultPanel myInstalledSearchPanel;
@@ -203,30 +203,31 @@ public final class PluginManagerConfigurable
myTabHeaderComponent.addTab(IdeBundle.message("plugin.manager.tab.installed"), myCountIcon);
CustomPluginRepositoryService.getInstance().clearCache();
myPluginUpdatesService = UiPluginManager.getInstance().subscribeToUpdatesCount(myPluginModelFacade.getModel().getSessionId(), countValue -> {
int count = countValue == null ? 0 : countValue;
String text = Integer.toString(count);
boolean visible = count > 0;
myPluginUpdatesService =
UiPluginManager.getInstance().subscribeToUpdatesCount(myPluginModelFacade.getModel().getSessionId(), countValue -> {
int count = countValue == null ? 0 : countValue;
String text = Integer.toString(count);
boolean visible = count > 0;
String tooltip = PluginUpdatesService.getUpdatesTooltip();
myTabHeaderComponent.setTabTooltip(INSTALLED_TAB, tooltip);
String tooltip = PluginUpdatesService.getUpdatesTooltip();
myTabHeaderComponent.setTabTooltip(INSTALLED_TAB, tooltip);
myUpdateAll.setEnabled(true);
myUpdateAllBundled.setEnabled(true);
myUpdateAll.setVisible(visible && myBundledUpdateGroup.ui == null);
myUpdateAllBundled.setVisible(visible);
myUpdateAll.setEnabled(true);
myUpdateAllBundled.setEnabled(true);
myUpdateAll.setVisible(visible && myBundledUpdateGroup.ui == null);
myUpdateAllBundled.setVisible(visible);
myUpdateCounter.setText(text);
myUpdateCounter.setToolTipText(tooltip);
myUpdateCounterBundled.setText(text);
myUpdateCounterBundled.setToolTipText(tooltip);
myUpdateCounter.setVisible(visible && myBundledUpdateGroup.ui == null);
myUpdateCounterBundled.setVisible(visible);
myUpdateCounter.setText(text);
myUpdateCounter.setToolTipText(tooltip);
myUpdateCounterBundled.setText(text);
myUpdateCounterBundled.setToolTipText(tooltip);
myUpdateCounter.setVisible(visible && myBundledUpdateGroup.ui == null);
myUpdateCounterBundled.setVisible(visible);
myCountIcon.setText(text);
myTabHeaderComponent.update();
return null;
});
myCountIcon.setText(text);
myTabHeaderComponent.update();
return null;
});
myPluginModelFacade.getModel().setPluginUpdatesService(myPluginUpdatesService);
UiPluginManager.getInstance().updateDescriptorsForInstalledPlugins();
@@ -623,7 +624,7 @@ public final class PluginManagerConfigurable
}
try {
ProcessIOExecutorService.INSTANCE.submit(() -> {
allTags.addAll(myMarketplaceRequests.getMarketplaceTagsSupplier().get());
allTags.addAll(UiPluginManager.getInstance().getAllPluginsTags());
}).get();
}
catch (InterruptedException | ExecutionException e) {
@@ -642,7 +643,7 @@ public final class PluginManagerConfigurable
LinkedHashSet<String> vendors = new LinkedHashSet<>();
try {
ProcessIOExecutorService.INSTANCE.submit(() -> {
vendors.addAll(myMarketplaceRequests.getMarketplaceVendorsSupplier().get());
vendors.addAll(UiPluginManager.getInstance().getAllVendors());
}).get();
}
catch (InterruptedException | ExecutionException e) {
@@ -829,124 +830,120 @@ public final class PluginManagerConfigurable
@Override
protected void handleQuery(@NotNull String query, @NotNull PluginsGroup result) {
int searchIndex = PluginManagerUsageCollector.updateAndGetSearchIndex();
try {
SearchQueryParser.Marketplace parser = new SearchQueryParser.Marketplace(query);
Map<PluginUiModel, Double> pluginToScore = null;
SearchQueryParser.Marketplace parser = new SearchQueryParser.Marketplace(query);
if (parser.internal) {
PluginsViewCustomizer.PluginsGroupDescriptor groupDescriptor =
getPluginsViewCustomizer().getInternalPluginsGroupDescriptor();
if (groupDescriptor != null) {
if (parser.searchQuery == null) {
result.addDescriptors(groupDescriptor.getPlugins());
}
else {
for (IdeaPluginDescriptor pluginDescriptor : groupDescriptor.getPlugins()) {
if (StringUtil.containsIgnoreCase(pluginDescriptor.getName(), parser.searchQuery)) {
result.addDescriptor(pluginDescriptor);
}
}
}
result.removeDuplicates();
result.sortByName();
return;
Map<PluginUiModel, Double> pluginToScore = null;
if (parser.internal) {
PluginsViewCustomizer.PluginsGroupDescriptor groupDescriptor =
getPluginsViewCustomizer().getInternalPluginsGroupDescriptor();
if (groupDescriptor != null) {
if (parser.searchQuery == null) {
result.addDescriptors(groupDescriptor.getPlugins());
}
}
Map<String, List<PluginUiModel>> customRepositoriesMap =
CustomPluginRepositoryService.getInstance().getCustomRepositoryPluginMap();
if (parser.suggested && project != null) {
result.addModels(PluginsAdvertiserStartupActivityKt.findSuggestedPlugins(project, customRepositoriesMap));
}
else if (!parser.repositories.isEmpty()) {
for (String repository : parser.repositories) {
List<PluginUiModel> descriptors = customRepositoriesMap.get(repository);
if (descriptors == null) {
continue;
}
if (parser.searchQuery == null) {
result.addModels(descriptors);
}
else {
for (PluginUiModel descriptor : descriptors) {
if (StringUtil.containsIgnoreCase(descriptor.getName(), parser.searchQuery)) {
result.addModel(descriptor);
}
else {
for (IdeaPluginDescriptor pluginDescriptor : groupDescriptor.getPlugins()) {
if (StringUtil.containsIgnoreCase(pluginDescriptor.getName(), parser.searchQuery)) {
result.addDescriptor(pluginDescriptor);
}
}
}
result.removeDuplicates();
result.sortByName();
return;
}
else {
List<PluginUiModel> pluginsFromMarketplace =
myMarketplaceRequests.executePluginSearch(parser.getUrlQuery(), 10000, true);
// compare plugin versions between marketplace & custom repositories
List<PluginUiModel> customPlugins = ContainerUtil.flatten(customRepositoriesMap.values());
Collection<PluginUiModel> plugins = RepositoryHelper.mergePluginModelsFromRepositories(pluginsFromMarketplace,
customPlugins,
false);
result.addModels(0, new ArrayList<>(plugins));
}
if (parser.searchQuery != null) {
List<PluginUiModel> descriptors = ContainerUtil.filter(customPlugins,
descriptor -> StringUtil.containsIgnoreCase(descriptor.getName(),
parser.searchQuery));
result.addModels(0, descriptors);
Map<String, List<PluginUiModel>> customRepositoriesMap =
CustomPluginRepositoryService.getInstance().getCustomRepositoryPluginMap();
if (parser.suggested && project != null) {
result.addModels(PluginsAdvertiserStartupActivityKt.findSuggestedPlugins(project, customRepositoriesMap));
}
else if (!parser.repositories.isEmpty()) {
for (String repository : parser.repositories) {
List<PluginUiModel> descriptors = customRepositoriesMap.get(repository);
if (descriptors == null) {
continue;
}
result.removeDuplicates();
final var localRanker = MarketplaceLocalRanker.getInstanceIfEnabled();
if (localRanker != null) {
pluginToScore = localRanker.rankPlugins(parser, result.getModels());
if (parser.searchQuery == null) {
result.addModels(descriptors);
}
if (!result.getModels().isEmpty()) {
String title = IdeBundle.message("plugin.manager.action.label.sort.by.1");
for (AnAction action : myMarketplaceSortByGroup.getChildren(ActionManager.getInstance())) {
MarketplaceSortByAction sortByAction = (MarketplaceSortByAction)action;
sortByAction.setState(parser);
if (sortByAction.myState) {
title = IdeBundle.message("plugin.manager.action.label.sort.by",
sortByAction.myOption.getPresentableNameSupplier().get());
else {
for (PluginUiModel descriptor : descriptors) {
if (StringUtil.containsIgnoreCase(descriptor.getName(), parser.searchQuery)) {
result.addModel(descriptor);
}
}
myMarketplaceSortByAction.setText(title);
result.addRightAction(myMarketplaceSortByAction);
Collection<PluginUiModel> updates = UiPluginManager.getInstance().getUpdateModels();
if (!ContainerUtil.isEmpty(updates)) {
myPostFillGroupCallback = () -> {
applyUpdates(myPanel, updates);
selectionListener.accept(myMarketplacePanel);
selectionListener.accept(myMarketplaceSearchPanel.getPanel());
};
}
}
}
result.removeDuplicates();
result.sortByName();
}
else {
PluginSearchResult searchResult = UiPluginManager.getInstance().executeMarketplaceQuery(parser.getUrlQuery(), 10000, true);
if (searchResult.getError() != null) {
ApplicationManager.getApplication().invokeLater(
() -> myPanel.getEmptyText()
.setText(IdeBundle.message("plugins.configurable.search.result.not.loaded"))
.appendSecondaryText(
IdeBundle.message("plugins.configurable.check.internet"), StatusText.DEFAULT_ATTRIBUTES, null), ModalityState.any()
);
}
// compare plugin versions between marketplace & custom repositories
List<PluginUiModel> customPlugins = ContainerUtil.flatten(customRepositoriesMap.values());
Collection<PluginUiModel> plugins = RepositoryHelper.mergePluginModelsFromRepositories(searchResult.getPlugins(),
customPlugins,
false);
result.addModels(0, new ArrayList<>(plugins));
PluginManagerUsageCollector.INSTANCE.performMarketplaceSearch(
ProjectUtil.getActiveProject(), parser, result.getModels(), searchIndex, pluginToScore);
}
catch (IOException e) {
LOG.info(e);
ApplicationManager.getApplication().invokeLater(
() -> myPanel.getEmptyText()
.setText(IdeBundle.message("plugins.configurable.search.result.not.loaded"))
.appendSecondaryText(
IdeBundle.message("plugins.configurable.check.internet"), StatusText.DEFAULT_ATTRIBUTES, null), ModalityState.any()
);
if (parser.searchQuery != null) {
List<PluginUiModel> descriptors = ContainerUtil.filter(customPlugins,
descriptor -> StringUtil.containsIgnoreCase(descriptor.getName(),
parser.searchQuery));
result.addModels(0, descriptors);
}
result.removeDuplicates();
final var localRanker = MarketplaceLocalRanker.getInstanceIfEnabled();
if (localRanker != null) {
pluginToScore = localRanker.rankPlugins(parser, result.getModels());
}
if (!result.getModels().isEmpty()) {
String title = IdeBundle.message("plugin.manager.action.label.sort.by.1");
for (AnAction action : myMarketplaceSortByGroup.getChildren(ActionManager.getInstance())) {
MarketplaceSortByAction sortByAction = (MarketplaceSortByAction)action;
sortByAction.setState(parser);
if (sortByAction.myState) {
title = IdeBundle.message("plugin.manager.action.label.sort.by",
sortByAction.myOption.getPresentableNameSupplier().get());
}
}
myMarketplaceSortByAction.setText(title);
result.addRightAction(myMarketplaceSortByAction);
Collection<PluginUiModel> updates = UiPluginManager.getInstance().getUpdateModels();
if (!ContainerUtil.isEmpty(updates)) {
myPostFillGroupCallback = () -> {
applyUpdates(myPanel, updates);
selectionListener.accept(myMarketplacePanel);
selectionListener.accept(myMarketplaceSearchPanel.getPanel());
};
}
}
}
PluginManagerUsageCollector.INSTANCE.performMarketplaceSearch(
ProjectUtil.getActiveProject(), parser, result.getModels(), searchIndex, pluginToScore);
}
};
return myMarketplaceSearchPanel;
}
@@ -1905,9 +1902,13 @@ public final class PluginManagerConfigurable
@NotNull @NonNls String query,
@NotNull @NonNls String showAllQuery) throws IOException {
LOG.info("Marketplace tab: '" + name + "' group load started");
List<PluginUiModel> pluginUiModels = myMarketplaceRequests.executePluginSearch(query, ITEMS_PER_GROUP * 2);
PluginSearchResult searchResult = UiPluginManager.getInstance().executeMarketplaceQuery(query, ITEMS_PER_GROUP * 2, false);
if (searchResult.getError() != null) {
throw new IOException(searchResult.getError());
}
for (PluginUiModel plugin : pluginUiModels) {
List<PluginUiModel> plugins = searchResult.getPlugins();
for (PluginUiModel plugin : plugins) {
plugin.setInstallSource(FUSEventSource.PLUGINS_STAFF_PICKS_GROUP);
FUSEventSource.PLUGINS_STAFF_PICKS_GROUP.logPluginSuggested(plugin.getPluginId());
}
@@ -1916,8 +1917,8 @@ public final class PluginManagerConfigurable
name,
type,
showAllQuery,
pluginUiModels,
__ -> pluginUiModels.size() >= ITEMS_PER_GROUP);
plugins,
__ -> plugins.size() >= ITEMS_PER_GROUP);
}
@Override
@@ -201,7 +201,6 @@ public final class RepositoryHelper {
}
}
@Deprecated(forRemoval = true)
public static @NotNull Collection<PluginNode> mergePluginsFromRepositories(
@NotNull List<PluginNode> marketplacePlugins,
@@ -13,7 +13,6 @@ import com.intellij.ide.plugins.newui.PluginUiModel
import com.intellij.ide.plugins.newui.PluginUiModelAdapter
import com.intellij.ide.plugins.newui.PluginUiModelBuilderFactory
import com.intellij.ide.plugins.newui.Tags
import com.intellij.ide.plugins.newui.UiPluginManager
import com.intellij.ide.util.PropertiesComponent
import com.intellij.internal.statistic.eventLog.fus.MachineIdManager
import com.intellij.openapi.application.ApplicationManager
@@ -159,7 +158,80 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
buildNumber: BuildNumber? = null,
throwExceptions: Boolean = false,
): List<IdeCompatibleUpdate> {
return UiPluginManager.getInstance().getLastCompatiblePluginUpdate(allIds, throwExceptions, buildNumber?.asString())
val chunks = mutableListOf<MutableList<PluginId>>()
chunks.add(mutableListOf())
val maxLength = 3500 // 4k minus safety gap
var currentLength = 0
val pluginXmlIdsLength = "&pluginXmlId=".length
for (id in allIds) {
val adder = id.idString.length + pluginXmlIdsLength
val newLength = currentLength + adder
if (newLength > maxLength) {
chunks.add(mutableListOf(id))
currentLength = adder
}
else {
currentLength = newLength
chunks.last().add(id)
}
}
return chunks.flatMap {
loadLastCompatiblePluginsUpdate(it, buildNumber, throwExceptions)
}
}
private fun loadLastCompatiblePluginsUpdate(
ids: Collection<PluginId>,
buildNumber: BuildNumber? = null,
throwExceptions: Boolean = false,
): List<IdeCompatibleUpdate> {
try {
if (ids.isEmpty()) {
return emptyList()
}
val url = URI(MarketplaceUrls.getSearchPluginsUpdatesUrl())
val os = URLEncoder.encode(OS.CURRENT.name + " " + OS.CURRENT.version, CharsetToolkit.UTF8)
val machineId = if (LoadingState.COMPONENTS_LOADED.isOccurred) {
MachineIdManager.getAnonymizedMachineId("JetBrainsUpdates") // same as regular updates
.takeIf { !PropertiesComponent.getInstance().getBoolean(UpdateChecker.MACHINE_ID_DISABLED_PROPERTY, false) }
} else null
val query = buildString {
append("build=${ApplicationInfoImpl.orFromPluginCompatibleBuild(buildNumber)}")
append("&os=$os")
if (machineId != null) {
append("&mid=$machineId")
}
for (id in ids) {
append("&pluginXmlId=${URLEncoder.encode(id.idString, CharsetToolkit.UTF8)}")
}
}
val urlString = url.withQuery(query).toString()
return HttpRequests.request(urlString)
.accept(HttpRequests.JSON_CONTENT_TYPE)
.setHeadersViaTuner()
.productNameAsUserAgent()
.throwStatusCodeException(throwExceptions)
.connect {
objectMapper.readValue(it.inputStream, object : TypeReference<List<IdeCompatibleUpdate>>() {})
}
}
catch (pce: ProcessCanceledException) {
throw pce
}
catch (e: Exception) {
LOG.infoOrDebug("Can not get compatible updates from Marketplace", e)
if (throwExceptions) {
throw e
}
return emptyList()
}
}
@RequiresBackgroundThread
@@ -214,7 +286,10 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
ideCompatibleUpdate: IdeCompatibleUpdate,
indicator: ProgressIndicator? = null,
): PluginUiModel {
return UiPluginManager.getInstance().loadUpdateMetadata(xmlId, ideCompatibleUpdate, indicator).toUiModel()
val updateMetadataFile = Paths.get(PathManager.getPluginTempPath(), "meta")
return readOrUpdateFile(updateMetadataFile.resolve(ideCompatibleUpdate.externalUpdateId + ".json"), MarketplaceUrls.getUpdateMetaUrl(ideCompatibleUpdate.externalPluginId, ideCompatibleUpdate.externalUpdateId), indicator, IdeBundle.message("progress.downloading.plugins.meta", xmlId)) {
objectMapper.readValue(it, IntellijUpdateMetadata::class.java)
}.toUiModel()
}
/**
@@ -448,7 +523,16 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
val includeIncompatible = includeUpgradeToCommercialIde && suggestedIdeCode != null
val marketplaceSearchPluginData = UiPluginManager.getInstance().executeMarketplaceQuery(query, count, includeIncompatible)
val marketplaceSearchPluginData = HttpRequests
.request(MarketplaceUrls.getSearchPluginsUrl(query, count, includeIncompatible))
.setHeadersViaTuner()
.throwStatusCodeException(false)
.connect {
objectMapper.readValue(
it.inputStream,
object : TypeReference<List<MarketplaceSearchPluginData>>() {}
)
}
// Marketplace Search Service can produce objects without "externalUpdateId". It means that an update is not in the search index yet.
return marketplaceSearchPluginData
.mapNotNull {
@@ -599,7 +683,18 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
@RequiresBackgroundThread
@RequiresReadLockAbsence
internal fun loadPluginMetadata(externalPluginId: String): IntellijPluginMetadata? {
return UiPluginManager.getInstance().loadPluginMetadata(externalPluginId)
try {
return readOrUpdateFile(
Paths.get(PathManager.getPluginTempPath(), "${externalPluginId}-meta.json"),
MarketplaceUrls.getPluginMetaUrl(externalPluginId),
null,
""
) { objectMapper.readValue(it, object : TypeReference<IntellijPluginMetadata>() {}) }
}
catch (e: Exception) {
LOG.warn(e)
return null
}
}
@Deprecated("use #getLastCompatiblePluginUpdateModel(PluginId, BuildNumber, ProgressIndicator)")
@@ -622,7 +717,8 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
buildNumber: BuildNumber? = null,
indicator: ProgressIndicator? = null,
): PluginUiModel? {
return UiPluginManager.getInstance().getLastCompatiblePluginUpdateModel(pluginId, buildNumber?.asString(), indicator)
return getLastCompatiblePluginUpdate(setOf(pluginId), buildNumber, false).firstOrNull()
?.let { loadPluginModel(pluginId.idString, it, indicator) }
}
fun getCompatibleUpdateByModule(module: String): PluginId? {
@@ -765,9 +861,16 @@ class MarketplaceRequests(private val coroutineScope: CoroutineScope) : PluginIn
@RequiresBackgroundThread
@RequiresReadLockAbsence
fun loadPluginReviews(pluginUiModel: PluginUiModel, page: Int): List<PluginReviewComment>? {
fun loadPluginReviews(pluginId: PluginId, page: Int): List<PluginReviewComment>? {
try {
return UiPluginManager.getInstance().loadPluginReviews(pluginUiModel, page)
return HttpRequests
.request(MarketplaceUrls.getPluginReviewsUrl(pluginId, page))
.setHeadersViaTuner()
.productNameAsUserAgent()
.throwStatusCodeException(false)
.connect {
objectMapper.readValue(it.inputStream, object : TypeReference<List<PluginReviewComment>>() {})
}
}
catch (e: IOException) {
LOG.warn(e)
@@ -0,0 +1,16 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.plugins.marketplace
import com.intellij.ide.plugins.api.PluginDto
import com.intellij.ide.plugins.newui.PluginUiModel
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable
data class PluginSearchResult(
@Transient val pluginModels: List<PluginUiModel> = emptyList(),
val error: String? = null,
val pluginDtos: List<PluginDto> = pluginModels.map(PluginDto::fromModel),
) {
fun getPlugins(): List<PluginUiModel> = pluginModels.ifEmpty { pluginDtos }
}
@@ -3,7 +3,6 @@ package com.intellij.ide.plugins.newui
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.intellij.diagnostic.LoadingState
import com.intellij.ide.IdeBundle
import com.intellij.ide.plugins.*
import com.intellij.ide.plugins.DynamicPlugins.allowLoadUnloadWithoutRestart
@@ -15,41 +14,30 @@ import com.intellij.ide.plugins.PluginManagerCore.isIncompatible
import com.intellij.ide.plugins.PluginManagerCore.isUpdatedBundledPlugin
import com.intellij.ide.plugins.PluginManagerCore.looksLikePlatformPluginAlias
import com.intellij.ide.plugins.PluginUtils.toPluginDescriptors
import com.intellij.ide.plugins.api.PluginDto
import com.intellij.ide.plugins.marketplace.*
import com.intellij.ide.plugins.marketplace.MarketplaceRequests.Companion.readOrUpdateFile
import com.intellij.ide.plugins.marketplace.utils.MarketplaceUrls
import com.intellij.ide.plugins.newui.UiPluginManager.Companion.getInstance
import com.intellij.ide.util.PropertiesComponent
import com.intellij.internal.statistic.eventLog.fus.MachineIdManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.application.impl.ApplicationInfoImpl
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.options.ConfigurationException
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageDialogBuilder.Companion.okCancel
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.updateSettings.impl.UpdateChecker
import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
import com.intellij.util.concurrency.annotations.RequiresReadLockAbsence
import com.intellij.util.io.HttpRequests
import com.intellij.util.system.OS
import com.intellij.util.withQuery
import com.intellij.xml.util.XmlStringUtil
import org.jetbrains.annotations.ApiStatus
import java.io.File
import java.io.IOException
import java.net.URI
import java.net.URLEncoder
import java.nio.file.FileVisitResult
import java.nio.file.Paths
import java.util.*
@@ -231,6 +219,14 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
return service
}
override fun getAllPluginsTags(): Set<String> {
return MarketplaceRequests.getInstance().marketplaceTagsSupplier.get()
}
override fun getAllVendors(): Set<String> {
return MarketplaceRequests.getInstance().marketplaceVendorsSupplier.get()
}
override fun performInstallOperation(
request: InstallPluginRequest,
parentComponent: JComponent?,
@@ -464,47 +460,36 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
return updatePluginDependencies(session, null)
}
override fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): List<MarketplaceSearchPluginData> {
return HttpRequests.request(MarketplaceUrls.getSearchPluginsUrl(query, count, includeIncompatible)).setHeadersViaTuner().throwStatusCodeException(false).connect {
objectMapper.readValue(it.inputStream, object : TypeReference<List<MarketplaceSearchPluginData>>() {})
override fun executePluginsSearch(query: String, count: Int, includeIncompatible: Boolean): PluginSearchResult {
try {
val plugins = MarketplaceRequests.getInstance().executePluginSearch(query, count, includeIncompatible)
return PluginSearchResult(plugins)
}
catch (e: IOException) {
LOG.warn(e)
return PluginSearchResult(emptyList(), e.message)
}
}
override fun loadUpdateMetadata(
override fun loadPluginDetails(
xmlId: String,
ideCompatibleUpdate: IdeCompatibleUpdate,
indicator: ProgressIndicator?,
): IntellijUpdateMetadata {
val updateMetadataFile = Paths.get(PathManager.getPluginTempPath(), "meta")
return readOrUpdateFile(updateMetadataFile.resolve(ideCompatibleUpdate.externalUpdateId + ".json"), MarketplaceUrls.getUpdateMetaUrl(ideCompatibleUpdate.externalPluginId, ideCompatibleUpdate.externalUpdateId), indicator, IdeBundle.message("progress.downloading.plugins.meta", xmlId)) {
objectMapper.readValue(it, IntellijUpdateMetadata::class.java)
}
): PluginUiModel {
return MarketplaceRequests.loadPluginModel(xmlId, ideCompatibleUpdate, indicator)
}
@RequiresBackgroundThread
@RequiresReadLockAbsence
@Throws(IOException::class)
override fun loadPluginReviews(pluginId: PluginId, page: Int): List<PluginReviewComment>? {
return HttpRequests.request(MarketplaceUrls.getPluginReviewsUrl(pluginId, page)).setHeadersViaTuner().productNameAsUserAgent().throwStatusCodeException(false).connect {
objectMapper.readValue(it.inputStream, object : TypeReference<List<PluginReviewComment>>() {})
}
return MarketplaceRequests.getInstance().loadPluginReviews(pluginId, page)
}
@RequiresBackgroundThread
@RequiresReadLockAbsence
override fun loadPluginMetadata(externalPluginId: String): IntellijPluginMetadata? {
try {
return readOrUpdateFile(
Paths.get(PathManager.getPluginTempPath(), "${externalPluginId}-meta.json"),
MarketplaceUrls.getPluginMetaUrl(externalPluginId),
null,
""
) { objectMapper.readValue(it, object : TypeReference<IntellijPluginMetadata>() {}) }
}
catch (e: Exception) {
LOG.warn(e)
return null
}
return MarketplaceRequests.getInstance().loadPluginMetadata(externalPluginId)
}
override fun getPluginManagerUrl(): String {
@@ -512,8 +497,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
}
override fun getLastCompatiblePluginUpdateModel(pluginId: PluginId, buildNumber: String?, indicator: ProgressIndicator?): PluginUiModel? {
return getLastCompatiblePluginUpdate(setOf(pluginId), false, BuildNumber.fromString(buildNumber)).firstOrNull()
?.let { loadUpdateMetadata(pluginId.idString, it, indicator).toUiModel() }
return MarketplaceRequests.getInstance().getLastCompatiblePluginUpdateModel(pluginId, BuildNumber.fromString(buildNumber), indicator)
}
override fun getLastCompatiblePluginUpdate(
@@ -521,7 +505,7 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
throwExceptions: Boolean,
buildNumber: String?,
): List<IdeCompatibleUpdate> {
return getLastCompatiblePluginUpdate(allIds, throwExceptions, BuildNumber.fromString(buildNumber))
return MarketplaceRequests.getLastCompatiblePluginUpdate(allIds, BuildNumber.fromString(buildNumber), throwExceptions)
}
override fun getErrors(sessionId: String, pluginId: PluginId): CheckErrorsResult {
@@ -771,90 +755,6 @@ object DefaultUiPluginManagerController : UiPluginManagerController {
}
}
@RequiresBackgroundThread
@RequiresReadLockAbsence
private fun getLastCompatiblePluginUpdate(
allIds: Set<PluginId>,
throwExceptions: Boolean,
buildNumber: BuildNumber?,
): List<IdeCompatibleUpdate> {
val chunks = mutableListOf<MutableList<PluginId>>()
chunks.add(mutableListOf())
val maxLength = 3500 // 4k minus safety gap
var currentLength = 0
val pluginXmlIdsLength = "&pluginXmlId=".length
for (id in allIds) {
val adder = id.idString.length + pluginXmlIdsLength
val newLength = currentLength + adder
if (newLength > maxLength) {
chunks.add(mutableListOf(id))
currentLength = adder
}
else {
currentLength = newLength
chunks.last().add(id)
}
}
return chunks.flatMap {
loadLastCompatiblePluginsUpdate(it, buildNumber, throwExceptions)
}
}
private fun loadLastCompatiblePluginsUpdate(
ids: Collection<PluginId>,
buildNumber: BuildNumber? = null,
throwExceptions: Boolean = false,
): List<IdeCompatibleUpdate> {
try {
if (ids.isEmpty()) {
return emptyList()
}
val url = URI(MarketplaceUrls.getSearchPluginsUpdatesUrl())
val os = URLEncoder.encode(OS.CURRENT.name + " " + OS.CURRENT.version, CharsetToolkit.UTF8)
val machineId = if (LoadingState.COMPONENTS_LOADED.isOccurred) {
MachineIdManager.getAnonymizedMachineId("JetBrainsUpdates") // same as regular updates
.takeIf { !PropertiesComponent.getInstance().getBoolean(UpdateChecker.MACHINE_ID_DISABLED_PROPERTY, false) }
}
else null
val query = buildString {
append("build=${ApplicationInfoImpl.orFromPluginCompatibleBuild(buildNumber)}")
append("&os=$os")
if (machineId != null) {
append("&mid=$machineId")
}
for (id in ids) {
append("&pluginXmlId=${URLEncoder.encode(id.idString, CharsetToolkit.UTF8)}")
}
}
val urlString = url.withQuery(query).toString()
return HttpRequests.request(urlString)
.accept(HttpRequests.JSON_CONTENT_TYPE)
.setHeadersViaTuner()
.productNameAsUserAgent()
.throwStatusCodeException(throwExceptions)
.connect {
objectMapper.readValue(it.inputStream, object : TypeReference<List<IdeCompatibleUpdate>>() {})
}
}
catch (pce: ProcessCanceledException) {
throw pce
}
catch (e: Exception) {
LOG.infoOrDebug("Can not get compatible updates from Marketplace", e)
if (throwExceptions) {
throw e
}
return emptyList()
}
}
private fun getInstalledAndPendingPlugins(): List<IdeaPluginDescriptor> {
return InstalledPluginsState.getInstance().installedPlugins + PluginManagerCore.plugins
}
@@ -13,8 +13,7 @@ import com.intellij.ide.plugins.*
import com.intellij.ide.plugins.PluginManagerCore.getPlugin
import com.intellij.ide.plugins.PluginManagerCore.looksLikePlatformPluginAlias
import com.intellij.ide.plugins.api.ReviewsPageContainer
import com.intellij.ide.plugins.marketplace.MarketplaceRequests
import com.intellij.ide.plugins.marketplace.MarketplaceRequests.Companion.getLastCompatiblePluginUpdate
import com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
import com.intellij.ide.plugins.marketplace.statistics.PluginManagerUsageCollector.pluginCardOpened
import com.intellij.ide.plugins.marketplace.utils.MarketplaceUrls.getPluginHomepage
import com.intellij.ide.plugins.marketplace.utils.MarketplaceUrls.getPluginReviewNoteUrl
@@ -377,7 +376,7 @@ class PluginDetailsPageComponent @JvmOverloads constructor(
val plugin = plugin
if (plugin != null && !sentFeedbackPlugins.contains(plugin.pluginId)) {
val foundPlugin = UiPluginManager.getInstance().findPlugin(plugin.pluginId)
val foundPlugin = DefaultUiPluginManagerController.findPlugin(plugin.pluginId)
if (foundPlugin != null && pluginModel.isUninstalled(foundPlugin.pluginId)) {
rootPanel.add(uninstallFeedbackNotification!!, BorderLayout.NORTH)
}
@@ -566,7 +565,7 @@ class PluginDetailsPageComponent @JvmOverloads constructor(
val reviewComments = node.reviewComments!!
val page = reviewComments.getNextPage()
ProcessIOExecutorService.INSTANCE.execute {
val items = MarketplaceRequests.getInstance().loadPluginReviews(node, page)
val items = UiPluginManager.getInstance().loadPluginReviews(node.pluginId, page)
if (items == null) return@execute
ApplicationManager.getApplication().invokeLater({
if (showComponent != component) {
@@ -703,7 +702,7 @@ class PluginDetailsPageComponent @JvmOverloads constructor(
?: return@doLoad
coroutineContext.ensureActive()
val update = getLastCompatiblePluginUpdate(setOf(component.pluginModel.pluginId))
val update = UiPluginManager.getInstance().getLastCompatiblePluginUpdate(setOf(component.pluginModel.pluginId), false)
if (!update.isEmpty()) {
val compatibleUpdate = update[0]
lastUpdateModel.externalPluginId = compatibleUpdate.externalPluginId
@@ -1433,18 +1432,23 @@ class PluginDetailsPageComponent @JvmOverloads constructor(
@ApiStatus.Internal
fun loadPluginDetails(model: PluginUiModel): PluginUiModel? {
return MarketplaceRequests.getInstance().loadPluginDetails(model)
val externalPluginId = model.externalPluginId ?: return model
val externalUpdateId = model.externalUpdateId ?: return model
return UiPluginManager.getInstance().loadPluginDetails(
model.pluginId.idString,
IdeCompatibleUpdate(externalUpdateId = externalUpdateId, externalPluginId = externalPluginId)
)
}
@ApiStatus.Internal
fun loadAllPluginDetails(existingModel: PluginUiModel, targetModel: PluginUiModel): PluginUiModel? {
val marketplaceRequests = MarketplaceRequests.getInstance()
if (!existingModel.suggestedFeatures.isEmpty()) {
targetModel.suggestedFeatures = existingModel.suggestedFeatures
}
val externalPluginId = existingModel.externalPluginId ?: return null
val metadata = marketplaceRequests.loadPluginMetadata(externalPluginId)
val metadata = UiPluginManager.getInstance().loadPluginMetadata(externalPluginId)
if (metadata != null) {
if (metadata.screenshots != null) {
targetModel.screenShots = metadata.screenshots
@@ -1460,7 +1464,7 @@ fun loadAllPluginDetails(existingModel: PluginUiModel, targetModel: PluginUiMode
@ApiStatus.Internal
fun loadReviews(existingModel: PluginUiModel): PluginUiModel? {
val reviewComments = ReviewsPageContainer(20, 0)
val reviews = MarketplaceRequests.getInstance().loadPluginReviews(existingModel, reviewComments.getNextPage()) ?: emptyList()
val reviews = UiPluginManager.getInstance().loadPluginReviews(existingModel.pluginId, reviewComments.getNextPage()) ?: emptyList()
reviewComments.addItems(reviews)
existingModel.reviewComments = reviewComments
return existingModel
@@ -8,9 +8,9 @@ import com.intellij.ide.plugins.marketplace.CheckErrorsResult
import com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
import com.intellij.ide.plugins.marketplace.InstallPluginResult
import com.intellij.ide.plugins.marketplace.IntellijPluginMetadata
import com.intellij.ide.plugins.marketplace.IntellijUpdateMetadata
import com.intellij.ide.plugins.marketplace.MarketplaceSearchPluginData
import com.intellij.ide.plugins.marketplace.PluginReviewComment
import com.intellij.ide.plugins.marketplace.PluginSearchResult
import com.intellij.ide.plugins.marketplace.PrepareToUninstallResult
import com.intellij.ide.plugins.marketplace.SetEnabledStateResult
import com.intellij.openapi.application.ModalityState
@@ -45,8 +45,8 @@ class UiPluginManager {
getController().closeSession(uuid.toString())
}
fun executeMarketplaceQuery(query: String, count: Int, includeUpgradeToCommercialIde: Boolean): List<MarketplaceSearchPluginData> {
return getController().executeMarketplaceQuery(query, count, includeUpgradeToCommercialIde)
fun executeMarketplaceQuery(query: String, count: Int, includeUpgradeToCommercialIde: Boolean): PluginSearchResult {
return getController().executePluginsSearch(query, count, includeUpgradeToCommercialIde)
}
fun getVisiblePlugins(showImplementationDetails: Boolean): List<PluginUiModel> {
@@ -65,12 +65,12 @@ class UiPluginManager {
return getController().isPluginDisabled(pluginId)
}
fun loadUpdateMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate, indicator: ProgressIndicator? = null): IntellijUpdateMetadata {
return getController().loadUpdateMetadata(xmlId, ideCompatibleUpdate, indicator)
fun loadPluginDetails(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate, indicator: ProgressIndicator? = null): PluginUiModel {
return getController().loadPluginDetails(xmlId, ideCompatibleUpdate, indicator)
}
fun loadPluginReviews(model: PluginUiModel, page: Int): List<PluginReviewComment>? {
return getController().loadPluginReviews(model.pluginId, page)
fun loadPluginReviews(pluginId: PluginId, page: Int): List<PluginReviewComment>? {
return getController().loadPluginReviews(pluginId, page)
}
fun tryUnloadPluginIfAllowed(parentComponent: JComponent?, pluginId: PluginId, isUpdate: Boolean): Boolean {
@@ -224,6 +224,14 @@ class UiPluginManager {
return getController().getPluginManagerUrl()
}
fun getAllPluginsTags(): Set<String> {
return getController().getAllPluginsTags()
}
fun getAllVendors(): Set<String> {
return getController().getAllVendors()
}
fun updateDescriptorsForInstalledPlugins() {
getController().updateDescriptorsForInstalledPlugins()
}
@@ -8,9 +8,8 @@ import com.intellij.ide.plugins.marketplace.CheckErrorsResult
import com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
import com.intellij.ide.plugins.marketplace.InstallPluginResult
import com.intellij.ide.plugins.marketplace.IntellijPluginMetadata
import com.intellij.ide.plugins.marketplace.IntellijUpdateMetadata
import com.intellij.ide.plugins.marketplace.MarketplaceSearchPluginData
import com.intellij.ide.plugins.marketplace.PluginReviewComment
import com.intellij.ide.plugins.marketplace.PluginSearchResult
import com.intellij.ide.plugins.marketplace.PrepareToUninstallResult
import com.intellij.ide.plugins.marketplace.SetEnabledStateResult
import com.intellij.openapi.application.ModalityState
@@ -31,8 +30,8 @@ interface UiPluginManagerController {
fun getInstalledPlugins(): List<PluginUiModel>
fun getUpdates(): List<PluginUiModel>
fun isPluginDisabled(pluginId: PluginId): Boolean
fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): List<MarketplaceSearchPluginData>
fun loadUpdateMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate, indicator: ProgressIndicator? = null): IntellijUpdateMetadata
fun executePluginsSearch(query: String, count: Int, includeIncompatible: Boolean): PluginSearchResult
fun loadPluginDetails(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate, indicator: ProgressIndicator? = null): PluginUiModel
fun loadPluginReviews(pluginId: PluginId, page: Int): List<PluginReviewComment>?
fun loadPluginMetadata(externalPluginId: String): IntellijPluginMetadata?
fun getPluginManagerUrl(): String
@@ -72,6 +71,8 @@ interface UiPluginManagerController {
fun updateDescriptorsForInstalledPlugins()
fun isNeedUpdate(pluginId: PluginId): Boolean
fun connectToUpdateServiceWithCounter(sessionId: String, callback: (Int?) -> Unit): PluginUpdatesService
fun getAllPluginsTags(): Set<String>
fun getAllVendors(): Set<String>
suspend fun resetSession(sessionId: String, removeSession: Boolean, parentComponent: JComponent? = null): Map<PluginId, Boolean>
@@ -12,6 +12,7 @@ import com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
import com.intellij.ide.plugins.marketplace.IntellijPluginMetadata
import com.intellij.ide.plugins.marketplace.IntellijUpdateMetadata
import com.intellij.ide.plugins.marketplace.PluginReviewComment
import com.intellij.ide.plugins.marketplace.PluginSearchResult
import com.intellij.ide.plugins.marketplace.SetEnabledStateResult
import com.intellij.ide.plugins.newui.PluginManagerSessionService
import com.intellij.ide.plugins.newui.PluginUiModel
@@ -47,7 +48,7 @@ class BackendPluginManagerApi : PluginManagerApi {
override suspend fun getInstalledPlugins(): List<PluginDto> {
return InstalledPluginsState.getInstance().installedPlugins.map(PluginDescriptorConverter::toPluginDto)
}
override suspend fun getUpdates(): List<PluginDto> {
return DefaultUiPluginManagerController.getUpdates().map { PluginDto.fromModel(it) }
}
@@ -110,16 +111,16 @@ class BackendPluginManagerApi : PluginManagerApi {
DefaultUiPluginManagerController.createSession(sessionId)
}
override suspend fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): List<MarketplaceSearchPluginData> {
return DefaultUiPluginManagerController.executeMarketplaceQuery(query, count, includeIncompatible)
override suspend fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): PluginSearchResult {
return DefaultUiPluginManagerController.executePluginsSearch(query, count, includeIncompatible)
}
override suspend fun isPluginDisabled(pluginId: PluginId): Boolean {
return PluginManagerCore.isDisabled(pluginId)
}
override suspend fun loadMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate): IntellijUpdateMetadata {
return DefaultUiPluginManagerController.loadUpdateMetadata(xmlId, ideCompatibleUpdate)
override suspend fun loadMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate): PluginDto {
return PluginDto.fromModel(DefaultUiPluginManagerController.loadPluginDetails(xmlId, ideCompatibleUpdate))
}
override suspend fun loadPluginReviews(pluginId: PluginId, page: Int): List<PluginReviewComment>? {
@@ -134,6 +135,14 @@ class BackendPluginManagerApi : PluginManagerApi {
return DefaultUiPluginManagerController.getPluginManagerUrl()
}
override suspend fun getAllPluginsTags(): Set<String> {
return DefaultUiPluginManagerController.getAllPluginsTags()
}
override suspend fun getAllVendors(): Set<String> {
return DefaultUiPluginManagerController.getAllVendors()
}
override suspend fun updateDescriptorsForInstalledPlugins() {
DefaultUiPluginManagerController.updateDescriptorsForInstalledPlugins()
}
@@ -8,9 +8,8 @@ import com.intellij.ide.plugins.marketplace.CheckErrorsResult
import com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
import com.intellij.ide.plugins.marketplace.InstallPluginResult
import com.intellij.ide.plugins.marketplace.IntellijPluginMetadata
import com.intellij.ide.plugins.marketplace.IntellijUpdateMetadata
import com.intellij.ide.plugins.marketplace.MarketplaceSearchPluginData
import com.intellij.ide.plugins.marketplace.PluginReviewComment
import com.intellij.ide.plugins.marketplace.PluginSearchResult
import com.intellij.ide.plugins.marketplace.PrepareToUninstallResult
import com.intellij.ide.plugins.marketplace.SetEnabledStateResult
import com.intellij.ide.plugins.newui.PluginUiModel
@@ -174,11 +173,11 @@ class BackendUiPluginManagerController() : UiPluginManagerController {
return awaitForResult { PluginManagerApi.getInstance().isPluginDisabled(pluginId) }
}
override fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): List<MarketplaceSearchPluginData> {
override fun executePluginsSearch(query: String, count: Int, includeIncompatible: Boolean): PluginSearchResult {
return awaitForResult { PluginManagerApi.getInstance().executeMarketplaceQuery(query, count, includeIncompatible) }
}
override fun loadUpdateMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate, indicator: ProgressIndicator?): IntellijUpdateMetadata {
override fun loadPluginDetails(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate, indicator: ProgressIndicator?): PluginUiModel {
return awaitForResult { PluginManagerApi.getInstance().loadMetadata(xmlId, ideCompatibleUpdate) }
}
@@ -194,6 +193,14 @@ class BackendUiPluginManagerController() : UiPluginManagerController {
return awaitForResult { PluginManagerApi.getInstance().getPluginManagerUrl() }
}
override fun getAllPluginsTags(): Set<String> {
return awaitForResult { PluginManagerApi.getInstance().getAllPluginsTags() }
}
override fun getAllVendors(): Set<String> {
return awaitForResult { PluginManagerApi.getInstance().getAllVendors() }
}
override fun updateDescriptorsForInstalledPlugins() {
service<BackendRpcCoroutineContext>().coroutineScope.launch {
PluginManagerApi.getInstance().updateDescriptorsForInstalledPlugins()
@@ -7,6 +7,7 @@ import com.intellij.ide.plugins.marketplace.IdeCompatibleUpdate
import com.intellij.ide.plugins.marketplace.IntellijPluginMetadata
import com.intellij.ide.plugins.marketplace.IntellijUpdateMetadata
import com.intellij.ide.plugins.marketplace.PluginReviewComment
import com.intellij.ide.plugins.marketplace.PluginSearchResult
import com.intellij.ide.plugins.marketplace.SetEnabledStateResult
import com.intellij.ide.plugins.newui.PluginUiModel
import com.intellij.openapi.extensions.PluginId
@@ -29,12 +30,14 @@ interface PluginManagerApi : RemoteApi<Unit> {
suspend fun findPlugin(pluginId: PluginId): PluginDto?
suspend fun getLastCompatiblePluginUpdateModel(pluginId: PluginId, buildNumber: String?): PluginDto?
suspend fun getLastCompatiblePluginUpdate(allIds: Set<PluginId>, throwExceptions: Boolean, buildNumber: String?): List<IdeCompatibleUpdate>
suspend fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): List<MarketplaceSearchPluginData>
suspend fun executeMarketplaceQuery(query: String, count: Int, includeIncompatible: Boolean): PluginSearchResult
suspend fun isPluginDisabled(pluginId: PluginId): Boolean
suspend fun loadMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate): IntellijUpdateMetadata
suspend fun loadMetadata(xmlId: String, ideCompatibleUpdate: IdeCompatibleUpdate): PluginDto
suspend fun loadPluginReviews(pluginId: PluginId, page: Int): List<PluginReviewComment>?
suspend fun loadPluginMetadata(externalPluginId: String): IntellijPluginMetadata?
suspend fun getPluginManagerUrl(): String
suspend fun getAllPluginsTags(): Set<String>
suspend fun getAllVendors(): Set<String>
suspend fun updateDescriptorsForInstalledPlugins()
suspend fun createSession(sessionId: String)
suspend fun closeSession(sessionId: String)