Browse Source

ref(starfish): Extract some URL data logic into hooks (#54695)

More preparations. I need to refactor/copy/move a bunch of components,
but they're unwildy and annoying. This PR helps by moving some logic
into hooks. With better primitives, refactoring is easier.

Instead of pulling stuff off the location in the components, pull all
that logic into hooks where they can be shared.
George Gritsouk 1 year ago
parent
commit
826ccbf445

+ 5 - 11
static/app/views/starfish/views/spans/spanTimeCharts.tsx

@@ -21,6 +21,7 @@ import {
   getDurationChartTitle,
   getThroughputChartTitle,
 } from 'sentry/views/starfish/views/spans/types';
+import {ModuleFilters} from 'sentry/views/starfish/views/spans/useModuleFilters';
 import {NULL_SPAN_CATEGORY} from 'sentry/views/starfish/views/webServiceView/spanGroupBreakdownContainer';
 
 const {SPAN_SELF_TIME, SPAN_OP, SPAN_MODULE, SPAN_DESCRIPTION} = SpanMetricsFields;
@@ -28,20 +29,13 @@ const {SPAN_SELF_TIME, SPAN_OP, SPAN_MODULE, SPAN_DESCRIPTION} = SpanMetricsFiel
 const CHART_HEIGHT = 140;
 
 type Props = {
-  appliedFilters: AppliedFilters;
+  appliedFilters: ModuleFilters;
   moduleName: ModuleName;
   spanCategory?: string;
 };
 
-type AppliedFilters = {
-  'span.action': string;
-  'span.domain': string;
-  'span.group': string;
-  'span.op': string;
-};
-
 type ChartProps = {
-  filters: AppliedFilters;
+  filters: ModuleFilters;
   moduleName: ModuleName;
 };
 
@@ -238,7 +232,7 @@ const SPAN_FILTER_KEYS = ['span_operation', 'domain', 'action'];
 const getEventView = (
   moduleName: ModuleName,
   pageFilters: PageFilters,
-  appliedFilters: AppliedFilters,
+  appliedFilters: ModuleFilters,
   spanCategory?: string
 ) => {
   const query = buildDiscoverQueryConditions(moduleName, appliedFilters, spanCategory);
@@ -259,7 +253,7 @@ const getEventView = (
 
 const buildDiscoverQueryConditions = (
   moduleName: ModuleName,
-  appliedFilters: AppliedFilters,
+  appliedFilters: ModuleFilters,
   spanCategory?: string
 ) => {
   const result = Object.keys(appliedFilters)

+ 2 - 24
static/app/views/starfish/views/spans/spansTable.tsx

@@ -11,7 +11,6 @@ import {Organization} from 'sentry/types';
 import {defined} from 'sentry/utils';
 import {EventsMetaType} from 'sentry/utils/discover/eventView';
 import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
-import type {Sort} from 'sentry/utils/discover/fields';
 import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
 import {decodeScalar} from 'sentry/utils/queryString';
 import {useLocation} from 'sentry/utils/useLocation';
@@ -19,13 +18,10 @@ import useOrganization from 'sentry/utils/useOrganization';
 import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/renderHeadCell';
 import {SpanDescriptionCell} from 'sentry/views/starfish/components/tableCells/spanDescriptionCell';
 import {useSpanList} from 'sentry/views/starfish/queries/useSpanList';
-import {
-  ModuleName,
-  SpanMetricsFields,
-  StarfishFunctions,
-} from 'sentry/views/starfish/types';
+import {ModuleName, SpanMetricsFields} from 'sentry/views/starfish/types';
 import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
 import {DataTitles, getThroughputTitle} from 'sentry/views/starfish/views/spans/types';
+import type {ValidSort} from 'sentry/views/starfish/views/spans/useModuleSort';
 
 type Row = {
   'avg(span.self_time)': number;
@@ -41,10 +37,6 @@ type Row = {
 
 type Column = GridColumnHeader<keyof Row>;
 
-type ValidSort = Sort & {
-  field: keyof Row;
-};
-
 type Props = {
   moduleName: ModuleName;
   sort: ValidSort;
@@ -57,16 +49,6 @@ type Props = {
 
 const {SPAN_SELF_TIME, SPAN_DESCRIPTION, SPAN_DOMAIN, SPAN_GROUP, SPAN_OP, PROJECT_ID} =
   SpanMetricsFields;
-const {TIME_SPENT_PERCENTAGE, SPS, SPM, HTTP_ERROR_COUNT} = StarfishFunctions;
-
-const SORTABLE_FIELDS = new Set([
-  `avg(${SPAN_SELF_TIME})`,
-  `${SPS}()`,
-  `${SPM}()`,
-  `${TIME_SPENT_PERCENTAGE}()`,
-  `${TIME_SPENT_PERCENTAGE}(local)`,
-  `${HTTP_ERROR_COUNT}()`,
-]);
 
 export default function SpansTable({
   moduleName,
@@ -284,7 +266,3 @@ function getColumns(
 
   return order.filter((item): item is NonNullable<Column> => Boolean(item));
 }
-
-export function isAValidSort(sort: Sort): sort is ValidSort {
-  return SORTABLE_FIELDS.has(sort.field);
-}

+ 10 - 34
static/app/views/starfish/views/spans/spansView.tsx

@@ -1,26 +1,19 @@
 import {Fragment} from 'react';
 import styled from '@emotion/styled';
-import pick from 'lodash/pick';
 
 import {space} from 'sentry/styles/space';
-import {fromSorts} from 'sentry/utils/discover/eventView';
-import type {Sort} from 'sentry/utils/discover/fields';
-import {useLocation} from 'sentry/utils/useLocation';
 import {ModuleName, SpanMetricsFields} from 'sentry/views/starfish/types';
-import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
 import {ActionSelector} from 'sentry/views/starfish/views/spans/selectors/actionSelector';
 import {DomainSelector} from 'sentry/views/starfish/views/spans/selectors/domainSelector';
 import {SpanOperationSelector} from 'sentry/views/starfish/views/spans/selectors/spanOperationSelector';
 import {SpanTimeCharts} from 'sentry/views/starfish/views/spans/spanTimeCharts';
+import {useModuleFilters} from 'sentry/views/starfish/views/spans/useModuleFilters';
+import {useModuleSort} from 'sentry/views/starfish/views/spans/useModuleSort';
 
-import SpansTable, {isAValidSort} from './spansTable';
+import SpansTable from './spansTable';
 
-const {SPAN_ACTION, SPAN_DOMAIN, SPAN_OP, SPAN_GROUP} = SpanMetricsFields;
+const {SPAN_ACTION, SPAN_DOMAIN, SPAN_OP} = SpanMetricsFields;
 
-const DEFAULT_SORT: Sort = {
-  kind: 'desc',
-  field: 'time_spent_percentage()',
-};
 const LIMIT: number = 25;
 
 type Props = {
@@ -28,34 +21,17 @@ type Props = {
   spanCategory?: string;
 };
 
-type Query = {
-  'span.action': string;
-  'span.domain': string;
-  'span.group': string;
-  'span.op': string;
-  [QueryParameterNames.SORT]: string;
-};
-
 export default function SpansView(props: Props) {
   const moduleName = props.moduleName ?? ModuleName.ALL;
 
-  const location = useLocation<Query>();
-  const appliedFilters = pick(location.query, [
-    SPAN_ACTION,
-    SPAN_DOMAIN,
-    SPAN_OP,
-    SPAN_GROUP,
-  ]);
-
-  const sort =
-    fromSorts(location.query[QueryParameterNames.SORT]).filter(isAValidSort)[0] ??
-    DEFAULT_SORT; // We only allow one sort on this table in this view
+  const moduleFilters = useModuleFilters();
+  const sort = useModuleSort();
 
   return (
     <Fragment>
       <SpanTimeCharts
         moduleName={moduleName}
-        appliedFilters={appliedFilters}
+        appliedFilters={moduleFilters}
         spanCategory={props.spanCategory}
       />
 
@@ -66,20 +42,20 @@ export default function SpansView(props: Props) {
         {[ModuleName.ALL, ModuleName.OTHER].includes(moduleName) && (
           <SpanOperationSelector
             moduleName={moduleName}
-            value={appliedFilters[SPAN_OP] || ''}
+            value={moduleFilters[SPAN_OP] || ''}
             spanCategory={props.spanCategory}
           />
         )}
 
         <ActionSelector
           moduleName={moduleName}
-          value={appliedFilters[SPAN_ACTION] || ''}
+          value={moduleFilters[SPAN_ACTION] || ''}
           spanCategory={props.spanCategory}
         />
 
         <DomainSelector
           moduleName={moduleName}
-          value={appliedFilters[SPAN_DOMAIN] || ''}
+          value={moduleFilters[SPAN_DOMAIN] || ''}
           spanCategory={props.spanCategory}
         />
       </FilterOptionsContainer>

+ 22 - 0
static/app/views/starfish/views/spans/useModuleFilters.ts

@@ -0,0 +1,22 @@
+import pick from 'lodash/pick';
+
+import {useLocation} from 'sentry/utils/useLocation';
+import {SpanMetricsFields} from 'sentry/views/starfish/types';
+
+export type ModuleFilters = {
+  [SpanMetricsFields.SPAN_ACTION]?: string;
+  [SpanMetricsFields.SPAN_DOMAIN]?: string;
+  [SpanMetricsFields.SPAN_GROUP]?: string;
+  [SpanMetricsFields.SPAN_OP]?: string;
+};
+
+export const useModuleFilters = () => {
+  const location = useLocation<ModuleFilters>();
+
+  return pick(location.query, [
+    SpanMetricsFields.SPAN_ACTION,
+    SpanMetricsFields.SPAN_DOMAIN,
+    SpanMetricsFields.SPAN_OP,
+    SpanMetricsFields.SPAN_GROUP,
+  ]);
+};

+ 43 - 0
static/app/views/starfish/views/spans/useModuleSort.ts

@@ -0,0 +1,43 @@
+import {fromSorts} from 'sentry/utils/discover/eventView';
+import type {Sort} from 'sentry/utils/discover/fields';
+import {useLocation} from 'sentry/utils/useLocation';
+import {SpanMetricsFields, StarfishFunctions} from 'sentry/views/starfish/types';
+import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
+
+type Query = {
+  [QueryParameterNames.SORT]: string;
+};
+
+const SORTABLE_FIELDS = [
+  `avg(${SpanMetricsFields.SPAN_SELF_TIME})`,
+  `${StarfishFunctions.HTTP_ERROR_COUNT}()`,
+  `${StarfishFunctions.SPM}()`,
+  `${StarfishFunctions.TIME_SPENT_PERCENTAGE}()`,
+  `${StarfishFunctions.TIME_SPENT_PERCENTAGE}(local)`,
+] as const;
+
+export type ValidSort = Sort & {
+  field: (typeof SORTABLE_FIELDS)[number];
+};
+
+/**
+ * Parses a `Sort` object from the URL. In case of multiple specified sorts
+ * picks the first one, since span module UIs only support one sort at a time.
+ */
+export function useModuleSort() {
+  const location = useLocation<Query>();
+
+  return (
+    fromSorts(location.query[QueryParameterNames.SORT]).filter(isAValidSort)[0] ??
+    DEFAULT_SORT
+  );
+}
+
+const DEFAULT_SORT: Sort = {
+  kind: 'desc',
+  field: `${StarfishFunctions.TIME_SPENT_PERCENTAGE}()`,
+};
+
+function isAValidSort(sort: Sort): sort is ValidSort {
+  return (SORTABLE_FIELDS as unknown as string[]).includes(sort.field);
+}