Browse Source

chore(dashboards): remove custom metrics from widget builder (#65156)

Ogi 1 year ago
parent
commit
e4f0a04990

+ 57 - 20
static/app/components/modals/widgetBuilder/addToDashboardModal.tsx

@@ -51,6 +51,11 @@ type WidgetAsQueryParams = Query<{
   statsPeriod?: string | null;
 }>;
 
+type AddToDashboardModalActions =
+  | 'add-and-open-dashboard'
+  | 'add-and-stay-on-current-page'
+  | 'open-in-widget-builder';
+
 export type AddToDashboardModalProps = {
   location: Location;
   organization: Organization;
@@ -58,12 +63,18 @@ export type AddToDashboardModalProps = {
   selection: PageFilters;
   widget: Widget;
   widgetAsQueryParams: WidgetAsQueryParams;
+  actions?: AddToDashboardModalActions[];
 };
 
 type Props = ModalRenderProps & AddToDashboardModalProps;
 
 const SELECT_DASHBOARD_MESSAGE = t('Select a dashboard');
 
+const DEFAULT_ACTIONS: AddToDashboardModalActions[] = [
+  'add-and-stay-on-current-page',
+  'open-in-widget-builder',
+];
+
 function AddToDashboardModal({
   Header,
   Body,
@@ -75,6 +86,7 @@ function AddToDashboardModal({
   selection,
   widget,
   widgetAsQueryParams,
+  actions = DEFAULT_ACTIONS,
 }: Props) {
   const api = useApi();
   const [dashboards, setDashboards] = useState<DashboardListItem[] | null>(null);
@@ -123,11 +135,13 @@ function AddToDashboardModal({
     };
   }, [api, organization.slug, selectedDashboardId]);
 
-  function handleGoToBuilder() {
-    const pathname =
+  function goToDashboard(page: 'builder' | 'preview') {
+    const dashboardsPath =
       selectedDashboardId === NEW_DASHBOARD_ID
-        ? `/organizations/${organization.slug}/dashboards/new/widget/new/`
-        : `/organizations/${organization.slug}/dashboard/${selectedDashboardId}/widget/new/`;
+        ? `/organizations/${organization.slug}/dashboards/new/`
+        : `/organizations/${organization.slug}/dashboard/${selectedDashboardId}/`;
+
+    const pathname = page === 'builder' ? `${dashboardsPath}widget/new/` : dashboardsPath;
 
     router.push(
       normalizeUrl({
@@ -141,7 +155,7 @@ function AddToDashboardModal({
     closeModal();
   }
 
-  async function handleAddAndStayOnCurrentPage() {
+  async function handleAddWidget() {
     if (selectedDashboard === null) {
       return;
     }
@@ -164,11 +178,21 @@ function AddToDashboardModal({
     };
 
     await updateDashboard(api, organization.slug, newDashboard);
+  }
+
+  async function handleAddAndStayOnCurrentPage() {
+    await handleAddWidget();
 
     closeModal();
     addSuccessMessage(t('Successfully added widget to dashboard'));
   }
 
+  async function handleAddAndOpenDaashboard() {
+    await handleAddWidget();
+
+    goToDashboard('preview');
+  }
+
   const canSubmit = selectedDashboardId !== null;
 
   return (
@@ -248,21 +272,34 @@ function AddToDashboardModal({
 
       <Footer>
         <StyledButtonBar gap={1.5}>
-          <Button
-            onClick={handleAddAndStayOnCurrentPage}
-            disabled={!canSubmit || selectedDashboardId === NEW_DASHBOARD_ID}
-            title={canSubmit ? undefined : SELECT_DASHBOARD_MESSAGE}
-          >
-            {t('Add + Stay on this Page')}
-          </Button>
-          <Button
-            priority="primary"
-            onClick={handleGoToBuilder}
-            disabled={!canSubmit}
-            title={canSubmit ? undefined : SELECT_DASHBOARD_MESSAGE}
-          >
-            {t('Open in Widget Builder')}
-          </Button>
+          {actions.includes('add-and-stay-on-current-page') && (
+            <Button
+              onClick={handleAddAndStayOnCurrentPage}
+              disabled={!canSubmit || selectedDashboardId === NEW_DASHBOARD_ID}
+              title={canSubmit ? undefined : SELECT_DASHBOARD_MESSAGE}
+            >
+              {t('Add + Stay on this Page')}
+            </Button>
+          )}
+          {actions.includes('add-and-open-dashboard') && (
+            <Button
+              onClick={handleAddAndOpenDaashboard}
+              disabled={!canSubmit || selectedDashboardId === NEW_DASHBOARD_ID}
+              title={canSubmit ? undefined : SELECT_DASHBOARD_MESSAGE}
+            >
+              {t('Add + Open Dashboard')}
+            </Button>
+          )}
+          {actions.includes('open-in-widget-builder') && (
+            <Button
+              priority="primary"
+              onClick={() => goToDashboard('builder')}
+              disabled={!canSubmit}
+              title={canSubmit ? undefined : SELECT_DASHBOARD_MESSAGE}
+            >
+              {t('Open in Widget Builder')}
+            </Button>
+          )}
         </StyledButtonBar>
       </Footer>
     </OrganizationContext.Provider>

+ 0 - 100
static/app/components/modals/widgetViewerModal.tsx

@@ -25,7 +25,6 @@ import Pagination from 'sentry/components/pagination';
 import QuestionTooltip from 'sentry/components/questionTooltip';
 import {parseSearch} from 'sentry/components/searchSyntax/parser';
 import HighlightQuery from 'sentry/components/searchSyntax/renderer';
-import {TabList, TabPanels, Tabs} from 'sentry/components/tabs';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import type {Organization, PageFilters, SelectValue} from 'sentry/types';
@@ -42,7 +41,6 @@ import {
   isEquationAlias,
 } from 'sentry/utils/discover/fields';
 import {hasDDMFeature} from 'sentry/utils/metrics/features';
-import {parseField, parseMRI} from 'sentry/utils/metrics/mri';
 import {createOnDemandFilterWarning} from 'sentry/utils/onDemandMetrics';
 import {hasOnDemandMetricWidgetFeature} from 'sentry/utils/onDemandMetrics/features';
 import parseLinkHeader from 'sentry/utils/parseLinkHeader';
@@ -81,12 +79,9 @@ import {
 import type {GenericWidgetQueriesChildrenProps} from 'sentry/views/dashboards/widgetCard/genericWidgetQueries';
 import IssueWidgetQueries from 'sentry/views/dashboards/widgetCard/issueWidgetQueries';
 import {MetricWidgetChartContainer} from 'sentry/views/dashboards/widgetCard/metricWidgetCard';
-import MetricWidgetQueries from 'sentry/views/dashboards/widgetCard/metricWidgetQueries';
 import ReleaseWidgetQueries from 'sentry/views/dashboards/widgetCard/releaseWidgetQueries';
 import {WidgetCardChartContainer} from 'sentry/views/dashboards/widgetCard/widgetCardChartContainer';
 import WidgetQueries from 'sentry/views/dashboards/widgetCard/widgetQueries';
-import {CodeLocations} from 'sentry/views/ddm/codeLocations';
-import {SampleTable} from 'sentry/views/ddm/sampleTable';
 import {decodeColumnOrder} from 'sentry/views/discover/utils';
 import {OrganizationContext} from 'sentry/views/organizationContext';
 import {MetricsDataSwitcher} from 'sentry/views/performance/landing/metricsDataSwitcher';
@@ -98,7 +93,6 @@ import {
   renderDiscoverGridHeaderCell,
   renderGridBodyCell,
   renderIssueGridHeaderCell,
-  renderMetricGridHeaderCell,
   renderReleaseGridHeaderCell,
 } from './widgetViewerModal/widgetViewerTableCell';
 
@@ -691,70 +685,6 @@ function WidgetViewerModal(props: Props) {
     );
   };
 
-  const renderMetricsTable: MetricWidgetQueries['props']['children'] = ({
-    tableResults,
-    loading,
-    pageLinks,
-  }) => {
-    const links = parseLinkHeader(pageLinks ?? null);
-    const isFirstPage = links.previous?.results === false;
-    const data = tableResults?.[0]?.data ?? [];
-
-    const mainField = props.widget.queries[0].aggregates[0];
-    const parsedField = parseField(mainField);
-    if (!parsedField) {
-      return null;
-    }
-
-    const {useCase} = parseMRI(parsedField.mri)!;
-
-    return (
-      <Fragment>
-        <Tabs>
-          <TabList>
-            <TabList.Item key="samples">{t('Samples')}</TabList.Item>
-            <TabList.Item hidden={useCase !== 'custom'} key="codeLocation">
-              {t('Code Location')}
-            </TabList.Item>
-            <TabList.Item key="summary">{t('Summary')}</TabList.Item>
-          </TabList>
-          <MetricWidgetTabContent>
-            <TabPanels>
-              <TabPanels.Item key="samples">
-                <SampleTable mri={parsedField.mri} query={widget.queries[0].conditions} />
-              </TabPanels.Item>
-              <TabPanels.Item key="codeLocation">
-                <CodeLocations mri={parsedField.mri} />
-              </TabPanels.Item>
-              <TabPanels.Item key="summary">
-                <GridEditable
-                  isLoading={loading}
-                  data={data}
-                  columnOrder={columnOrder}
-                  columnSortBy={columnSortBy}
-                  grid={{
-                    renderHeadCell: renderMetricGridHeaderCell() as (
-                      column: GridColumnOrder,
-                      columnIndex: number
-                    ) => React.ReactNode,
-                    renderBodyCell: renderGridBodyCell({
-                      ...props,
-                      location,
-                      tableData: tableResults?.[0],
-                      isFirstPage,
-                    }),
-                    onResizeColumn,
-                  }}
-                  location={location}
-                />
-              </TabPanels.Item>
-            </TabPanels>
-          </MetricWidgetTabContent>
-        </Tabs>
-      </Fragment>
-    );
-  };
-
   const onZoom: AugmentedEChartDataZoomHandler = (evt, chart) => {
     // @ts-expect-error getModel() is private but we need this to retrieve datetime values of zoomed in region
     const model = chart.getModel();
@@ -856,31 +786,6 @@ function WidgetViewerModal(props: Props) {
             {renderReleaseTable}
           </ReleaseWidgetQueries>
         );
-      case WidgetType.METRICS:
-        if (tableData && chartUnmodified && widget.displayType === DisplayType.TABLE) {
-          return renderMetricsTable({
-            tableResults: tableData,
-            loading: false,
-            pageLinks: defaultPageLinks,
-          });
-        }
-        return (
-          <MetricWidgetQueries
-            api={api}
-            organization={organization}
-            widget={tableWidget}
-            selection={modalTableSelection}
-            limit={
-              widget.displayType === DisplayType.TABLE
-                ? FULL_TABLE_ITEM_LIMIT
-                : HALF_TABLE_ITEM_LIMIT
-            }
-            cursor={cursor}
-            dashboardFilters={dashboardFilters}
-          >
-            {renderMetricsTable}
-          </MetricWidgetQueries>
-        );
       case WidgetType.DISCOVER:
       default:
         if (tableData && chartUnmodified && widget.displayType === DisplayType.TABLE) {
@@ -1333,9 +1238,4 @@ const WidgetTitleRow = styled('div')`
   gap: ${space(0.75)};
 `;
 
-const MetricWidgetTabContent = styled('div')`
-  position: relative;
-  padding-top: ${space(2)};
-`;
-
 export default withPageFilters(WidgetViewerModal);

+ 0 - 3
static/app/components/modals/widgetViewerModal/widgetViewerTableCell.tsx

@@ -32,7 +32,6 @@ import {
   generateEventSlug,
 } from 'sentry/utils/discover/urls';
 import {formatMRIField, parseField} from 'sentry/utils/metrics/mri';
-import {renderMetricField} from 'sentry/views/dashboards/datasetConfig/metrics';
 import type {Widget} from 'sentry/views/dashboards/types';
 import {DisplayType, WidgetType} from 'sentry/views/dashboards/types';
 import {eventViewFromWidget} from 'sentry/views/dashboards/utils';
@@ -200,8 +199,6 @@ export const renderGridBodyCell = ({
           getIssueFieldRenderer(columnKey) ?? getFieldRenderer(columnKey, ISSUE_FIELDS)
         )(dataRow, {organization, location});
         break;
-      case WidgetType.METRICS:
-        return renderMetricField(columnKey, dataRow[column.key]);
       case WidgetType.DISCOVER:
       default:
         if (!tableData || !tableData.meta) {

+ 2 - 11
static/app/views/dashboards/datasetConfig/base.tsx

@@ -21,7 +21,6 @@ import {getNumEquations} from '../utils';
 
 import {ErrorsAndTransactionsConfig} from './errorsAndTransactions';
 import {IssuesConfig} from './issues';
-import {MetricsConfig} from './metrics';
 import {ReleasesConfig} from './releases';
 
 export type WidgetBuilderSearchBarProps = {
@@ -216,24 +215,16 @@ export function getDatasetConfig<T extends WidgetType | undefined>(
   ? typeof IssuesConfig
   : T extends WidgetType.RELEASE
     ? typeof ReleasesConfig
-    : T extends WidgetType.METRICS
-      ? typeof MetricsConfig
-      : typeof ErrorsAndTransactionsConfig;
+    : typeof ErrorsAndTransactionsConfig;
 
 export function getDatasetConfig(
   widgetType?: WidgetType
-):
-  | typeof IssuesConfig
-  | typeof ReleasesConfig
-  | typeof MetricsConfig
-  | typeof ErrorsAndTransactionsConfig {
+): typeof IssuesConfig | typeof ReleasesConfig | typeof ErrorsAndTransactionsConfig {
   switch (widgetType) {
     case WidgetType.ISSUE:
       return IssuesConfig;
     case WidgetType.RELEASE:
       return ReleasesConfig;
-    case WidgetType.METRICS:
-      return MetricsConfig;
     case WidgetType.DISCOVER:
     default:
       return ErrorsAndTransactionsConfig;

+ 0 - 432
static/app/views/dashboards/datasetConfig/metrics.tsx

@@ -1,432 +0,0 @@
-import omit from 'lodash/omit';
-
-import type {Client, ResponseMeta} from 'sentry/api';
-import {t} from 'sentry/locale';
-import type {
-  MetricsQueryApiResponse,
-  Organization,
-  PageFilters,
-  TagCollection,
-} from 'sentry/types';
-import type {Series} from 'sentry/types/echarts';
-import type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
-import type {TableData} from 'sentry/utils/discover/discoverQuery';
-import type {EventData} from 'sentry/utils/discover/eventView';
-import {NumberContainer} from 'sentry/utils/discover/styles';
-import {getMetricsSeriesName, groupByOp} from 'sentry/utils/metrics';
-import {formatMetricUsingUnit} from 'sentry/utils/metrics/formatters';
-import {
-  formatMRIField,
-  getMRI,
-  getUseCaseFromMRI,
-  isMRIField,
-  parseField,
-  parseMRI,
-} from 'sentry/utils/metrics/mri';
-import {getMetricsQueryApiRequestPayload} from 'sentry/utils/metrics/useMetricsQuery';
-import type {OnDemandControlContext} from 'sentry/utils/performance/contexts/onDemandControl';
-import {MetricSearchBar} from 'sentry/views/dashboards/widgetBuilder/buildSteps/filterResultsStep/metricSearchBar';
-import type {FieldValueOption} from 'sentry/views/discover/table/queryField';
-import {FieldValueKind} from 'sentry/views/discover/table/types';
-
-import type {Widget, WidgetQuery} from '../types';
-import {DisplayType} from '../types';
-
-import type {DatasetConfig} from './base';
-import {handleOrderByReset} from './base';
-
-const DEFAULT_WIDGET_QUERY: WidgetQuery = {
-  name: '',
-  fields: [''],
-  columns: [''],
-  fieldAliases: [],
-  aggregates: [''],
-  conditions: '',
-  orderby: '',
-};
-
-export const MetricsConfig: DatasetConfig<
-  MetricsQueryApiResponse,
-  MetricsQueryApiResponse
-> = {
-  defaultWidgetQuery: DEFAULT_WIDGET_QUERY,
-  enableEquations: false,
-  getTableRequest: (
-    api: Client,
-    _: Widget,
-    query: WidgetQuery,
-    organization: Organization,
-    pageFilters: PageFilters,
-    __?: OnDemandControlContext,
-    limit?: number
-  ) => getMetricRequest(api, query, organization, pageFilters, limit),
-  getSeriesRequest: getMetricSeriesRequest,
-  getCustomFieldRenderer: field => (data: EventData) =>
-    renderMetricField(field, data[field]),
-  SearchBar: MetricSearchBar,
-  handleOrderByReset: handleMetricTableOrderByReset,
-  supportedDisplayTypes: [
-    DisplayType.AREA,
-    DisplayType.BAR,
-    DisplayType.BIG_NUMBER,
-    DisplayType.LINE,
-    DisplayType.TABLE,
-    DisplayType.TOP_N,
-  ],
-  transformSeries: transformMetricsResponseToSeries,
-  transformTable: transformMetricsResponseToTable,
-  getTableFieldOptions: getFields,
-  getTimeseriesSortOptions: getMetricTimeseriesSortOptions,
-  getTableSortOptions: getMetricTableSortOptions,
-  filterTableOptions: filterMetricOperations,
-  filterYAxisOptions: () => option => filterMetricOperations(option),
-  filterAggregateParams: filterMetricMRIs,
-  filterYAxisAggregateParams: () => option => filterMetricMRIs(option),
-  getGroupByFieldOptions: getTagsForMetric,
-  getFieldHeaderMap: getFormattedMRIHeaders,
-};
-
-export function renderMetricField(field: string, value: any) {
-  const parsedField = parseField(field);
-  if (parsedField) {
-    const unit = parseMRI(parsedField.mri)?.unit ?? '';
-    return <NumberContainer>{formatMetricUsingUnit(value, unit)}</NumberContainer>;
-  }
-  return value;
-}
-
-export function formatMetricAxisValue(field: string, value: number) {
-  const unit = parseMRI(parseField(field)?.mri)?.unit ?? '';
-  return formatMetricUsingUnit(value, unit);
-}
-
-function getFormattedMRIHeaders(query?: WidgetQuery) {
-  if (!query) {
-    return {};
-  }
-
-  return (query.fields || []).reduce((acc, field, index) => {
-    const fieldAlias = query.fieldAliases?.[index];
-    acc[field] = fieldAlias || formatMRIField(field);
-    return acc;
-  }, {});
-}
-
-function getMetricTimeseriesSortOptions(_, widgetQuery: WidgetQuery) {
-  if (!widgetQuery.fields?.[0]) {
-    return [];
-  }
-
-  return widgetQuery.fields.reduce((acc, field) => {
-    if (!isMRIField(field)) {
-      return acc;
-    }
-    return {
-      ...acc,
-      [`field:${field}`]: {
-        label: formatMRIField(field),
-        value: {
-          kind: FieldValueKind.FIELD,
-          value: field,
-          meta: {
-            name: field,
-            dataType: 'number',
-          },
-        },
-      },
-    };
-  }, {});
-}
-
-function getMetricTableSortOptions(_, widgetQuery: WidgetQuery) {
-  if (!widgetQuery.fields?.[0]) {
-    return [];
-  }
-
-  return widgetQuery.fields
-    .map((field, i) => {
-      const alias = widgetQuery.fieldAliases?.[i];
-
-      return {
-        label: alias || formatMRIField(field),
-        value: field,
-      };
-    })
-    .filter(option => isMRIField(option.value));
-}
-
-function getFields(
-  organization: Organization,
-  _?: TagCollection | undefined,
-  __?: CustomMeasurementCollection,
-  api?: Client
-) {
-  if (!api) {
-    return {};
-  }
-
-  return api
-    .requestPromise(`/organizations/${organization.slug}/metrics/meta/`, {
-      query: {useCase: 'custom'},
-    })
-    .then(metaReponse => {
-      const groupedByOp = groupByOp(metaReponse);
-      const typesByOp: Record<string, Set<string>> = Object.entries(groupedByOp).reduce(
-        (acc, [operation, fields]) => {
-          const types = new Set();
-          fields.forEach(field => types.add(field.type));
-          acc[operation] = types;
-          return acc;
-        },
-        {}
-      );
-
-      const fieldOptions: Record<string, any> = {};
-      Object.entries(groupedByOp).forEach(([operation, fields]) => {
-        fieldOptions[`function:${operation}`] = {
-          label: `${operation}(${'\u2026'})`,
-          value: {
-            kind: FieldValueKind.FUNCTION,
-            meta: {
-              name: operation,
-              parameters: [
-                {
-                  kind: 'column',
-                  columnTypes: [...typesByOp[operation]],
-                  defaultValue: fields[0].mri,
-                  required: true,
-                },
-              ],
-            },
-          },
-        };
-      });
-
-      metaReponse
-        .sort((a, b) => a.name.localeCompare(b.name))
-        .forEach(field => {
-          fieldOptions[`field:${field.mri}`] = {
-            label: field.name,
-            value: {
-              kind: FieldValueKind.METRICS,
-              meta: {
-                name: field.mri,
-                dataType: field.type,
-              },
-            },
-          };
-        });
-
-      return fieldOptions;
-    });
-}
-
-function filterMetricOperations(option: FieldValueOption) {
-  return option.value.kind === FieldValueKind.FUNCTION;
-}
-
-function filterMetricMRIs(option: FieldValueOption) {
-  return option.value.kind === FieldValueKind.METRICS;
-}
-
-function getTagsForMetric(
-  organization: Organization,
-  _?: TagCollection,
-  __?: CustomMeasurementCollection,
-  api?: Client,
-  queries?: WidgetQuery[]
-) {
-  const fieldOptions = {};
-
-  if (!api) {
-    return fieldOptions;
-  }
-  const field = queries?.[0].aggregates[0] ?? '';
-  const mri = getMRI(field);
-  const useCase = getUseCaseFromMRI(mri);
-
-  return api
-    .requestPromise(`/organizations/${organization.slug}/metrics/tags/`, {
-      query: {metric: mri, useCase},
-    })
-    .then(tagsResponse => {
-      tagsResponse.forEach(tag => {
-        fieldOptions[`field:${tag.key}`] = {
-          label: tag.key,
-          value: {
-            kind: FieldValueKind.TAG,
-            meta: {name: tag.key, dataType: 'string'},
-          },
-        };
-      });
-      return fieldOptions;
-    });
-}
-
-function getMetricSeriesRequest(
-  api: Client,
-  widget: Widget,
-  queryIndex: number,
-  organization: Organization,
-  pageFilters: PageFilters
-) {
-  const query = widget.queries[queryIndex];
-  return getMetricRequest(
-    api,
-    query,
-    organization,
-    pageFilters,
-    widget.limit,
-    widget.displayType
-  );
-}
-
-function handleMetricTableOrderByReset(widgetQuery: WidgetQuery, newFields: string[]) {
-  const disableSortBy = widgetQuery.columns.includes('session.status');
-  if (disableSortBy) {
-    widgetQuery.orderby = '';
-  }
-  return handleOrderByReset(widgetQuery, newFields);
-}
-
-export function transformMetricsResponseToTable(
-  data: MetricsQueryApiResponse,
-  widgetQuery: WidgetQuery
-): TableData {
-  const field = widgetQuery.aggregates[0];
-  const rows = data.data.flatMap((group, index) =>
-    group.map(entry => {
-      const groupColumn = mapMetricGroupsToFields(entry.by);
-      const value = {
-        [field]: entry.totals,
-      };
-      return {
-        id: String(index),
-        ...groupColumn,
-        ...value,
-      };
-    })
-  );
-
-  const singleRow = rows[0];
-  const meta = {
-    ...changeObjectValuesToTypes(omit(singleRow, 'id')),
-  };
-  return {meta, data: rows};
-}
-
-function mapMetricGroupsToFields(
-  results: Record<string, number | string | null> | undefined
-) {
-  if (!results) {
-    return {};
-  }
-
-  const mappedResults: typeof results = {};
-  for (const [key, value] of Object.entries(results)) {
-    mappedResults[key] = value;
-  }
-  return mappedResults;
-}
-
-function changeObjectValuesToTypes(
-  obj: Record<string, number | string | null> | undefined
-) {
-  return Object.entries(obj ?? {}).reduce((acc, [key, value]) => {
-    acc[key] = typeof value;
-    return acc;
-  }, {});
-}
-
-export function transformMetricsResponseToSeries(
-  data: MetricsQueryApiResponse,
-  widgetQuery: WidgetQuery
-) {
-  if (data === null) {
-    return [];
-  }
-
-  const field = widgetQuery.aggregates[0];
-
-  const results: Series[] = [];
-  const queryAlias = widgetQuery.name;
-
-  if (!data.data.length) {
-    return [
-      {
-        seriesName: `(${t('no results')})`,
-        data: data.intervals.map(interval => ({
-          name: interval,
-          value: 0,
-        })),
-      },
-    ];
-  }
-
-  data.data.forEach(group =>
-    group.forEach(entry => {
-      results.push({
-        seriesName: getMetricsSeriesName(queryAlias || field, entry.by),
-        data: data.intervals.map((interval, index) => ({
-          name: interval,
-          value: entry.series[field][index] ?? 0,
-        })),
-      });
-    })
-  );
-
-  return results;
-}
-
-function getMetricRequest(
-  api: Client,
-  query: WidgetQuery,
-  organization: Organization,
-  pageFilters: PageFilters,
-  limit?: number,
-  displayType?: DisplayType
-): Promise<[MetricsQueryApiResponse, string | undefined, ResponseMeta | undefined]> {
-  if (!query.aggregates[0]) {
-    // No aggregate selected, return empty response
-    return Promise.resolve([
-      {
-        intervals: [],
-        groups: [],
-        meta: [],
-      },
-      'OK',
-      {
-        getResponseHeader: () => '',
-      },
-    ] as any);
-  }
-
-  const payload = getMetricsQueryApiRequestPayload(
-    [
-      {
-        field: query.aggregates[0],
-        query: query.conditions || undefined,
-        groupBy: query.columns || undefined,
-        orderBy: query.orderby
-          ? query.orderby.indexOf('-') === 0
-            ? 'desc'
-            : 'asc'
-          : undefined,
-        limit: limit || undefined,
-      },
-    ],
-    pageFilters,
-    {
-      intervalLadder: displayType === DisplayType.BAR ? 'bar' : 'dashboard',
-    }
-  );
-
-  const pathname = `/organizations/${organization.slug}/metrics/query/`;
-
-  return api.requestPromise(pathname, {
-    method: 'POST',
-    query: payload.query,
-    data: payload.body,
-    includeAllArgs: true,
-  });
-}

+ 8 - 5
static/app/views/dashboards/widgetBuilder/buildSteps/columnsStep/index.tsx

@@ -2,10 +2,11 @@ import ExternalLink from 'sentry/components/links/externalLink';
 import {t, tct} from 'sentry/locale';
 import type {Organization, TagCollection} from 'sentry/types';
 import type {QueryFieldValue} from 'sentry/utils/discover/fields';
+import useCustomMeasurements from 'sentry/utils/useCustomMeasurements';
 import {getDatasetConfig} from 'sentry/views/dashboards/datasetConfig/base';
 import type {DisplayType, WidgetQuery, WidgetType} from 'sentry/views/dashboards/types';
 
-import {DataSet, useTableFieldOptions} from '../../utils';
+import {DataSet} from '../../utils';
 import {BuildStep} from '../buildStep';
 
 import {ColumnFields} from './columnFields';
@@ -17,7 +18,6 @@ interface Props {
   handleColumnFieldChange: (newFields: QueryFieldValue[]) => void;
   onQueryChange: (queryIndex: number, newQuery: WidgetQuery) => void;
   organization: Organization;
-  queries: WidgetQuery[];
   tags: TagCollection;
   widgetType: WidgetType;
   queryErrors?: Record<string, any>[];
@@ -33,10 +33,9 @@ export function ColumnsStep({
   explodedFields,
   tags,
 }: Props) {
+  const {customMeasurements} = useCustomMeasurements();
   const datasetConfig = getDatasetConfig(widgetType);
 
-  const fieldOptions = useTableFieldOptions(organization, tags, widgetType);
-
   return (
     <BuildStep
       data-test-id="choose-column-step"
@@ -82,7 +81,11 @@ export function ColumnsStep({
         widgetType={widgetType}
         fields={explodedFields}
         errors={queryErrors}
-        fieldOptions={fieldOptions}
+        fieldOptions={datasetConfig.getTableFieldOptions(
+          organization,
+          tags,
+          customMeasurements
+        )}
         filterAggregateParameters={datasetConfig.filterAggregateParams}
         filterPrimaryOptions={datasetConfig.filterTableOptions}
         onChange={handleColumnFieldChange}

+ 0 - 6
static/app/views/dashboards/widgetBuilder/buildSteps/dataSetStep.tsx

@@ -19,7 +19,6 @@ import {BuildStep} from './buildStep';
 interface Props {
   dataSet: DataSet;
   displayType: DisplayType;
-  hasCustomMetricsFeature: boolean;
   hasReleaseHealthFeature: boolean;
   onChange: (dataSet: DataSet) => void;
 }
@@ -28,7 +27,6 @@ export function DataSetStep({
   dataSet,
   onChange,
   hasReleaseHealthFeature,
-  hasCustomMetricsFeature,
   displayType,
 }: Props) {
   const disabledChoices: RadioGroupProps<string>['disabledChoices'] = [];
@@ -48,10 +46,6 @@ export function DataSetStep({
     datasetChoices.set(DataSet.RELEASES, t('Releases (Sessions, Crash rates)'));
   }
 
-  if (hasCustomMetricsFeature) {
-    datasetChoices.set(DataSet.METRICS, t('Custom Metrics'));
-  }
-
   return (
     <BuildStep
       title={t('Choose your dataset')}

+ 6 - 4
static/app/views/dashboards/widgetBuilder/buildSteps/groupByStep/index.tsx

@@ -1,10 +1,10 @@
 import {t} from 'sentry/locale';
 import type {Organization, TagCollection} from 'sentry/types';
 import type {QueryFieldValue} from 'sentry/utils/discover/fields';
+import {getDatasetConfig} from 'sentry/views/dashboards/datasetConfig/base';
 import type {WidgetQuery} from 'sentry/views/dashboards/types';
 
 import type {DataSet} from '../../utils';
-import {useGroupByOptions} from '../../utils';
 import {DATA_SET_TO_WIDGET_TYPE} from '../../widgetBuilder';
 import {BuildStep} from '../buildStep';
 
@@ -25,10 +25,12 @@ export function GroupByStep({
   onGroupByChange,
   organization,
   tags,
-  queries,
 }: Props) {
-  const widgetType = DATA_SET_TO_WIDGET_TYPE[dataSet];
-  const groupByOptions = useGroupByOptions(organization, tags, widgetType, queries);
+  const datasetConfig = getDatasetConfig(DATA_SET_TO_WIDGET_TYPE[dataSet]);
+
+  const groupByOptions = datasetConfig.getGroupByFieldOptions
+    ? datasetConfig.getGroupByFieldOptions(organization, tags)
+    : {};
 
   return (
     <BuildStep

+ 0 - 1
static/app/views/dashboards/widgetBuilder/buildSteps/sortByStep/sortBySelectors.tsx

@@ -142,7 +142,6 @@ export function SortBySelectors({
                 : undefined
             }
             filterAggregateParameters={datasetConfig.filterAggregateParams}
-            placeholder={widgetType === WidgetType.METRICS ? t('(tag)') : undefined}
             onChange={value => {
               if (value.alias && isEquationAlias(value.alias)) {
                 onChange({

+ 9 - 7
static/app/views/dashboards/widgetBuilder/buildSteps/yAxisStep/yAxisSelector/index.tsx

@@ -6,15 +6,14 @@ import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import type {TagCollection} from 'sentry/types';
 import type {QueryFieldValue} from 'sentry/utils/discover/fields';
+import useCustomMeasurements from 'sentry/utils/useCustomMeasurements';
 import useOrganization from 'sentry/utils/useOrganization';
 import {getDatasetConfig} from 'sentry/views/dashboards/datasetConfig/base';
 import type {Widget} from 'sentry/views/dashboards/types';
-import {DisplayType, WidgetType} from 'sentry/views/dashboards/types';
+import {DisplayType} from 'sentry/views/dashboards/types';
 import {QueryField} from 'sentry/views/discover/table/queryField';
 import {FieldValueKind} from 'sentry/views/discover/table/types';
 
-import {useTableFieldOptions} from '../../../utils';
-
 import {AddButton} from './addButton';
 import {DeleteButton} from './deleteButton';
 
@@ -43,7 +42,7 @@ export function YAxisSelector({
   const organization = useOrganization();
   const datasetConfig = getDatasetConfig(widgetType);
 
-  const fieldOptions = useTableFieldOptions(organization, tags, widgetType);
+  const {customMeasurements} = useCustomMeasurements();
 
   function handleAddOverlay(event: React.MouseEvent) {
     event.preventDefault();
@@ -93,7 +92,11 @@ export function YAxisSelector({
         <QueryFieldWrapper key={`${fieldValue}:${i}`}>
           <QueryField
             fieldValue={fieldValue}
-            fieldOptions={fieldOptions}
+            fieldOptions={datasetConfig.getTableFieldOptions(
+              organization,
+              tags,
+              customMeasurements
+            )}
             onChange={value => handleChangeQueryField(value, i)}
             filterPrimaryOptions={datasetConfig.filterYAxisOptions?.(displayType)}
             filterAggregateParameters={datasetConfig.filterYAxisAggregateParams?.(
@@ -110,8 +113,7 @@ export function YAxisSelector({
         </QueryFieldWrapper>
       ))}
 
-      {/* TODO(ddm): support multiple overlays */}
-      {!hideAddYAxisButtons && widgetType !== WidgetType.METRICS && (
+      {!hideAddYAxisButtons && (
         <Actions gap={1}>
           <AddButton title={t('Add Overlay')} onAdd={handleAddOverlay} />
           {datasetConfig.enableEquations && (

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