import {Fragment} from 'react'; import {useTheme} from '@emotion/react'; import type {ToolboxComponentOption} from 'echarts'; import {Client} from 'sentry/api'; import EventsChart from 'sentry/components/charts/eventsChart'; import EventsRequest from 'sentry/components/charts/eventsRequest'; import {HeaderTitleLegend, HeaderValue} from 'sentry/components/charts/styles'; import {getInterval} from 'sentry/components/charts/utils'; import QuestionTooltip from 'sentry/components/questionTooltip'; import {t} from 'sentry/locale'; import type {Organization} from 'sentry/types/organization'; import type {ReleaseProject, ReleaseWithHealth} from 'sentry/types/release'; import {ReleaseComparisonChartType} from 'sentry/types/release'; import {tooltipFormatter} from 'sentry/utils/discover/charts'; import EventView from 'sentry/utils/discover/eventView'; import {aggregateOutputType} from 'sentry/utils/discover/fields'; import {DiscoverDatasets} from 'sentry/utils/discover/types'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; import useApi from 'sentry/utils/useApi'; import {useLocation} from 'sentry/utils/useLocation'; import useRouter from 'sentry/utils/useRouter'; import withOrganization from 'sentry/utils/withOrganization'; import {getTermHelp, PerformanceTerm} from 'sentry/views/performance/data'; import { generateReleaseMarkLines, releaseComparisonChartTitles, releaseMarkLinesLabels, } from '../../utils'; type Props = { chartType: ReleaseComparisonChartType; diff: React.ReactNode; organization: Organization; project: ReleaseProject; release: ReleaseWithHealth; value: React.ReactNode; end?: string; period?: string | null; start?: string; utc?: boolean; }; function ReleaseEventsChart({ release, project, chartType, value, diff, organization, period, start, end, utc, }: Props) { const location = useLocation(); const router = useRouter(); const api = useApi(); const theme = useTheme(); function getColors() { const colors = theme.charts.getColorPalette(14); switch (chartType) { case ReleaseComparisonChartType.ERROR_COUNT: return [colors[12]]; case ReleaseComparisonChartType.TRANSACTION_COUNT: return [colors[0]]; case ReleaseComparisonChartType.FAILURE_RATE: return [colors[9]]; default: return undefined; } } function getQuery() { const releaseFilter = `release:${release.version}`; switch (chartType) { case ReleaseComparisonChartType.ERROR_COUNT: return new MutableSearch(['event.type:error', releaseFilter]).formatString(); case ReleaseComparisonChartType.TRANSACTION_COUNT: return new MutableSearch([ 'event.type:transaction', releaseFilter, ]).formatString(); case ReleaseComparisonChartType.FAILURE_RATE: return new MutableSearch([ 'event.type:transaction', releaseFilter, ]).formatString(); default: return ''; } } function getField() { switch (chartType) { case ReleaseComparisonChartType.ERROR_COUNT: return ['count()']; case ReleaseComparisonChartType.TRANSACTION_COUNT: return ['count()']; case ReleaseComparisonChartType.FAILURE_RATE: return ['failure_rate()']; default: return undefined; } } function getYAxis() { switch (chartType) { case ReleaseComparisonChartType.ERROR_COUNT: return 'count()'; case ReleaseComparisonChartType.TRANSACTION_COUNT: return 'count()'; case ReleaseComparisonChartType.FAILURE_RATE: return 'failure_rate()'; default: return ''; } } function getHelp() { switch (chartType) { case ReleaseComparisonChartType.FAILURE_RATE: return getTermHelp(organization, PerformanceTerm.FAILURE_RATE); default: return null; } } const eventView = EventView.fromSavedQueryOrLocation(undefined, location); const projects = eventView.project as number[]; const environments = eventView.environment as string[]; const markLines = generateReleaseMarkLines(release, project, theme, location); return ( /** * EventsRequest is used to fetch the second series of Failure Rate chart. * First one is "This Release" - fetched as usual inside EventsChart * component and this one is "All Releases" that's shoehorned in place * of Previous Period via previousSeriesTransformer */ {({timeseriesData, loading, reloading}) => ( {releaseComparisonChartTitles[chartType]} {getHelp() && ( )} {value} {diff} } legendOptions={{ right: 10, top: 0, textStyle: { padding: [2, 0, 0, 0], }, }} chartOptions={{ grid: {left: '10px', right: '10px', top: '70px', bottom: '0px'}, tooltip: { trigger: 'axis', truncate: 80, valueFormatter: (val: number, label?: string) => { if (label && Object.values(releaseMarkLinesLabels).includes(label)) { return ''; } return tooltipFormatter(val, aggregateOutputType(getYAxis())); }, } as ToolboxComponentOption, }} usePageZoom height={240} seriesTransformer={series => [...series, ...markLines]} previousSeriesTransformer={series => { if (chartType === ReleaseComparisonChartType.FAILURE_RATE) { return timeseriesData?.[0]; } return series; }} referrer="api.releases.release-details-chart" /> )} ); } export default withOrganization(ReleaseEventsChart);