import type {SessionApiResponse} from 'sentry/types/organization'; import {percent} from 'sentry/utils'; import {useApiQuery} from 'sentry/utils/queryClient'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import useSessionAdoptionRate from 'sentry/views/insights/sessions/queries/useSessionProjectTotal'; export default function useCrashFreeSessions() { const location = useLocation(); const organization = useOrganization(); const { data: sessionData, isPending, error, } = useApiQuery( [ `/organizations/${organization.slug}/sessions/`, { query: { ...location.query, field: ['sum(session)'], groupBy: ['session.status', 'release'], }, }, ], {staleTime: 0} ); const projectTotal = useSessionAdoptionRate(); if (isPending) { return { series: [], isPending: true, error, }; } if (!sessionData && !isPending) { return { series: [], isPending: false, error, }; } const getStatusSeries = (status: string, groups: typeof sessionData.groups) => groups.find(group => group.by['session.status'] === status)?.series['sum(session)'] ?? []; // Maps release to its API response groups const releaseGroupMap = new Map(); // Maps release to its adoption rate calculation const releaseAdoptionMap = new Map(); sessionData.groups.forEach(group => { const release = group.by.release?.toString() ?? ''; if (!releaseGroupMap.has(release)) { releaseGroupMap.set(release, []); const releaseGroups = sessionData.groups.filter( g => g.by.release?.toString() === release ); // Calculate total sessions for entire time period const totalSessionCount = releaseGroups.reduce( (acc, g) => acc + (g.totals['sum(session)'] ?? 0), 0 ); // Only consider releases with total sessions > 0 if (totalSessionCount > 0) { releaseAdoptionMap.set(release, percent(totalSessionCount, projectTotal)); } } if (releaseAdoptionMap.has(release)) { releaseGroupMap.get(release)!.push(group); } }); // Get top 5 releases const topReleases = Array.from(releaseAdoptionMap.entries()) .sort(([, a], [, b]) => b - a) .slice(0, 5) .map(([release]) => release); const series = topReleases.map(release => { const groups = releaseGroupMap.get(release)!; // Get all status series at once and calculate crash-free session percentage for each interval const seriesData = getStatusSeries('crashed', groups).map((crashedCount, idx) => { const intervalTotal = [ crashedCount, getStatusSeries('abnormal', groups)[idx] || 0, getStatusSeries('crashed', groups)[idx] || 0, getStatusSeries('healthy', groups)[idx] || 0, ].reduce((sum, val) => sum + val, 0); return { name: sessionData.intervals[idx] ?? '', value: intervalTotal > 0 ? 1 - crashedCount / intervalTotal : 1, }; }); return { data: seriesData, seriesName: `crash_free_session_rate_${release}`, meta: { fields: { [`crash_free_session_rate_${release}`]: 'percentage' as const, time: 'date' as const, }, units: { [`crash_free_session_rate_${release}`]: '%', }, }, }; }); return {series, releases: topReleases, isPending, error}; }