Browse Source

ref(performance-metrics): Port TransactionOverview - vitalsChart to metrics (#31180)

Priscila Oliveira 3 years ago
parent
commit
403d111cac

+ 5 - 7
static/app/views/performance/landing/widgets/transforms/transformMetricsToArea.tsx

@@ -9,17 +9,17 @@ import {aggregateOutputType} from 'sentry/utils/discover/fields';
 import {MetricsRequestRenderProps} from 'sentry/utils/metrics/metricsRequest';
 
 import {WidgetDataConstraint, WidgetPropUnion} from '../types';
-import {PerformanceWidgetSetting} from '../widgetDefinitions';
 
 // Sentry treats transactions with a status other than “ok,” “cancelled”, and “unknown” as failures.
 // For more details, see https://docs.sentry.io/product/performance/metrics/#failure-rate
 const TRANSACTION_SUCCESS_STATUS = ['ok', 'unknown', 'cancelled'];
 
 export function transformMetricsToArea<T extends WidgetDataConstraint>(
-  widgetProps: Pick<WidgetPropUnion<T>, 'location' | 'fields' | 'chartSetting'>,
-  results: MetricsRequestRenderProps
+  widgetProps: Pick<WidgetPropUnion<T>, 'location' | 'fields'>,
+  results: MetricsRequestRenderProps,
+  failureRate = false
 ) {
-  const {location, fields, chartSetting} = widgetProps;
+  const {location, fields} = widgetProps;
 
   const {start, end, utc, interval, statsPeriod} = normalizeDateTimeParams(
     location.query
@@ -52,9 +52,7 @@ export function transformMetricsToArea<T extends WidgetDataConstraint>(
 
   const metricsField = fields[0];
 
-  const isFailureRateWidget = chartSetting === PerformanceWidgetSetting.FAILURE_RATE_AREA;
-
-  if (isFailureRateWidget) {
+  if (failureRate) {
     const failedGroups = response.groups.filter(
       group => !TRANSACTION_SUCCESS_STATUS.includes(group.by['transaction.status'])
     );

+ 8 - 2
static/app/views/performance/landing/widgets/widgets/lineChartListWidgetMetrics.tsx

@@ -24,7 +24,12 @@ import SelectableList, {
 } from '../components/selectableList';
 import {transformMetricsToArea} from '../transforms/transformMetricsToArea';
 import {transformMetricsToList} from '../transforms/transformMetricsToList';
-import {PerformanceWidgetProps, QueryDefinition, WidgetDataResult} from '../types';
+import {
+  GenericPerformanceWidgetProps,
+  PerformanceWidgetProps,
+  QueryDefinition,
+  WidgetDataResult,
+} from '../types';
 import {PerformanceWidgetSetting} from '../widgetDefinitions';
 
 type DataType = {
@@ -129,7 +134,8 @@ export function LineChartListWidgetMetrics(props: PerformanceWidgetProps) {
           </MetricsRequest>
         );
       },
-      transform: transformMetricsToArea,
+      transform: (data: GenericPerformanceWidgetProps<DataType>, result) =>
+        transformMetricsToArea(data, result),
     };
   }, [chartSetting, selectedListIndex]);
 

+ 10 - 7
static/app/views/performance/landing/widgets/widgets/singleFieldAreaWidgetMetrics.tsx

@@ -10,7 +10,12 @@ import _DurationChart from 'sentry/views/performance/charts/chart';
 
 import {GenericPerformanceWidget} from '../components/performanceWidget';
 import {transformMetricsToArea} from '../transforms/transformMetricsToArea';
-import {PerformanceWidgetProps, QueryDefinition, WidgetDataResult} from '../types';
+import {
+  GenericPerformanceWidgetProps,
+  PerformanceWidgetProps,
+  QueryDefinition,
+  WidgetDataResult,
+} from '../types';
 import {PerformanceWidgetSetting} from '../widgetDefinitions';
 
 import {DurationChart, HighlightNumber, Subtitle} from './singleFieldAreaWidget';
@@ -38,6 +43,7 @@ export function SingleFieldAreaWidgetMetrics(props: PerformanceWidgetProps) {
   }
 
   const field = fields[0];
+  const failureRate = chartSetting === PerformanceWidgetSetting.FAILURE_RATE_AREA;
 
   const chart = useMemo<QueryDefinition<DataType, WidgetDataResult>>(
     () => ({
@@ -61,17 +67,14 @@ export function SingleFieldAreaWidgetMetrics(props: PerformanceWidgetProps) {
           environment={environment}
           query={new MutableSearch(eventView.query).formatString()} // TODO(metrics): not all tags will be compatible with metrics
           field={decodeList(chartFields)}
-          groupBy={
-            chartSetting === PerformanceWidgetSetting.FAILURE_RATE_AREA
-              ? ['transaction.status']
-              : undefined
-          }
+          groupBy={failureRate ? ['transaction.status'] : undefined}
           includePrevious
         >
           {children}
         </MetricsRequest>
       ),
-      transform: transformMetricsToArea,
+      transform: (data: GenericPerformanceWidgetProps<DataType>, result) =>
+        transformMetricsToArea(data, result, failureRate),
     }),
     [chartSetting]
   );

+ 1 - 12
static/app/views/performance/transactionSummary/transactionOverview/durationChart.tsx

@@ -20,26 +20,15 @@ import {t, tct} from 'sentry/locale';
 import {OrganizationSummary} from 'sentry/types';
 import {getUtcToLocalDateObject} from 'sentry/utils/dates';
 import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts';
-import EventView from 'sentry/utils/discover/eventView';
 import getDynamicText from 'sentry/utils/getDynamicText';
 import useApi from 'sentry/utils/useApi';
 
+import {ViewProps} from '../../types';
 import {
   SPAN_OPERATION_BREAKDOWN_FILTER_TO_FIELD,
   SpanOperationBreakdownFilter,
 } from '../filter';
 
-const QUERY_KEYS = [
-  'environment',
-  'project',
-  'query',
-  'start',
-  'end',
-  'statsPeriod',
-] as const;
-
-type ViewProps = Pick<EventView, typeof QUERY_KEYS[number]>;
-
 type Props = WithRouterProps &
   ViewProps & {
     location: Location;

+ 3 - 14
static/app/views/performance/transactionSummary/transactionOverview/durationPercentileChart.tsx

@@ -19,22 +19,11 @@ import EventView from 'sentry/utils/discover/eventView';
 import {getDuration} from 'sentry/utils/formatters';
 import {Theme} from 'sentry/utils/theme';
 
+import {ViewProps} from '../../types';
+import {QUERY_KEYS} from '../../utils';
 import {filterToColor, filterToField, SpanOperationBreakdownFilter} from '../filter';
 
-const QUERY_KEYS = [
-  'environment',
-  'project',
-  'query',
-  'start',
-  'end',
-  'statsPeriod',
-] as const;
-
-type ViewProps = Pick<EventView, typeof QUERY_KEYS[number]>;
-
-type ApiResult = {
-  [bucket: string]: number;
-};
+type ApiResult = Record<string, number>;
 
 type Props = AsyncComponent['props'] &
   ViewProps & {

+ 1 - 10
static/app/views/performance/transactionSummary/transactionOverview/latencyChart.tsx

@@ -30,16 +30,7 @@ export const ZOOM_START = 'startDuration';
 export const ZOOM_END = 'endDuration';
 
 const NUM_BUCKETS = 50;
-const QUERY_KEYS = [
-  'environment',
-  'project',
-  'query',
-  'start',
-  'end',
-  'statsPeriod',
-] as const;
-
-type ViewProps = Pick<EventView, typeof QUERY_KEYS[number]>;
+import {ViewProps} from '../../types';
 
 type Props = ViewProps & {
   organization: OrganizationSummary;

+ 2 - 4
static/app/views/performance/transactionSummary/transactionOverview/sidebarCharts.tsx

@@ -33,7 +33,6 @@ import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import useApi from 'sentry/utils/useApi';
 import {getTermHelp, PERFORMANCE_TERM} from 'sentry/views/performance/data';
 import {transformMetricsToArea} from 'sentry/views/performance/landing/widgets/transforms/transformMetricsToArea';
-import {PerformanceWidgetSetting} from 'sentry/views/performance/landing/widgets/widgetDefinitions';
 
 type ContainerProps = WithRouterProps & {
   organization: Organization;
@@ -327,9 +326,9 @@ function SidebarChartsContainer({
             {
               location,
               fields,
-              chartSetting: PerformanceWidgetSetting.FAILURE_RATE_AREA,
             },
-            failureRateRequestProps
+            failureRateRequestProps,
+            true
           );
 
           const failureRateSerie = failureRateData.data.map(values => ({
@@ -357,7 +356,6 @@ function SidebarChartsContainer({
                   {
                     location,
                     fields,
-                    chartSetting: PerformanceWidgetSetting.TPM_AREA,
                   },
                   tpmRequestProps
                 );

+ 1 - 12
static/app/views/performance/transactionSummary/transactionOverview/trendChart.tsx

@@ -20,22 +20,11 @@ import {t} from 'sentry/locale';
 import {OrganizationSummary} from 'sentry/types';
 import {getUtcToLocalDateObject} from 'sentry/utils/dates';
 import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts';
-import EventView from 'sentry/utils/discover/eventView';
 import getDynamicText from 'sentry/utils/getDynamicText';
 import useApi from 'sentry/utils/useApi';
 
 import {transformEventStatsSmoothed} from '../../trends/utils';
-
-const QUERY_KEYS = [
-  'environment',
-  'project',
-  'query',
-  'start',
-  'end',
-  'statsPeriod',
-] as const;
-
-type ViewProps = Pick<EventView, typeof QUERY_KEYS[number]>;
+import {ViewProps} from '../../types';
 
 type Props = WithRouterProps &
   ViewProps & {

+ 0 - 238
static/app/views/performance/transactionSummary/transactionOverview/vitalsChart.tsx

@@ -1,238 +0,0 @@
-import {Fragment} from 'react';
-import {browserHistory, withRouter, WithRouterProps} from 'react-router';
-import {useTheme} from '@emotion/react';
-import {Location} from 'history';
-
-import ChartZoom from 'sentry/components/charts/chartZoom';
-import ErrorPanel from 'sentry/components/charts/errorPanel';
-import EventsRequest from 'sentry/components/charts/eventsRequest';
-import LineChart from 'sentry/components/charts/lineChart';
-import ReleaseSeries from 'sentry/components/charts/releaseSeries';
-import {HeaderTitleLegend} from 'sentry/components/charts/styles';
-import TransitionChart from 'sentry/components/charts/transitionChart';
-import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
-import {getInterval, getSeriesSelection} from 'sentry/components/charts/utils';
-import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
-import Placeholder from 'sentry/components/placeholder';
-import QuestionTooltip from 'sentry/components/questionTooltip';
-import {IconWarning} from 'sentry/icons';
-import {t} from 'sentry/locale';
-import {OrganizationSummary} from 'sentry/types';
-import {getUtcToLocalDateObject} from 'sentry/utils/dates';
-import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts';
-import EventView from 'sentry/utils/discover/eventView';
-import {getAggregateArg, getMeasurementSlug} from 'sentry/utils/discover/fields';
-import getDynamicText from 'sentry/utils/getDynamicText';
-import useApi from 'sentry/utils/useApi';
-import {TransactionsListOption} from 'sentry/views/releases/detail/overview';
-
-const QUERY_KEYS = [
-  'environment',
-  'project',
-  'query',
-  'start',
-  'end',
-  'statsPeriod',
-] as const;
-
-type ViewProps = Pick<EventView, typeof QUERY_KEYS[number]>;
-
-type Props = WithRouterProps &
-  ViewProps & {
-    location: Location;
-    organization: OrganizationSummary;
-    queryExtra: object;
-    withoutZerofill: boolean;
-  };
-
-const YAXIS_VALUES = [
-  'p75(measurements.fp)',
-  'p75(measurements.fcp)',
-  'p75(measurements.lcp)',
-  'p75(measurements.fid)',
-];
-
-function VitalsChart({
-  project,
-  environment,
-  location,
-  organization,
-  query,
-  statsPeriod,
-  router,
-  queryExtra,
-  withoutZerofill,
-  start: propsStart,
-  end: propsEnd,
-}: Props) {
-  const api = useApi();
-  const theme = useTheme();
-
-  const handleLegendSelectChanged = legendChange => {
-    const {selected} = legendChange;
-    const unselected = Object.keys(selected).filter(key => !selected[key]);
-
-    const to = {
-      ...location,
-      query: {
-        ...location.query,
-        unselectedSeries: unselected,
-      },
-    };
-    browserHistory.push(to);
-  };
-
-  const start = propsStart ? getUtcToLocalDateObject(propsStart) : null;
-  const end = propsEnd ? getUtcToLocalDateObject(propsEnd) : null;
-  const {utc} = normalizeDateTimeParams(location.query);
-
-  const legend = {
-    right: 10,
-    top: 0,
-    selected: getSeriesSelection(location),
-    formatter: seriesName => {
-      const arg = getAggregateArg(seriesName);
-      if (arg !== null) {
-        const slug = getMeasurementSlug(arg);
-        if (slug !== null) {
-          seriesName = slug.toUpperCase();
-        }
-      }
-      return seriesName;
-    },
-  };
-
-  const datetimeSelection = {
-    start,
-    end,
-    period: statsPeriod,
-  };
-
-  return (
-    <Fragment>
-      <HeaderTitleLegend>
-        {t('Web Vitals Breakdown')}
-        <QuestionTooltip
-          size="sm"
-          position="top"
-          title={t(
-            `Web Vitals Breakdown reflects the 75th percentile of web vitals over time.`
-          )}
-        />
-      </HeaderTitleLegend>
-      <ChartZoom
-        router={router}
-        period={statsPeriod}
-        start={start}
-        end={end}
-        utc={utc === 'true'}
-      >
-        {zoomRenderProps => (
-          <EventsRequest
-            api={api}
-            organization={organization}
-            period={statsPeriod}
-            project={project}
-            environment={environment}
-            start={start}
-            end={end}
-            interval={getInterval(datetimeSelection, 'high')}
-            showLoading={false}
-            query={query}
-            includePrevious={false}
-            yAxis={YAXIS_VALUES}
-            partial
-            withoutZerofill={withoutZerofill}
-            referrer="api.performance.transaction-summary.vitals-chart"
-          >
-            {({results, errored, loading, reloading, timeframe}) => {
-              if (errored) {
-                return (
-                  <ErrorPanel>
-                    <IconWarning color="gray500" size="lg" />
-                  </ErrorPanel>
-                );
-              }
-
-              const chartOptions = {
-                grid: {
-                  left: '10px',
-                  right: '10px',
-                  top: '40px',
-                  bottom: '0px',
-                },
-                seriesOptions: {
-                  showSymbol: false,
-                },
-                tooltip: {
-                  trigger: 'axis' as const,
-                  valueFormatter: tooltipFormatter,
-                },
-                xAxis: timeframe
-                  ? {
-                      min: timeframe.start,
-                      max: timeframe.end,
-                    }
-                  : undefined,
-                yAxis: {
-                  axisLabel: {
-                    color: theme.chartLabel,
-                    // p75(measurements.fcp) coerces the axis to be time based
-                    formatter: (value: number) =>
-                      axisLabelFormatter(value, 'p75(measurements.fcp)'),
-                  },
-                },
-              };
-
-              const colors =
-                (results && theme.charts.getColorPalette(results.length - 2)) || [];
-
-              // Create a list of series based on the order of the fields,
-              const series = results
-                ? results.map((values, i: number) => ({
-                    ...values,
-                    color: colors[i],
-                  }))
-                : [];
-
-              return (
-                <ReleaseSeries
-                  start={start}
-                  end={end}
-                  queryExtra={{
-                    ...queryExtra,
-                    showTransactions: TransactionsListOption.SLOW_LCP,
-                  }}
-                  period={statsPeriod}
-                  utc={utc === 'true'}
-                  projects={project}
-                  environments={environment}
-                >
-                  {({releaseSeries}) => (
-                    <TransitionChart loading={loading} reloading={reloading}>
-                      <TransparentLoadingMask visible={reloading} />
-                      {getDynamicText({
-                        value: (
-                          <LineChart
-                            {...zoomRenderProps}
-                            {...chartOptions}
-                            legend={legend}
-                            onLegendSelectChanged={handleLegendSelectChanged}
-                            series={[...series, ...releaseSeries]}
-                          />
-                        ),
-                        fixed: <Placeholder height="200px" testId="skeleton-ui" />,
-                      })}
-                    </TransitionChart>
-                  )}
-                </ReleaseSeries>
-              );
-            }}
-          </EventsRequest>
-        )}
-      </ChartZoom>
-    </Fragment>
-  );
-}
-
-export default withRouter(VitalsChart);

+ 136 - 0
static/app/views/performance/transactionSummary/transactionOverview/vitalsChart/content.tsx

@@ -0,0 +1,136 @@
+import {InjectedRouter} from 'react-router';
+import {Query} from 'history';
+
+import ChartZoom from 'sentry/components/charts/chartZoom';
+import ErrorPanel from 'sentry/components/charts/errorPanel';
+import LineChart from 'sentry/components/charts/lineChart';
+import ReleaseSeries from 'sentry/components/charts/releaseSeries';
+import TransitionChart from 'sentry/components/charts/transitionChart';
+import TransparentLoadingMask from 'sentry/components/charts/transparentLoadingMask';
+import Placeholder from 'sentry/components/placeholder';
+import {IconWarning} from 'sentry/icons';
+import {Series} from 'sentry/types/echarts';
+import {axisLabelFormatter, tooltipFormatter} from 'sentry/utils/discover/charts';
+import getDynamicText from 'sentry/utils/getDynamicText';
+import {Theme} from 'sentry/utils/theme';
+import {TransactionsListOption} from 'sentry/views/releases/detail/overview';
+
+type Props = {
+  loading: boolean;
+  reloading: boolean;
+  theme: Theme;
+  errored: boolean;
+  queryExtra: Query;
+  router: InjectedRouter;
+  series?: Series[];
+  timeFrame?: {
+    start: number;
+    end: number;
+  };
+} & Omit<React.ComponentProps<typeof ReleaseSeries>, 'children' | 'queryExtra'> &
+  Pick<React.ComponentProps<typeof LineChart>, 'onLegendSelectChanged' | 'legend'>;
+
+function Content({
+  errored,
+  theme,
+  series: data,
+  timeFrame,
+  start,
+  end,
+  period,
+  projects,
+  environments,
+  loading,
+  reloading,
+  legend,
+  utc,
+  queryExtra,
+  router,
+  onLegendSelectChanged,
+}: Props) {
+  if (errored) {
+    return (
+      <ErrorPanel>
+        <IconWarning color="gray500" size="lg" />
+      </ErrorPanel>
+    );
+  }
+
+  const chartOptions = {
+    grid: {
+      left: '10px',
+      right: '10px',
+      top: '40px',
+      bottom: '0px',
+    },
+    seriesOptions: {
+      showSymbol: false,
+    },
+    tooltip: {
+      trigger: 'axis' as const,
+      valueFormatter: tooltipFormatter,
+    },
+    xAxis: timeFrame
+      ? {
+          min: timeFrame.start,
+          max: timeFrame.end,
+        }
+      : undefined,
+    yAxis: {
+      axisLabel: {
+        color: theme.chartLabel,
+        // p75(measurements.fcp) coerces the axis to be time based
+        formatter: (value: number) => axisLabelFormatter(value, 'p75(measurements.fcp)'),
+      },
+    },
+  };
+
+  const colors = (data && theme.charts.getColorPalette(data.length - 2)) || [];
+
+  // Create a list of series based on the order of the fields,
+  const series = data
+    ? data.map((values, i: number) => ({
+        ...values,
+        color: colors[i],
+      }))
+    : [];
+
+  return (
+    <ChartZoom router={router} period={period} start={start} end={end} utc={utc}>
+      {zoomRenderProps => (
+        <ReleaseSeries
+          start={start}
+          end={end}
+          queryExtra={{
+            ...queryExtra,
+            showTransactions: TransactionsListOption.SLOW_LCP,
+          }}
+          period={period}
+          utc={utc}
+          projects={projects}
+          environments={environments}
+        >
+          {({releaseSeries}) => (
+            <TransitionChart loading={loading} reloading={reloading}>
+              <TransparentLoadingMask visible={reloading} />
+              {getDynamicText({
+                value: (
+                  <LineChart
+                    {...zoomRenderProps}
+                    {...chartOptions}
+                    legend={legend}
+                    onLegendSelectChanged={onLegendSelectChanged}
+                    series={[...series, ...releaseSeries]}
+                  />
+                ),
+                fixed: <Placeholder height="200px" testId="skeleton-ui" />,
+              })}
+            </TransitionChart>
+          )}
+        </ReleaseSeries>
+      )}
+    </ChartZoom>
+  );
+}
+
+export default Content;

Some files were not shown because too many files changed in this diff