import chunk from 'lodash/chunk'; import type {NewQuery} from 'sentry/types/organization'; import type {Release} from 'sentry/types/release'; import type {TableData} from 'sentry/utils/discover/discoverQuery'; import EventView from 'sentry/utils/discover/eventView'; import {DiscoverDatasets} from 'sentry/utils/discover/types'; import type {ApiQueryKey} from 'sentry/utils/queryClient'; import {useApiQuery, useQueries} from 'sentry/utils/queryClient'; import {decodeScalar} from 'sentry/utils/queryString'; import {escapeFilterValue} from 'sentry/utils/tokenizeSearch'; import useApi from 'sentry/utils/useApi'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; export function useReleases(searchTerm?: string) { const organization = useOrganization(); const location = useLocation(); const {selection, isReady} = usePageFilters(); const {environments, projects} = selection; const api = useApi(); const releaseResults = useApiQuery( [ `/organizations/${organization.slug}/releases/`, { query: { project: projects, per_page: 50, environment: environments, query: searchTerm, sort: 'date', }, }, ], {staleTime: Infinity, enabled: isReady, retry: false} ); const chunks = releaseResults.data?.length ? chunk(releaseResults.data, 10) : []; const releaseMetrics = useQueries({ queries: chunks.map(releases => { const newQuery: NewQuery = { name: '', fields: ['release', 'count()'], query: `transaction.op:ui.load ${escapeFilterValue( `release:[${releases.map(r => `"${r.version}"`).join()}]` )}`, dataset: DiscoverDatasets.METRICS, version: 2, projects: selection.projects, }; const eventView = EventView.fromNewQueryWithPageFilters(newQuery, selection); const queryKey = [ `/organizations/${organization.slug}/events/`, { query: { ...eventView.getEventsAPIPayload(location), referrer: 'api.starfish.mobile-release-selector', }, }, ] as ApiQueryKey; return { queryKey, queryFn: () => api.requestPromise(queryKey[0], { method: 'GET', query: queryKey[1]?.query, }) as Promise, staleTime: Infinity, enabled: isReady && !releaseResults.isPending, retry: false, }; }), }); const metricsFetched = releaseMetrics.every(result => result.isFetched); const metricsStats: {[version: string]: {count: number}} = {}; if (metricsFetched) { releaseMetrics.forEach(c => c.data?.data?.forEach(release => { metricsStats[release.release] = {count: release['count()'] as number}; }) ); } const releaseStats: { dateCreated: string; version: string; count?: number; }[] = releaseResults.data?.length && metricsFetched ? releaseResults.data.flatMap(release => { const releaseVersion = release.version; const dateCreated = release.dateCreated; if (metricsStats[releaseVersion]?.count) { return { dateCreated, version: releaseVersion, count: metricsStats[releaseVersion]?.count, }; } return []; }) : []; return { ...releaseResults, data: releaseStats, isLoading: !metricsFetched || releaseResults.isPending, }; } export function useReleaseSelection(): { isLoading: boolean; primaryRelease: string | undefined; secondaryRelease: string | undefined; } { const location = useLocation(); const {data: releases, isLoading} = useReleases(); // If there are more than 1 release, the first one should be the older one const primaryRelease = decodeScalar(location.query.primaryRelease) ?? (releases && releases.length > 1 ? releases?.[1]?.version : releases?.[0]?.version); // If there are more than 1 release, the second one should be the newest one const secondaryRelease = decodeScalar(location.query.secondaryRelease) ?? (releases && releases.length > 1 ? releases?.[0]?.version : undefined); return {primaryRelease, secondaryRelease, isLoading}; }