import { useQueries } from '@tanstack/react-query'; import { useMemo } from 'react'; import { compact, flatMap } from 'lodash'; import { withGlobalError } from '@/react-tools/react-query'; import axios, { parseAxiosError } from '@/portainer/services/axios'; interface HelmSearch { entries: Entries; } interface Entries { [key: string]: { version: string; appVersion: string }[]; } export interface ChartVersion { Chart?: string; Repo: string; Label?: string; Version: string; AppVersion?: string; } type RepoSource = { repo?: string; }; /** * React hook to get a list of available versions for a chart from specified repositories * * @param chart The chart name to get versions for * @param repositories Array of repository URLs to search in * @param staleTime Stale time for the query * @param useCache Whether to use the cache for the query */ export function useHelmRepoVersions( chart: string, staleTime: number, repoSources: RepoSource[] = [], useCache: boolean = true ) { // Fetch versions from each repository in parallel as separate queries const versionQueries = useQueries({ queries: useMemo( () => repoSources.map(({ repo }) => ({ queryKey: ['helm', 'repositories', chart, repo, useCache], queryFn: () => getSearchHelmRepo({ repo, chart, useCache }), enabled: !!chart && repoSources.length > 0, staleTime, ...withGlobalError(`Unable to retrieve versions from ${repo}`), })), [repoSources, chart, staleTime, useCache] ), }); // Combine the results from all repositories for easier consumption const allVersions = useMemo(() => { const successfulResults = compact(versionQueries.map((q) => q.data)); return flatMap(successfulResults); }, [versionQueries]); return { data: allVersions, isInitialLoading: versionQueries.some((q) => q.isInitialLoading), isError: versionQueries.some((q) => q.isError), isFetching: versionQueries.some((q) => q.isFetching), refetch: () => Promise.all(versionQueries.map((q) => q.refetch())), }; } type SearchRepoParams = { repo?: string; chart: string; useCache?: boolean; }; /** * Get Helm repositories for user */ async function getSearchHelmRepo( params: SearchRepoParams ): Promise { try { const { data } = await axios.get(`templates/helm`, { params, }); // if separated by '/', take the last part const chartKey = params.chart.split('/').pop() || params.chart; const versions = data.entries[chartKey]; return ( versions?.map((v) => ({ Repo: params.repo ?? '', Version: v.version, AppVersion: v.appVersion, })) ?? [] ); } catch (err) { throw parseAxiosError(err, 'Unable to retrieve helm repositories for user'); } }