Просмотр исходного кода

feat(mep): Make some changes to landing for e2e (#36970)

* feat(mep): Make some changes to landing for e2e

This splits some of the UX between transaction-name-only-search and performance-use-metrics, leaving the rename of those flags / generation to a new more sensible flag name for later.
Kev 2 лет назад
Родитель
Сommit
b9b8a530b9

+ 9 - 19
static/app/utils/performance/contexts/metricsEnhancedPerformanceDataContext.tsx

@@ -1,12 +1,11 @@
 import {ReactNode, useCallback, useState} from 'react';
 
 import Tag from 'sentry/components/tag';
-import {t} from 'sentry/locale';
 import useOrganization from 'sentry/utils/useOrganization';
 import {WIDGET_MAP_DENY_LIST} from 'sentry/views/performance/landing/widgets/utils';
 import {PerformanceWidgetSetting} from 'sentry/views/performance/landing/widgets/widgetDefinitions';
 
-import {AutoSampleState, MEPState, useMEPSettingContext} from './metricsEnhancedSetting';
+import {AutoSampleState, useMEPSettingContext} from './metricsEnhancedSetting';
 import {createDefinedContext} from './utils';
 
 interface MetricsEnhancedPerformanceDataContext {
@@ -42,7 +41,7 @@ export const MEPDataProvider = ({
       }
       _setIsMetricsData(value);
     },
-    [setAutoSampleState, _setIsMetricsData]
+    [setAutoSampleState, _setIsMetricsData, chartSetting]
   );
 
   return (
@@ -56,27 +55,18 @@ export const useMEPDataContext = _useMEPDataContext;
 
 export const MEPTag = () => {
   const {isMetricsData} = useMEPDataContext();
-  const {metricSettingState} = useMEPSettingContext();
   const organization = useOrganization();
 
   if (!organization.features.includes('performance-use-metrics')) {
     // Separate if for easier flag deletion
     return null;
   }
-  if (organization.features.includes('transaction-name-only-search')) {
-    return null;
-  }
-  if (metricSettingState === MEPState.auto && isMetricsData === false) {
-    return (
-      <Tag
-        tooltipText={t(
-          'These search conditions are only applicable to sampled transaction data. To edit sampling rates, go to Filters & Sampling in settings.'
-        )}
-        data-test-id="has-metrics-data-tag"
-      >
-        {'Sampled'}
-      </Tag>
-    );
+
+  if (isMetricsData === undefined) {
+    return <span data-test-id="no-metrics-data-tag" />;
   }
-  return <span data-test-id="no-metrics-data-tag" />;
+
+  const tagText = isMetricsData ? 'metrics' : 'transactions';
+
+  return <Tag data-test-id="has-metrics-data-tag">{tagText}</Tag>;
 };

+ 51 - 25
static/app/utils/performance/contexts/metricsEnhancedSetting.tsx

@@ -1,19 +1,21 @@
-import {Dispatch, ReactNode, useReducer} from 'react';
+import {Dispatch, ReactNode, useCallback, useReducer} from 'react';
+import {browserHistory} from 'react-router';
+import {Location} from 'history';
 
 import localStorage from 'sentry/utils/localStorage';
+import {decodeScalar} from 'sentry/utils/queryString';
 import useOrganization from 'sentry/utils/useOrganization';
 
 import {createDefinedContext} from './utils';
 
 export interface MetricsEnhancedSettingContext {
   autoSampleState: AutoSampleState;
-  hideSinceMetricsOnly: boolean;
   memoizationKey: string;
   metricSettingState: MEPState | null;
   setAutoSampleState: Dispatch<AutoSampleState>;
   setMetricSettingState: Dispatch<MEPState>;
+  shouldQueryProvideMEPAutoParams: boolean;
   shouldQueryProvideMEPMetricParams: boolean;
-  shouldQueryProvideMEPParams: boolean;
   shouldQueryProvideMEPTransactionParams: boolean;
 }
 
@@ -43,6 +45,8 @@ export enum MEPState {
   transactionsOnly = 'transactionsOnly',
 }
 
+const METRIC_SETTING_PARAM = 'metricSetting';
+
 const storageKey = 'performance.metrics-enhanced-setting';
 export class MEPSetting {
   static get(): MEPState | null {
@@ -64,43 +68,66 @@ export class MEPSetting {
 
 export const MEPSettingProvider = ({
   children,
+  location,
   _hasMEPState,
-  forceMetricsOnly,
 }: {
   children: ReactNode;
   _hasMEPState?: MEPState;
-  forceMetricsOnly?: boolean;
+  location?: Location;
 }) => {
   const organization = useOrganization();
+
   const canUseMEP =
-    organization.features.includes('performance-use-metrics') || !!forceMetricsOnly;
+    organization.features.includes('performance-use-metrics') ||
+    organization.features.includes('performance-transaction-name-only-search');
+  const shouldDefaultToMetrics = organization.features.includes(
+    'performance-transaction-name-only-search'
+  );
+
+  const allowedStates = [MEPState.auto, MEPState.metricsOnly, MEPState.transactionsOnly];
+  const _metricSettingFromParam = location
+    ? decodeScalar(location.query[METRIC_SETTING_PARAM])
+    : MEPState.auto;
+  const defaultMetricsState = shouldDefaultToMetrics
+    ? MEPState.metricsOnly
+    : MEPState.auto;
 
-  const isControlledMEP = typeof _hasMEPState !== 'undefined' || !!forceMetricsOnly;
+  const metricSettingFromParam =
+    allowedStates.find(s => s === _metricSettingFromParam) ?? defaultMetricsState;
 
-  const [_metricSettingState, setMetricSettingState] = useReducer(
+  const isControlledMEP = typeof _hasMEPState !== 'undefined';
+
+  const [_metricSettingState, _setMetricSettingState] = useReducer(
     (_: MEPState, next: MEPState) => next,
-    MEPState.auto
+    metricSettingFromParam
   );
+
+  const setMetricSettingState = useCallback(
+    (settingState: MEPState) => {
+      if (!location) {
+        return;
+      }
+      browserHistory.replace({
+        ...location,
+        query: {
+          ...location.query,
+          [METRIC_SETTING_PARAM]: settingState,
+        },
+      });
+      _setMetricSettingState(settingState);
+    },
+    [location, _setMetricSettingState]
+  );
+
   const [autoSampleState, setAutoSampleState] = useReducer(
     (_: AutoSampleState, next: AutoSampleState) => next,
     AutoSampleState.unset
   );
 
-  let metricSettingState = _metricSettingState;
-
-  if (isControlledMEP) {
-    if (forceMetricsOnly) {
-      metricSettingState = MEPState.metricsOnly;
-    }
-    if (_hasMEPState) {
-      metricSettingState = _hasMEPState;
-    }
-  }
+  const metricSettingState = isControlledMEP ? _hasMEPState : _metricSettingState;
 
-  const hideSinceMetricsOnly =
-    canUseMEP &&
-    (metricSettingState === MEPState.metricsOnly || metricSettingState === MEPState.auto); // TODO(k-fish): Change this so auto includes data state.
-  const shouldQueryProvideMEPParams = canUseMEP && metricSettingState === MEPState.auto;
+  const shouldQueryProvideMEPAutoParams =
+    canUseMEP && metricSettingState === MEPState.auto;
   const shouldQueryProvideMEPMetricParams =
     canUseMEP && metricSettingState === MEPState.metricsOnly;
   const shouldQueryProvideMEPTransactionParams =
@@ -113,8 +140,7 @@ export const MEPSettingProvider = ({
       value={{
         autoSampleState,
         metricSettingState,
-        hideSinceMetricsOnly,
-        shouldQueryProvideMEPParams,
+        shouldQueryProvideMEPAutoParams,
         shouldQueryProvideMEPMetricParams,
         shouldQueryProvideMEPTransactionParams,
         memoizationKey,

+ 1 - 5
static/app/views/performance/content.tsx

@@ -145,14 +145,10 @@ function PerformanceContent({selection, location, demoMode}: Props) {
     });
   }
 
-  const forceMetricsOnly = organization.features.includes(
-    'performance-transaction-name-only-search'
-  );
-
   return (
     <SentryDocumentTitle title={t('Performance')} orgSlug={organization.slug}>
       <PerformanceEventViewProvider value={{eventView}}>
-        <MEPSettingProvider forceMetricsOnly={forceMetricsOnly}>
+        <MEPSettingProvider location={location}>
           <PageFiltersContainer
             defaultSelection={{
               datetime: {

+ 4 - 10
static/app/views/performance/landing/index.tsx

@@ -153,11 +153,9 @@ export function PerformanceLanding(props: Props) {
     pageFilters = <SearchContainerWithFilter>{pageFilters}</SearchContainerWithFilter>;
   }
 
-  const SearchFilterContainer =
-    organization.features.includes('performance-use-metrics') &&
-    !organization.features.includes('performance-transaction-name-only-search')
-      ? SearchContainerWithFilterAndMetrics
-      : SearchContainerWithFilter;
+  const SearchFilterContainer = organization.features.includes('performance-use-metrics')
+    ? SearchContainerWithFilterAndMetrics
+    : SearchContainerWithFilter;
 
   return (
     <StyledPageContent data-test-id="performance-landing-v3">
@@ -254,11 +252,7 @@ export function PerformanceLanding(props: Props) {
                       )
                     }
                   </Feature>
-                  <Feature
-                    features={['organizations:performance-transaction-name-only-search']}
-                  >
-                    {({hasFeature}) => !hasFeature && <MetricsEventsDropdown />}
-                  </Feature>
+                  <MetricsEventsDropdown />
                 </SearchFilterContainer>
                 {initiallyLoaded ? (
                   <TeamKeyTransactionManager.Provider

+ 2 - 1
static/app/views/performance/landing/widgets/components/queryHandler.tsx

@@ -96,7 +96,8 @@ function QueryResultSaver<T extends WidgetDataConstraint>(
 
   useEffect(() => {
     const isMetricsData =
-      results?.seriesAdditionalInfo?.[props.queryProps.fields[0]]?.isMetricsData;
+      results?.seriesAdditionalInfo?.[props.queryProps.fields[0]]?.isMetricsData ??
+      results?.histograms?.meta?.isMetricsData;
     mepContext.setIsMetricsData(isMetricsData);
     props.setWidgetDataForKey(query.queryKey, transformed);
   }, [transformed?.hasData, transformed?.isLoading, transformed?.isErrored]);

+ 17 - 2
static/app/views/performance/landing/widgets/transforms/transformDiscoverToList.tsx

@@ -1,11 +1,25 @@
 import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
 import {defined} from 'sentry/utils';
-import {TableData} from 'sentry/utils/discover/discoverQuery';
+import {TableData, TableDataRow} from 'sentry/utils/discover/discoverQuery';
 import {GenericChildrenProps} from 'sentry/utils/discover/genericDiscoverQuery';
 import {DEFAULT_STATS_PERIOD} from 'sentry/views/performance/data';
 
 import {QueryDefinitionWithKey, WidgetDataConstraint, WidgetPropUnion} from '../types';
 
+/**
+ * Cleans up lists to remove 'null' transactions rows from metrics-backed data.
+ */
+function removeEmptyTransactionsFromList(data: TableDataRow[]) {
+  const transactionColumnExists = data.some(
+    d => typeof d === 'object' && 'transaction' in d
+  );
+  return transactionColumnExists
+    ? data.filter(d =>
+        typeof d === 'object' && 'transaction' in d ? d.transaction : true
+      )
+    : data;
+}
+
 export function transformDiscoverToList<T extends WidgetDataConstraint>(
   widgetProps: WidgetPropUnion<T>,
   results: GenericChildrenProps<TableData>,
@@ -18,7 +32,8 @@ export function transformDiscoverToList<T extends WidgetDataConstraint>(
     }
   );
 
-  const data = results.tableData?.data ?? [];
+  const _data = results.tableData?.data ?? [];
+  const data = removeEmptyTransactionsFromList(_data);
 
   const childData = {
     ...results,

+ 1 - 1
static/app/views/performance/landing/widgets/utils.tsx

@@ -25,7 +25,7 @@ function setWidgetStorageObject(localObject: Record<string, string>) {
 export function getMEPQueryParams(mepContext: MetricsEnhancedSettingContext) {
   let queryParams = {};
   const base = {preventMetricAggregates: '1'};
-  if (mepContext.shouldQueryProvideMEPParams) {
+  if (mepContext.shouldQueryProvideMEPAutoParams) {
     queryParams = {
       ...queryParams,
       ...base,

+ 1 - 1
static/app/views/performance/landing/widgets/widgets/lineChartListWidget.tsx

@@ -230,7 +230,7 @@ export function LineChartListWidget(props: PerformanceWidgetProps) {
               selectedIndex={selectedListIndex}
               setSelectedIndex={setSelectListIndex}
               items={provided.widgetData.list.data.map(listItem => () => {
-                const transaction = listItem.transaction as string;
+                const transaction = (listItem.transaction as string | undefined) ?? '';
 
                 const additionalQuery: Record<string, string> = {};
 

+ 10 - 3
static/app/views/performance/landing/widgets/widgets/vitalWidget.tsx

@@ -113,6 +113,13 @@ export function VitalWidget(props: PerformanceWidgetProps) {
           }));
 
           _eventView.sorts = [{kind: 'desc', field: sortField}];
+          if (
+            props.organization.features.includes(
+              'performance-transaction-name-only-search'
+            )
+          ) {
+            _eventView.additionalConditions.setFilterValues('!transaction', ['']);
+          }
 
           _eventView.fields = [
             {field: 'transaction'},
@@ -127,7 +134,7 @@ export function VitalWidget(props: PerformanceWidgetProps) {
               {...provided}
               eventView={_eventView}
               location={props.location}
-              limit={3}
+              limit={4}
               cursor="0:0:1"
               noPagination
               queryExtras={getMEPQueryParams(mepSetting)}
@@ -272,8 +279,8 @@ export function VitalWidget(props: PerformanceWidgetProps) {
             <SelectableList
               selectedIndex={selectedListIndex}
               setSelectedIndex={setSelectListIndex}
-              items={provided.widgetData.list.data.map(listItem => () => {
-                const transaction = listItem?.transaction as string;
+              items={provided.widgetData.list.data.slice(0, 3).map(listItem => () => {
+                const transaction = (listItem?.transaction as string | undefined) ?? '';
                 const _eventView = eventView.clone();
 
                 const initialConditions = new MutableSearch(_eventView.query);

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

@@ -54,7 +54,6 @@ import {
   TransactionFilterOptions,
 } from '../utils';
 
-import {MetricsEventsDropdown} from './metricEvents/metricsEventsDropdown';
 import TransactionSummaryCharts from './charts';
 import RelatedIssues from './relatedIssues';
 import SidebarCharts from './sidebarCharts';
@@ -296,7 +295,6 @@ function SummaryContent({
             onSearch={handleSearch}
             maxQueryLength={MAX_QUERY_LENGTH}
           />
-          <MetricsEventsDropdown />
         </FilterActions>
         <TransactionSummaryCharts
           organization={organization}

Некоторые файлы не были показаны из-за большого количества измененных файлов