Browse Source

ref(ddm): Split utils (#63933)

Split metric utils to reduce the dependencies that are being imported
into different parts of the code base.
Added new files for `formatters`, `types` and `constants`.
ArthurKnaus 1 year ago
parent
commit
f882e6e2af

+ 37 - 0
static/app/utils/metrics/constants.tsx

@@ -0,0 +1,37 @@
+import {t} from 'sentry/locale';
+import {MRI} from 'sentry/types';
+import type {MetricWidgetQueryParams, SortState} from 'sentry/utils/metrics/types';
+import {MetricDisplayType} from 'sentry/utils/metrics/types';
+
+export const METRICS_DOCS_URL =
+  'https://develop.sentry.dev/delightful-developer-metrics/';
+
+export const metricDisplayTypeOptions = [
+  {
+    value: MetricDisplayType.LINE,
+    label: t('Line'),
+  },
+  {
+    value: MetricDisplayType.AREA,
+    label: t('Area'),
+  },
+  {
+    value: MetricDisplayType.BAR,
+    label: t('Bar'),
+  },
+];
+
+export const DEFAULT_SORT_STATE: SortState = {
+  name: undefined,
+  order: 'asc',
+};
+
+export const emptyWidget: MetricWidgetQueryParams = {
+  mri: 'd:transactions/duration@millisecond' satisfies MRI,
+  op: 'avg',
+  query: '',
+  groupBy: [],
+  sort: DEFAULT_SORT_STATE,
+  displayType: MetricDisplayType.LINE,
+  title: undefined,
+};

+ 1 - 1
static/app/utils/metrics/dashboard.spec.tsx

@@ -1,11 +1,11 @@
 import {MRI} from 'sentry/types';
-import {MetricDisplayType} from 'sentry/utils/metrics';
 import {
   convertToDashboardWidget,
   getWidgetQuery,
   toDisplayType,
   toMetricDisplayType,
 } from 'sentry/utils/metrics/dashboard';
+import {MetricDisplayType} from 'sentry/utils/metrics/types';
 import {DisplayType} from 'sentry/views/dashboards/types';
 
 describe('convertToDashboardWidget', () => {

+ 3 - 1
static/app/utils/metrics/dashboard.tsx

@@ -1,8 +1,10 @@
 import {urlEncode} from '@sentry/utils';
 
 import {PageFilters} from 'sentry/types';
-import {emptyWidget, MetricDisplayType, MetricsQuery} from 'sentry/utils/metrics';
+import {emptyWidget} from 'sentry/utils/metrics/constants';
 import {formatMRI, MRIToField} from 'sentry/utils/metrics/mri';
+import type {MetricsQuery} from 'sentry/utils/metrics/types';
+import {MetricDisplayType} from 'sentry/utils/metrics/types';
 import {
   DashboardWidgetSource,
   DisplayType,

+ 2 - 1
static/app/utils/metrics/dashboardImport.tsx

@@ -1,5 +1,6 @@
 import {Client} from 'sentry/api';
-import {MetricDisplayType, MetricsQuery} from 'sentry/utils/metrics';
+import type {MetricsQuery} from 'sentry/utils/metrics/types';
+import {MetricDisplayType} from 'sentry/utils/metrics/types';
 
 import {MetricMeta, MRI} from '../../types/metrics';
 

+ 60 - 0
static/app/utils/metrics/formatters.spec.tsx

@@ -0,0 +1,60 @@
+import {
+  formatMetricsUsingUnitAndOp,
+  formatMetricUsingFixedUnit,
+  formattingSupportedMetricUnits,
+} from 'sentry/utils/metrics/formatters';
+
+describe('formatMetricsUsingUnitAndOp', () => {
+  it('should format the value according to the unit', () => {
+    // Test cases for different units
+    expect(formatMetricsUsingUnitAndOp(123456, 'millisecond')).toEqual('2.06min');
+    expect(formatMetricsUsingUnitAndOp(5000, 'second')).toEqual('1.39hr');
+    expect(formatMetricsUsingUnitAndOp(600, 'byte')).toEqual('600 B');
+    expect(formatMetricsUsingUnitAndOp(4096, 'kibibyte')).toEqual('4.0 MiB');
+    expect(formatMetricsUsingUnitAndOp(3145728, 'megabyte')).toEqual('3.15 TB');
+    expect(formatMetricsUsingUnitAndOp(0.99, 'ratio')).toEqual('99%');
+    expect(formatMetricsUsingUnitAndOp(99, 'percent')).toEqual('99%');
+  });
+
+  it('should handle value as null', () => {
+    expect(formatMetricsUsingUnitAndOp(null, 'millisecond')).toEqual('—');
+    expect(formatMetricsUsingUnitAndOp(null, 'byte')).toEqual('—');
+    expect(formatMetricsUsingUnitAndOp(null, 'megabyte')).toEqual('—');
+  });
+
+  it('should format count operation as a number', () => {
+    expect(formatMetricsUsingUnitAndOp(99, 'none', 'count')).toEqual('99');
+    expect(formatMetricsUsingUnitAndOp(null, 'none', 'count')).toEqual('');
+  });
+});
+
+describe('formatMetricUsingFixedUnit', () => {
+  it('should return the formatted value with the short form of the given unit', () => {
+    expect(formatMetricUsingFixedUnit(123456, 'millisecond')).toBe('123,456ms');
+    expect(formatMetricUsingFixedUnit(2.1231245, 'kibibyte')).toBe('2.12KiB');
+    expect(formatMetricUsingFixedUnit(1222.1231245, 'megabyte')).toBe('1,222.12MB');
+  });
+
+  it.each(formattingSupportedMetricUnits.filter(unit => unit !== 'none'))(
+    'appends a unit for every supported one (except none)',
+    unit => {
+      expect(formatMetricUsingFixedUnit(1234.56, unit)).toMatch(/1,234\.56.+/);
+    }
+  );
+
+  it('should not append a unit for unsupported units and "none"', () => {
+    expect(formatMetricUsingFixedUnit(1234.56, 'randomunitname')).toBe('1,234.56');
+    expect(formatMetricUsingFixedUnit(1234.56, 'none')).toBe('1,234.56');
+  });
+
+  it.each(['sum', 'count_unique', 'avg', 'max', 'p50', 'p75', 'p95', 'p99'])(
+    'should append a unit for every operation (except count)',
+    op => {
+      expect(formatMetricUsingFixedUnit(1234.56, 'second', op)).toMatch(/1,234\.56s/);
+    }
+  );
+
+  it('should not append a unit for count operation', () => {
+    expect(formatMetricUsingFixedUnit(1234.56, 'second', 'count')).toBe('1,234.56');
+  });
+});

+ 220 - 0
static/app/utils/metrics/formatters.tsx

@@ -0,0 +1,220 @@
+import {t} from 'sentry/locale';
+import type {MetricType} from 'sentry/types/metrics';
+import {defined, formatBytesBase2, formatBytesBase10} from 'sentry/utils';
+import {
+  DAY,
+  formatNumberWithDynamicDecimalPoints,
+  HOUR,
+  MINUTE,
+  MONTH,
+  SECOND,
+  WEEK,
+} from 'sentry/utils/formatters';
+
+const metricTypeToReadable: Record<MetricType, string> = {
+  c: t('counter'),
+  g: t('gauge'),
+  d: t('distribution'),
+  s: t('set'),
+  e: t('derived'),
+};
+
+// Converts from "c" to "counter"
+export function getReadableMetricType(type?: string) {
+  return metricTypeToReadable[type as MetricType] ?? t('unknown');
+}
+
+const MILLISECOND = 1;
+const MICROSECOND = MILLISECOND / 1000;
+
+function formatDuration(seconds: number): string {
+  if (!seconds) {
+    return '0ms';
+  }
+  const absValue = Math.abs(seconds * 1000);
+  // value in milliseconds
+  const msValue = seconds * 1000;
+
+  let unit: FormattingSupportedMetricUnit | 'month' = 'nanosecond';
+  let value = msValue * 1000000;
+
+  if (absValue >= MONTH) {
+    unit = 'month';
+    value = msValue / MONTH;
+  } else if (absValue >= WEEK) {
+    unit = 'week';
+    value = msValue / WEEK;
+  } else if (absValue >= DAY) {
+    unit = 'day';
+    value = msValue / DAY;
+  } else if (absValue >= HOUR) {
+    unit = 'hour';
+    value = msValue / HOUR;
+  } else if (absValue >= MINUTE) {
+    unit = 'minute';
+    value = msValue / MINUTE;
+  } else if (absValue >= SECOND) {
+    unit = 'second';
+    value = msValue / SECOND;
+  } else if (absValue >= MILLISECOND) {
+    unit = 'millisecond';
+    value = msValue;
+  } else if (absValue >= MICROSECOND) {
+    unit = 'microsecond';
+    value = msValue * 1000;
+  }
+
+  return `${formatNumberWithDynamicDecimalPoints(value)}${
+    unit === 'month' ? 'mo' : METRIC_UNIT_TO_SHORT[unit]
+  }`;
+}
+
+// The metric units that we have support for in the UI
+// others will still be displayed, but will not have any effect on formatting
+export const formattingSupportedMetricUnits = [
+  'none',
+  'nanosecond',
+  'microsecond',
+  'millisecond',
+  'second',
+  'minute',
+  'hour',
+  'day',
+  'week',
+  'ratio',
+  'percent',
+  'bit',
+  'byte',
+  'kibibyte',
+  'kilobyte',
+  'mebibyte',
+  'megabyte',
+  'gibibyte',
+  'gigabyte',
+  'tebibyte',
+  'terabyte',
+  'pebibyte',
+  'petabyte',
+  'exbibyte',
+  'exabyte',
+] as const;
+
+type FormattingSupportedMetricUnit = (typeof formattingSupportedMetricUnits)[number];
+
+const METRIC_UNIT_TO_SHORT: Record<FormattingSupportedMetricUnit, string> = {
+  nanosecond: 'ns',
+  microsecond: 'μs',
+  millisecond: 'ms',
+  second: 's',
+  minute: 'min',
+  hour: 'hr',
+  day: 'day',
+  week: 'wk',
+  ratio: '%',
+  percent: '%',
+  bit: 'b',
+  byte: 'B',
+  kibibyte: 'KiB',
+  kilobyte: 'KB',
+  mebibyte: 'MiB',
+  megabyte: 'MB',
+  gibibyte: 'GiB',
+  gigabyte: 'GB',
+  tebibyte: 'TiB',
+  terabyte: 'TB',
+  pebibyte: 'PiB',
+  petabyte: 'PB',
+  exbibyte: 'EiB',
+  exabyte: 'EB',
+  none: '',
+};
+
+export function formatMetricUsingUnit(value: number | null, unit: string) {
+  if (!defined(value)) {
+    return '\u2014';
+  }
+
+  switch (unit as FormattingSupportedMetricUnit) {
+    case 'nanosecond':
+      return formatDuration(value / 1000000000);
+    case 'microsecond':
+      return formatDuration(value / 1000000);
+    case 'millisecond':
+      return formatDuration(value / 1000);
+    case 'second':
+      return formatDuration(value);
+    case 'minute':
+      return formatDuration(value * 60);
+    case 'hour':
+      return formatDuration(value * 60 * 60);
+    case 'day':
+      return formatDuration(value * 60 * 60 * 24);
+    case 'week':
+      return formatDuration(value * 60 * 60 * 24 * 7);
+    case 'ratio':
+      return `${formatNumberWithDynamicDecimalPoints(value * 100)}%`;
+    case 'percent':
+      return `${formatNumberWithDynamicDecimalPoints(value)}%`;
+    case 'bit':
+      return formatBytesBase2(value / 8);
+    case 'byte':
+      return formatBytesBase10(value);
+    case 'kibibyte':
+      return formatBytesBase2(value * 1024);
+    case 'kilobyte':
+      return formatBytesBase10(value, 1);
+    case 'mebibyte':
+      return formatBytesBase2(value * 1024 ** 2);
+    case 'megabyte':
+      return formatBytesBase10(value, 2);
+    case 'gibibyte':
+      return formatBytesBase2(value * 1024 ** 3);
+    case 'gigabyte':
+      return formatBytesBase10(value, 3);
+    case 'tebibyte':
+      return formatBytesBase2(value * 1024 ** 4);
+    case 'terabyte':
+      return formatBytesBase10(value, 4);
+    case 'pebibyte':
+      return formatBytesBase2(value * 1024 ** 5);
+    case 'petabyte':
+      return formatBytesBase10(value, 5);
+    case 'exbibyte':
+      return formatBytesBase2(value * 1024 ** 6);
+    case 'exabyte':
+      return formatBytesBase10(value, 6);
+    case 'none':
+    default:
+      return value.toLocaleString();
+  }
+}
+
+const getShortMetricUnit = (unit: string): string => METRIC_UNIT_TO_SHORT[unit] ?? '';
+
+export function formatMetricUsingFixedUnit(
+  value: number | null,
+  unit: string,
+  op?: string
+) {
+  if (value === null) {
+    return '\u2014';
+  }
+
+  const formattedNumber = formatNumberWithDynamicDecimalPoints(value);
+
+  return op === 'count'
+    ? formattedNumber
+    : `${formattedNumber}${getShortMetricUnit(unit)}`.trim();
+}
+
+export function formatMetricsUsingUnitAndOp(
+  value: number | null,
+  unit: string,
+  operation?: string
+) {
+  if (operation === 'count') {
+    // if the operation is count, we want to ignore the unit and always format the value as a number
+    return value?.toLocaleString() ?? '';
+  }
+  return formatMetricUsingUnit(value, unit);
+}

+ 0 - 57
static/app/utils/metrics/index.spec.tsx

@@ -1,8 +1,5 @@
 import {MetricsApiRequestQueryOptions, MetricsOperation, PageFilters} from 'sentry/types';
 import {
-  formatMetricsUsingUnitAndOp,
-  formatMetricUsingFixedUnit,
-  formattingSupportedMetricUnits,
   getAbsoluteDateTimeRange,
   getDateTimeParams,
   getDDMInterval,
@@ -10,29 +7,6 @@ import {
   stringifyMetricWidget,
 } from 'sentry/utils/metrics';
 
-describe('formatMetricsUsingUnitAndOp', () => {
-  it('should format the value according to the unit', () => {
-    expect(formatMetricsUsingUnitAndOp(123456, 'millisecond')).toEqual('2.06min');
-    expect(formatMetricsUsingUnitAndOp(5000, 'second')).toEqual('1.39hr');
-    expect(formatMetricsUsingUnitAndOp(600, 'byte')).toEqual('600 B');
-    expect(formatMetricsUsingUnitAndOp(4096, 'kibibyte')).toEqual('4.0 MiB');
-    expect(formatMetricsUsingUnitAndOp(3145728, 'megabyte')).toEqual('3.15 TB');
-    expect(formatMetricsUsingUnitAndOp(0.99, 'ratio')).toEqual('99%');
-    expect(formatMetricsUsingUnitAndOp(99, 'percent')).toEqual('99%');
-  });
-
-  it('should handle value as null', () => {
-    expect(formatMetricsUsingUnitAndOp(null, 'millisecond')).toEqual('—');
-    expect(formatMetricsUsingUnitAndOp(null, 'byte')).toEqual('—');
-    expect(formatMetricsUsingUnitAndOp(null, 'megabyte')).toEqual('—');
-  });
-
-  it('should format count operation as a number', () => {
-    expect(formatMetricsUsingUnitAndOp(99, 'none', 'count')).toEqual('99');
-    expect(formatMetricsUsingUnitAndOp(null, 'none', 'count')).toEqual('');
-  });
-});
-
 describe('getMetricsApiRequestQuery', () => {
   it('should return the correct query object with default values', () => {
     const metric = {field: 'sessions', query: 'error', groupBy: ['project']};
@@ -256,37 +230,6 @@ describe('getDDMInterval', () => {
   });
 });
 
-describe('formatMetricUsingFixedUnit', () => {
-  it('should return the formatted value with the short form of the given unit', () => {
-    expect(formatMetricUsingFixedUnit(123456, 'millisecond')).toBe('123,456ms');
-    expect(formatMetricUsingFixedUnit(2.1231245, 'kibibyte')).toBe('2.12KiB');
-    expect(formatMetricUsingFixedUnit(1222.1231245, 'megabyte')).toBe('1,222.12MB');
-  });
-
-  it.each(formattingSupportedMetricUnits.filter(unit => unit !== 'none'))(
-    'appends a unit for every supported one (except none)',
-    unit => {
-      expect(formatMetricUsingFixedUnit(1234.56, unit)).toMatch(/1,234\.56.+/);
-    }
-  );
-
-  it('should not append a unit for unsupported units and "none"', () => {
-    expect(formatMetricUsingFixedUnit(1234.56, 'randomunitname')).toBe('1,234.56');
-    expect(formatMetricUsingFixedUnit(1234.56, 'none')).toBe('1,234.56');
-  });
-
-  it.each(['sum', 'count_unique', 'avg', 'max', 'p50', 'p75', 'p95', 'p99'])(
-    'does append a unit for every operation (except count)',
-    op => {
-      expect(formatMetricUsingFixedUnit(1234.56, 'second', op)).toMatch(/1,234\.56s/);
-    }
-  );
-
-  it('should not append a unit for count operation', () => {
-    expect(formatMetricUsingFixedUnit(1234.56, 'second', 'count')).toBe('1,234.56');
-  });
-});
-
 describe('getDateTimeParams', () => {
   it('should return the correct object with "statsPeriod" when period is provided', () => {
     const datetime = {start: '2023-01-01', end: '2023-01-31', period: '7d', utc: true};

+ 13 - 363
static/app/utils/metrics/index.tsx

@@ -16,30 +16,23 @@ import {
   TWENTY_FOUR_HOURS,
   TWO_WEEKS,
 } from 'sentry/components/charts/utils';
-import {t} from 'sentry/locale';
-import {MetricsApiResponse} from 'sentry/types';
 import {
+  normalizeDateTimeParams,
+  parseStatsPeriod,
+} from 'sentry/components/organizations/pageFilters/parse';
+import {t} from 'sentry/locale';
+import {MetricsApiResponse, PageFilters} from 'sentry/types';
+import type {
   MetricMeta,
   MetricsApiRequestMetric,
   MetricsApiRequestQuery,
   MetricsApiRequestQueryOptions,
   MetricsGroup,
   MetricsOperation,
-  MetricType,
   MRI,
   UseCase,
 } from 'sentry/types/metrics';
-import {defined, formatBytesBase2, formatBytesBase10} from 'sentry/utils';
 import {isMeasurement as isMeasurementName} from 'sentry/utils/discover/fields';
-import {
-  DAY,
-  formatNumberWithDynamicDecimalPoints,
-  HOUR,
-  MINUTE,
-  MONTH,
-  SECOND,
-  WEEK,
-} from 'sentry/utils/formatters';
 import {getMeasurements} from 'sentry/utils/measurements/measurements';
 import {
   formatMRI,
@@ -49,38 +42,14 @@ import {
   parseField,
   parseMRI,
 } from 'sentry/utils/metrics/mri';
+import type {
+  DdmQueryParams,
+  MetricsQuery,
+  MetricsQuerySubject,
+  MetricWidgetQueryParams,
+} from 'sentry/utils/metrics/types';
+import {MetricDisplayType} from 'sentry/utils/metrics/types';
 import useRouter from 'sentry/utils/useRouter';
-import {DEFAULT_SORT_STATE} from 'sentry/views/ddm/constants';
-
-import {
-  normalizeDateTimeParams,
-  parseStatsPeriod,
-} from '../../components/organizations/pageFilters/parse';
-import {DateString, PageFilters} from '../../types/core';
-
-export const METRICS_DOCS_URL =
-  'https://develop.sentry.dev/delightful-developer-metrics/';
-
-export enum MetricDisplayType {
-  LINE = 'line',
-  AREA = 'area',
-  BAR = 'bar',
-}
-
-export const metricDisplayTypeOptions = [
-  {
-    value: MetricDisplayType.LINE,
-    label: t('Line'),
-  },
-  {
-    value: MetricDisplayType.AREA,
-    label: t('Area'),
-  },
-  {
-    value: MetricDisplayType.BAR,
-    label: t('Bar'),
-  },
-];
 
 export function getDefaultMetricDisplayType(
   mri: MetricsQuery['mri'],
@@ -104,117 +73,6 @@ export const getMetricDisplayType = (displayType: unknown): MetricDisplayType =>
   return MetricDisplayType.LINE;
 };
 
-export type MetricTag = {
-  key: string;
-};
-
-export type SortState = {
-  name: 'name' | 'avg' | 'min' | 'max' | 'sum' | undefined;
-  order: 'asc' | 'desc';
-};
-
-export interface MetricWidgetQueryParams extends MetricsQuerySubject {
-  displayType: MetricDisplayType;
-  focusedSeries?: {
-    seriesName: string;
-    groupBy?: Record<string, string>;
-  };
-  highlightedSample?: string | null;
-  powerUserMode?: boolean;
-  showSummaryTable?: boolean;
-  sort?: SortState;
-}
-
-export interface DdmQueryParams {
-  widgets: string; // stringified json representation of MetricWidgetQueryParams
-  end?: DateString;
-  environment?: string[];
-  project?: number[];
-  start?: DateString;
-  statsPeriod?: string | null;
-  utc?: boolean | null;
-}
-
-export type MetricsQuery = {
-  datetime: PageFilters['datetime'];
-  environments: PageFilters['environments'];
-  mri: MRI;
-  projects: PageFilters['projects'];
-  groupBy?: string[];
-  op?: string;
-  query?: string;
-  title?: string;
-};
-
-export type MetricsQuerySubject = Pick<
-  MetricsQuery,
-  'mri' | 'op' | 'query' | 'groupBy' | 'title'
->;
-
-export type MetricCodeLocationFrame = {
-  absPath?: string;
-  contextLine?: string;
-  filename?: string;
-  function?: string;
-  lineNo?: number;
-  module?: string;
-  platform?: string;
-  postContext?: string[];
-  preContext?: string[];
-};
-
-export type MetricMetaCodeLocation = {
-  mri: string;
-  timestamp: number;
-  codeLocations?: MetricCodeLocationFrame[];
-  frames?: MetricCodeLocationFrame[];
-  metricSpans?: MetricCorrelation[];
-};
-
-export type MetricCorrelation = {
-  duration: number;
-  metricSummaries: {
-    spanId: string;
-    count?: number;
-    max?: number;
-    min?: number;
-    sum?: number;
-  }[];
-  profileId: string;
-  projectId: number;
-  segmentName: string;
-  spansDetails: {
-    spanDuration: number;
-    spanId: string;
-    spanTimestamp: string;
-  }[];
-  spansNumber: number;
-  timestamp: string;
-  traceId: string;
-  transactionId: string;
-  spansSummary?: {
-    spanDuration: number;
-    spanOp: string;
-  }[];
-};
-
-export type MetricRange = {
-  end?: DateString;
-  max?: number;
-  min?: number;
-  start?: DateString;
-};
-
-export const emptyWidget: MetricWidgetQueryParams = {
-  mri: 'd:transactions/duration@millisecond' satisfies MRI,
-  op: 'avg',
-  query: '',
-  groupBy: [],
-  sort: DEFAULT_SORT_STATE,
-  displayType: MetricDisplayType.LINE,
-  title: undefined,
-};
-
 export function getDdmUrl(
   orgSlug: string,
   {
@@ -318,14 +176,6 @@ export function getDateTimeParams({start, end, period}: PageFilters['datetime'])
     : {start: moment(start).toISOString(), end: moment(end).toISOString()};
 }
 
-const metricTypeToReadable: Record<MetricType, string> = {
-  c: t('counter'),
-  g: t('gauge'),
-  d: t('distribution'),
-  s: t('set'),
-  e: t('derived'),
-};
-
 export function getDefaultMetricOp(mri: MRI): MetricsOperation {
   const parsedMRI = parseMRI(mri);
   switch (parsedMRI?.type) {
@@ -340,206 +190,6 @@ export function getDefaultMetricOp(mri: MRI): MetricsOperation {
   }
 }
 
-// Converts from "c" to "counter"
-export function getReadableMetricType(type?: string) {
-  return metricTypeToReadable[type as MetricType] ?? t('unknown');
-}
-
-const MILLISECOND = 1;
-const MICROSECOND = MILLISECOND / 1000;
-
-export function formatDuration(seconds: number): string {
-  if (!seconds) {
-    return '0ms';
-  }
-  const absValue = Math.abs(seconds * 1000);
-  // value in milliseconds
-  const msValue = seconds * 1000;
-
-  let unit: FormattingSupportedMetricUnit | 'month' = 'nanosecond';
-  let value = msValue * 1000000;
-
-  if (absValue >= MONTH) {
-    unit = 'month';
-    value = msValue / MONTH;
-  } else if (absValue >= WEEK) {
-    unit = 'week';
-    value = msValue / WEEK;
-  } else if (absValue >= DAY) {
-    unit = 'day';
-    value = msValue / DAY;
-  } else if (absValue >= HOUR) {
-    unit = 'hour';
-    value = msValue / HOUR;
-  } else if (absValue >= MINUTE) {
-    unit = 'minute';
-    value = msValue / MINUTE;
-  } else if (absValue >= SECOND) {
-    unit = 'second';
-    value = msValue / SECOND;
-  } else if (absValue >= MILLISECOND) {
-    unit = 'millisecond';
-    value = msValue;
-  } else if (absValue >= MICROSECOND) {
-    unit = 'microsecond';
-    value = msValue * 1000;
-  }
-
-  return `${formatNumberWithDynamicDecimalPoints(value)}${
-    unit === 'month' ? 'mo' : METRIC_UNIT_TO_SHORT[unit]
-  }`;
-}
-
-// The metric units that we have support for in the UI
-// others will still be displayed, but will not have any effect on formatting
-export const formattingSupportedMetricUnits = [
-  'none',
-  'nanosecond',
-  'microsecond',
-  'millisecond',
-  'second',
-  'minute',
-  'hour',
-  'day',
-  'week',
-  'ratio',
-  'percent',
-  'bit',
-  'byte',
-  'kibibyte',
-  'kilobyte',
-  'mebibyte',
-  'megabyte',
-  'gibibyte',
-  'gigabyte',
-  'tebibyte',
-  'terabyte',
-  'pebibyte',
-  'petabyte',
-  'exbibyte',
-  'exabyte',
-] as const;
-
-type FormattingSupportedMetricUnit = (typeof formattingSupportedMetricUnits)[number];
-
-const METRIC_UNIT_TO_SHORT: Record<FormattingSupportedMetricUnit, string> = {
-  nanosecond: 'ns',
-  microsecond: 'μs',
-  millisecond: 'ms',
-  second: 's',
-  minute: 'min',
-  hour: 'hr',
-  day: 'day',
-  week: 'wk',
-  ratio: '%',
-  percent: '%',
-  bit: 'b',
-  byte: 'B',
-  kibibyte: 'KiB',
-  kilobyte: 'KB',
-  mebibyte: 'MiB',
-  megabyte: 'MB',
-  gibibyte: 'GiB',
-  gigabyte: 'GB',
-  tebibyte: 'TiB',
-  terabyte: 'TB',
-  pebibyte: 'PiB',
-  petabyte: 'PB',
-  exbibyte: 'EiB',
-  exabyte: 'EB',
-  none: '',
-};
-
-export function formatMetricUsingUnit(value: number | null, unit: string) {
-  if (!defined(value)) {
-    return '\u2014';
-  }
-
-  switch (unit as FormattingSupportedMetricUnit) {
-    case 'nanosecond':
-      return formatDuration(value / 1000000000);
-    case 'microsecond':
-      return formatDuration(value / 1000000);
-    case 'millisecond':
-      return formatDuration(value / 1000);
-    case 'second':
-      return formatDuration(value);
-    case 'minute':
-      return formatDuration(value * 60);
-    case 'hour':
-      return formatDuration(value * 60 * 60);
-    case 'day':
-      return formatDuration(value * 60 * 60 * 24);
-    case 'week':
-      return formatDuration(value * 60 * 60 * 24 * 7);
-    case 'ratio':
-      return `${formatNumberWithDynamicDecimalPoints(value * 100)}%`;
-    case 'percent':
-      return `${formatNumberWithDynamicDecimalPoints(value)}%`;
-    case 'bit':
-      return formatBytesBase2(value / 8);
-    case 'byte':
-      return formatBytesBase10(value);
-    case 'kibibyte':
-      return formatBytesBase2(value * 1024);
-    case 'kilobyte':
-      return formatBytesBase10(value, 1);
-    case 'mebibyte':
-      return formatBytesBase2(value * 1024 ** 2);
-    case 'megabyte':
-      return formatBytesBase10(value, 2);
-    case 'gibibyte':
-      return formatBytesBase2(value * 1024 ** 3);
-    case 'gigabyte':
-      return formatBytesBase10(value, 3);
-    case 'tebibyte':
-      return formatBytesBase2(value * 1024 ** 4);
-    case 'terabyte':
-      return formatBytesBase10(value, 4);
-    case 'pebibyte':
-      return formatBytesBase2(value * 1024 ** 5);
-    case 'petabyte':
-      return formatBytesBase10(value, 5);
-    case 'exbibyte':
-      return formatBytesBase2(value * 1024 ** 6);
-    case 'exabyte':
-      return formatBytesBase10(value, 6);
-    case 'none':
-    default:
-      return value.toLocaleString();
-  }
-}
-
-const getShortMetricUnit = (unit: string): string => METRIC_UNIT_TO_SHORT[unit] ?? '';
-
-export function formatMetricUsingFixedUnit(
-  value: number | null,
-  unit: string,
-  op?: string
-) {
-  if (value === null) {
-    return '\u2014';
-  }
-
-  const formattedNumber = formatNumberWithDynamicDecimalPoints(value);
-
-  return op === 'count'
-    ? formattedNumber
-    : `${formattedNumber}${getShortMetricUnit(unit)}`.trim();
-}
-
-export function formatMetricsUsingUnitAndOp(
-  value: number | null,
-  unit: string,
-  operation?: string
-) {
-  if (operation === 'count') {
-    // if the operation is count, we want to ignore the unit and always format the value as a number
-    return value?.toLocaleString() ?? '';
-  }
-  return formatMetricUsingUnit(value, unit);
-}
-
 export function isAllowedOp(op: string) {
   return !['max_timestamp', 'min_timestamp', 'histogram'].includes(op);
 }

+ 108 - 0
static/app/utils/metrics/types.tsx

@@ -0,0 +1,108 @@
+import type {DateString, MRI, PageFilters} from 'sentry/types';
+
+export enum MetricDisplayType {
+  LINE = 'line',
+  AREA = 'area',
+  BAR = 'bar',
+}
+
+export type MetricTag = {
+  key: string;
+};
+
+export type SortState = {
+  name: 'name' | 'avg' | 'min' | 'max' | 'sum' | undefined;
+  order: 'asc' | 'desc';
+};
+
+export interface MetricWidgetQueryParams extends MetricsQuerySubject {
+  displayType: MetricDisplayType;
+  focusedSeries?: {
+    seriesName: string;
+    groupBy?: Record<string, string>;
+  };
+  highlightedSample?: string | null;
+  powerUserMode?: boolean;
+  showSummaryTable?: boolean;
+  sort?: SortState;
+}
+
+export interface DdmQueryParams {
+  widgets: string; // stringified json representation of MetricWidgetQueryParams
+  end?: DateString;
+  environment?: string[];
+  project?: number[];
+  start?: DateString;
+  statsPeriod?: string | null;
+  utc?: boolean | null;
+}
+
+export type MetricsQuery = {
+  datetime: PageFilters['datetime'];
+  environments: PageFilters['environments'];
+  mri: MRI;
+  projects: PageFilters['projects'];
+  groupBy?: string[];
+  op?: string;
+  query?: string;
+  title?: string;
+};
+
+export type MetricsQuerySubject = Pick<
+  MetricsQuery,
+  'mri' | 'op' | 'query' | 'groupBy' | 'title'
+>;
+
+export type MetricCodeLocationFrame = {
+  absPath?: string;
+  contextLine?: string;
+  filename?: string;
+  function?: string;
+  lineNo?: number;
+  module?: string;
+  platform?: string;
+  postContext?: string[];
+  preContext?: string[];
+};
+
+export type MetricMetaCodeLocation = {
+  mri: string;
+  timestamp: number;
+  codeLocations?: MetricCodeLocationFrame[];
+  frames?: MetricCodeLocationFrame[];
+  metricSpans?: MetricCorrelation[];
+};
+
+export type MetricCorrelation = {
+  duration: number;
+  metricSummaries: {
+    spanId: string;
+    count?: number;
+    max?: number;
+    min?: number;
+    sum?: number;
+  }[];
+  profileId: string;
+  projectId: number;
+  segmentName: string;
+  spansDetails: {
+    spanDuration: number;
+    spanId: string;
+    spanTimestamp: string;
+  }[];
+  spansNumber: number;
+  timestamp: string;
+  traceId: string;
+  transactionId: string;
+  spansSummary?: {
+    spanDuration: number;
+    spanOp: string;
+  }[];
+};
+
+export type MetricRange = {
+  end?: DateString;
+  max?: number;
+  min?: number;
+  start?: DateString;
+};

+ 3 - 5
static/app/utils/metrics/useIncrementQueryMetric.tsx

@@ -1,12 +1,10 @@
 import * as Sentry from '@sentry/react';
 
 import {MRI} from 'sentry/types';
-import {
-  getDefaultMetricDisplayType,
-  getReadableMetricType,
-  MetricDisplayType,
-} from 'sentry/utils/metrics';
+import {getDefaultMetricDisplayType} from 'sentry/utils/metrics';
+import {getReadableMetricType} from 'sentry/utils/metrics/formatters';
 import {parseMRI} from 'sentry/utils/metrics/mri';
+import {MetricDisplayType} from 'sentry/utils/metrics/types';
 
 interface Options {
   displayType: MetricDisplayType;

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