refactor: add API endpoint to list latest plugin versions and query it in a asynchronous way (#17695)

This commit is contained in:
Yeuoly 2025-04-09 18:49:27 +09:00 committed by GitHub
parent 2c2efe2e1e
commit 33324ee23d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 67 additions and 23 deletions

View File

@ -49,6 +49,23 @@ class PluginListApi(Resource):
return jsonable_encoder({"plugins": plugins})
class PluginListLatestVersionsApi(Resource):
@setup_required
@login_required
@account_initialization_required
def post(self):
req = reqparse.RequestParser()
req.add_argument("plugin_ids", type=list, required=True, location="json")
args = req.parse_args()
try:
versions = PluginService.list_latest_versions(args["plugin_ids"])
except PluginDaemonClientSideError as e:
raise ValueError(e)
return jsonable_encoder({"versions": versions})
class PluginListInstallationsFromIdsApi(Resource):
@setup_required
@login_required
@ -453,6 +470,7 @@ class PluginFetchPermissionApi(Resource):
api.add_resource(PluginDebuggingKeyApi, "/workspaces/current/plugin/debugging-key")
api.add_resource(PluginListApi, "/workspaces/current/plugin/list")
api.add_resource(PluginListLatestVersionsApi, "/workspaces/current/plugin/list/latest-versions")
api.add_resource(PluginListInstallationsFromIdsApi, "/workspaces/current/plugin/list/installations/ids")
api.add_resource(PluginIconApi, "/workspaces/current/plugin/icon")
api.add_resource(PluginUploadFromPkgApi, "/workspaces/current/plugin/upload/pkg")

View File

@ -120,8 +120,6 @@ class PluginEntity(PluginInstallation):
name: str
installation_id: str
version: str
latest_version: Optional[str] = None
latest_unique_identifier: Optional[str] = None
@model_validator(mode="after")
def set_plugin_id(self):

View File

@ -94,6 +94,13 @@ class PluginService:
manager = PluginDebuggingManager()
return manager.get_debugging_key(tenant_id)
@staticmethod
def list_latest_versions(plugin_ids: Sequence[str]) -> Mapping[str, Optional[LatestPluginCache]]:
"""
List the latest versions of the plugins
"""
return PluginService.fetch_latest_plugin_version(plugin_ids)
@staticmethod
def list(tenant_id: str) -> list[PluginEntity]:
"""
@ -101,22 +108,6 @@ class PluginService:
"""
manager = PluginInstallationManager()
plugins = manager.list_plugins(tenant_id)
plugin_ids = [plugin.plugin_id for plugin in plugins if plugin.source == PluginInstallationSource.Marketplace]
try:
manifests = PluginService.fetch_latest_plugin_version(plugin_ids)
except Exception:
manifests = {}
logger.exception("failed to fetch plugin manifests")
for plugin in plugins:
if plugin.source == PluginInstallationSource.Marketplace:
if plugin.plugin_id in manifests:
latest_plugin_cache = manifests[plugin.plugin_id]
if latest_plugin_cache:
# set latest_version
plugin.latest_version = latest_plugin_cache.version
plugin.latest_unique_identifier = latest_plugin_cache.unique_identifier
return plugins
@staticmethod

View File

@ -3,17 +3,23 @@ import { useMemo } from 'react'
import type { FilterState } from './filter-management'
import FilterManagement from './filter-management'
import List from './list'
import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
import { useInstalledLatestVersion, useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins'
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
import { usePluginPageContext } from './context'
import { useDebounceFn } from 'ahooks'
import Empty from './empty'
import Loading from '../../base/loading'
import { PluginSource } from '../types'
const PluginsPanel = () => {
const filters = usePluginPageContext(v => v.filters) as FilterState
const setFilters = usePluginPageContext(v => v.setFilters)
const { data: pluginList, isLoading: isPluginListLoading } = useInstalledPluginList()
const { data: installedLatestVersion } = useInstalledLatestVersion(
pluginList?.plugins
.filter(plugin => plugin.source === PluginSource.marketplace)
.map(plugin => plugin.plugin_id) ?? [],
)
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
const currentPluginID = usePluginPageContext(v => v.currentPluginID)
const setCurrentPluginID = usePluginPageContext(v => v.setCurrentPluginID)
@ -22,9 +28,17 @@ const PluginsPanel = () => {
setFilters(filters)
}, { wait: 500 })
const pluginListWithLatestVersion = useMemo(() => {
return pluginList?.plugins.map(plugin => ({
...plugin,
latest_version: installedLatestVersion?.versions[plugin.plugin_id]?.version ?? '',
latest_unique_identifier: installedLatestVersion?.versions[plugin.plugin_id]?.unique_identifier ?? '',
})) || []
}, [pluginList, installedLatestVersion])
const filteredList = useMemo(() => {
const { categories, searchQuery, tags } = filters
const filteredList = pluginList?.plugins.filter((plugin) => {
const filteredList = pluginListWithLatestVersion.filter((plugin) => {
return (
(categories.length === 0 || categories.includes(plugin.declaration.category))
&& (tags.length === 0 || tags.some(tag => plugin.declaration.tags.includes(tag)))
@ -32,12 +46,12 @@ const PluginsPanel = () => {
)
})
return filteredList
}, [pluginList, filters])
}, [pluginListWithLatestVersion, filters])
const currentPluginDetail = useMemo(() => {
const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentPluginID)
const detail = pluginListWithLatestVersion.find(plugin => plugin.plugin_id === currentPluginID)
return detail
}, [currentPluginID, pluginList?.plugins])
}, [currentPluginID, pluginListWithLatestVersion])
const handleHide = () => setCurrentPluginID(undefined)

View File

@ -318,6 +318,15 @@ export type InstalledPluginListResponse = {
plugins: PluginDetail[]
}
export type InstalledLatestVersionResponse = {
versions: {
[plugin_id: string]: {
unique_identifier: string
version: string
} | null
}
}
export type UninstallPluginResponse = {
success: boolean
}

View File

@ -9,6 +9,7 @@ import type {
Dependency,
GitHubItemAndMarketPlaceDependency,
InstallPackageResponse,
InstalledLatestVersionResponse,
InstalledPluginListResponse,
PackageDependency,
Permissions,
@ -72,6 +73,19 @@ export const useInstalledPluginList = (disable?: boolean) => {
})
}
export const useInstalledLatestVersion = (pluginIds: string[]) => {
return useQuery<InstalledLatestVersionResponse>({
queryKey: [NAME_SPACE, 'installedLatestVersion', pluginIds],
queryFn: () => post<InstalledLatestVersionResponse>('/workspaces/current/plugin/list/latest-versions', {
body: {
plugin_ids: pluginIds,
},
}),
enabled: !!pluginIds.length,
initialData: pluginIds.length ? undefined : { versions: {} },
})
}
export const useInvalidateInstalledPluginList = () => {
const queryClient = useQueryClient()
const invalidateAllBuiltInTools = useInvalidateAllBuiltInTools()