Browse Source

chore(metrics): Add type guards for widgets (#67868)

ArthurKnaus 11 months ago
parent
commit
3773640aff

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

@@ -53,6 +53,18 @@ export interface MetricsEquationWidget extends BaseWidgetParams {
 
 export type MetricsWidget = MetricsQueryWidget | MetricsEquationWidget;
 
+export function isMetricsEquationWidget(
+  widget: MetricsWidget
+): widget is MetricsEquationWidget {
+  return widget.type === MetricExpressionType.EQUATION;
+}
+
+export function isMetricsQueryWidget(
+  widget: MetricsWidget
+): widget is MetricsQueryWidget {
+  return widget.type === MetricExpressionType.QUERY;
+}
+
 export interface MetricsQueryParams {
   widgets: string; // stringified json representation of MetricsWidget
   end?: DateString;

+ 2 - 3
static/app/views/metrics/metricQueryContextMenu.tsx

@@ -27,8 +27,8 @@ import {
 } from 'sentry/utils/metrics/dashboard';
 import {hasCustomMetrics} from 'sentry/utils/metrics/features';
 import {
+  isMetricsQueryWidget,
   type MetricDisplayType,
-  MetricExpressionType,
   type MetricsQuery,
 } from 'sentry/utils/metrics/types';
 import useOrganization from 'sentry/utils/useOrganization';
@@ -64,8 +64,7 @@ export function MetricQueryContextMenu({
   );
 
   // At least one query must remain
-  const canDelete =
-    widgets.filter(widget => widget.type === MetricExpressionType.QUERY).length > 1;
+  const canDelete = widgets.filter(isMetricsQueryWidget).length > 1;
 
   const items = useMemo<MenuItemProps[]>(
     () => [

+ 3 - 2
static/app/views/metrics/queries.tsx

@@ -10,6 +10,7 @@ import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {trackAnalytics} from 'sentry/utils/analytics';
 import {
+  isMetricsQueryWidget,
   MetricExpressionType,
   type MetricsEquationWidget,
   type MetricsQuery,
@@ -72,7 +73,7 @@ export function Queries() {
     const querySymbolSet = new Set<string>();
     for (const widget of widgets) {
       const symbol = getQuerySymbol(widget.id);
-      if (widget.type === MetricExpressionType.QUERY) {
+      if (isMetricsQueryWidget(widget)) {
         querySymbolSet.add(symbol);
       }
     }
@@ -89,7 +90,7 @@ export function Queries() {
             key={`${widget.type}_${widget.id}`}
             onFocusCapture={() => setSelectedWidgetIndex(index)}
           >
-            {widget.type === MetricExpressionType.QUERY ? (
+            {isMetricsQueryWidget(widget) ? (
               <Query
                 widget={widget}
                 onChange={handleChange}

+ 6 - 2
static/app/views/metrics/scratchpad.tsx

@@ -7,7 +7,11 @@ import type {Field} from 'sentry/components/metrics/metricSamplesTable';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {getMetricsCorrelationSpanUrl} from 'sentry/utils/metrics';
-import {MetricExpressionType, type MetricsWidget} from 'sentry/utils/metrics/types';
+import {
+  isMetricsEquationWidget,
+  MetricExpressionType,
+  type MetricsWidget,
+} from 'sentry/utils/metrics/types';
 import type {MetricsQueryApiQueryParams} from 'sentry/utils/metrics/useMetricsQuery';
 import type {MetricsSamplesResults} from 'sentry/utils/metrics/useMetricsSamples';
 import useOrganization from 'sentry/utils/useOrganization';
@@ -163,7 +167,7 @@ function MultiChartWidgetQueries({
   const queries = useMemo(() => {
     return [
       widgetToQuery(widget),
-      ...(widget.type === MetricExpressionType.EQUATION
+      ...(isMetricsEquationWidget(widget)
         ? formulaDependencies[widget.id]?.dependencies?.map(dependency =>
             widgetToQuery(dependency, true)
           )

+ 6 - 2
static/app/views/metrics/useCreateDashboard.tsx

@@ -2,7 +2,11 @@ import {useMemo} from 'react';
 
 import {openCreateDashboardFromScratchpad} from 'sentry/actionCreators/modal';
 import {convertToDashboardWidget} from 'sentry/utils/metrics/dashboard';
-import {MetricExpressionType, type MetricsWidget} from 'sentry/utils/metrics/types';
+import {
+  isMetricsEquationWidget,
+  MetricExpressionType,
+  type MetricsWidget,
+} from 'sentry/utils/metrics/types';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import useRouter from 'sentry/utils/useRouter';
@@ -22,7 +26,7 @@ export function useCreateDashboard(
     if (!isMultiChartMode) {
       const queryIdsInArray = new Set<number>();
       const widgetsWithDependencies = widgets.reduce<MetricsWidget[]>((acc, widget) => {
-        if (widget.type === MetricExpressionType.EQUATION) {
+        if (isMetricsEquationWidget(widget)) {
           const {dependencies, isError} = formulaDependencies[widget.id];
           if (isError) {
             return acc;

+ 7 - 3
static/app/views/metrics/utils/useFormulaDependencies.tsx

@@ -1,7 +1,11 @@
 import {useCallback, useMemo} from 'react';
 
 import {unescapeMetricsFormula} from 'sentry/utils/metrics';
-import {MetricExpressionType, type MetricsQueryWidget} from 'sentry/utils/metrics/types';
+import {
+  isMetricsEquationWidget,
+  isMetricsQueryWidget,
+  type MetricsQueryWidget,
+} from 'sentry/utils/metrics/types';
 import {useMetricsContext} from 'sentry/views/metrics/context';
 import {parseFormula} from 'sentry/views/metrics/formulaParser/parser';
 import {type TokenList, TokenType} from 'sentry/views/metrics/formulaParser/types';
@@ -17,7 +21,7 @@ export function useFormulaDependencies() {
   const queriesLookup = useMemo(() => {
     const lookup = new Map<string, MetricsQueryWidget>();
     widgets.forEach(widget => {
-      if (widget.type === MetricExpressionType.QUERY) {
+      if (isMetricsQueryWidget(widget)) {
         lookup.set(getQuerySymbol(widget.id), widget);
       }
     });
@@ -56,7 +60,7 @@ export function useFormulaDependencies() {
 
   const formulaDependencies = useMemo(() => {
     return widgets.reduce((acc: Record<number, FormulaDependencies>, widget) => {
-      if (widget.type === MetricExpressionType.EQUATION) {
+      if (isMetricsEquationWidget(widget)) {
         acc[widget.id] = getFormulaQueryDependencies(widget.formula);
       }
       return acc;

+ 2 - 3
static/app/views/metrics/utils/useMetricsIntervalParam.tsx

@@ -16,7 +16,7 @@ import type {PageFilters} from 'sentry/types';
 import {parsePeriodToHours} from 'sentry/utils/dates';
 import {useUpdateQuery} from 'sentry/utils/metrics';
 import {parseMRI} from 'sentry/utils/metrics/mri';
-import {MetricExpressionType} from 'sentry/utils/metrics/types';
+import {isMetricsEquationWidget} from 'sentry/utils/metrics/types';
 import {decodeScalar} from 'sentry/utils/queryString';
 import useLocationQuery from 'sentry/utils/url/useLocationQuery';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -87,8 +87,7 @@ export function useMetricsIntervalParam() {
   const isCustomMetricsOnly = useMemo(() => {
     return widgets.every(
       widget =>
-        widget.type === MetricExpressionType.EQUATION ||
-        parseMRI(widget.mri)?.useCase === 'custom'
+        isMetricsEquationWidget(widget) || parseMRI(widget.mri)?.useCase === 'custom'
     );
   }, [widgets]);
 

+ 2 - 2
static/app/views/metrics/utils/widgetToQuery.tsx

@@ -1,4 +1,4 @@
-import {MetricExpressionType, type MetricsWidget} from 'sentry/utils/metrics/types';
+import {isMetricsEquationWidget, type MetricsWidget} from 'sentry/utils/metrics/types';
 import type {MetricsQueryApiQueryParams} from 'sentry/utils/metrics/useMetricsQuery';
 import {getEquationSymbol} from 'sentry/views/metrics/equationSymbol copy';
 import {getQuerySymbol} from 'sentry/views/metrics/querySymbol';
@@ -7,7 +7,7 @@ export function widgetToQuery(
   widget: MetricsWidget,
   isQueryOnly = false
 ): MetricsQueryApiQueryParams {
-  return widget.type === MetricExpressionType.EQUATION
+  return isMetricsEquationWidget(widget)
     ? {
         name: getEquationSymbol(widget.id),
         formula: widget.formula,