Browse Source

feat(insights): implement geo region selector in web vitals and assets (#76185)

Work toward #75230 

Adds geo selector in the web vitals and resource module and ensure all
queries gets the filter applied

NOTE: not all the metrics are properly tagged yet, so web vitals doesn't
work entierely yet, and the resource module doesn't have geo data for
resource size metrics. Its all behind a feature flag, so this not an
issue that customers can see.
Dominik Buszowiecki 6 months ago
parent
commit
3465d8462b

+ 4 - 0
static/app/views/insights/browser/common/queries/useResourcesQuery.ts

@@ -25,6 +25,7 @@ const {
   HTTP_RESPONSE_CONTENT_LENGTH,
   PROJECT_ID,
   FILE_EXTENSION,
+  USER_GEO_SUBREGION,
 } = SpanMetricsField;
 
 const {TIME_SPENT_PERCENTAGE} = SpanFunction;
@@ -49,6 +50,9 @@ export const getResourcesEventViewQuery = (
     ...(resourceFilters.transaction
       ? [`transaction:"${resourceFilters.transaction}"`]
       : []),
+    ...(resourceFilters[USER_GEO_SUBREGION]
+      ? [`user.geo.subregion:[${resourceFilters[USER_GEO_SUBREGION]}]`]
+      : []),
     ...getDomainFilter(resourceFilters[SPAN_DOMAIN]),
     ...(resourceFilters[RESOURCE_RENDER_BLOCKING_STATUS]
       ? [

+ 5 - 0
static/app/views/insights/browser/resources/components/charts/resourceSummaryCharts.tsx

@@ -45,6 +45,11 @@ function ResourceSummaryCharts(props: {groupId: string}) {
                   filters[RESOURCE_RENDER_BLOCKING_STATUS],
               }
             : {}),
+          ...(filters[SpanMetricsField.USER_GEO_SUBREGION]
+            ? {
+                [SpanMetricsField.USER_GEO_SUBREGION]: `[${filters[SpanMetricsField.USER_GEO_SUBREGION].join(',')}]`,
+              }
+            : {}),
         }),
         yAxis: [
           `spm()`,

+ 7 - 1
static/app/views/insights/browser/resources/components/resourceView.tsx

@@ -36,6 +36,7 @@ const {
   SPAN_DOMAIN,
   TRANSACTION,
   RESOURCE_RENDER_BLOCKING_STATUS,
+  USER_GEO_SUBREGION,
 } = BrowserStarfishFields;
 
 type Option = {
@@ -52,7 +53,12 @@ function ResourceView() {
     ...(filters[SPAN_DOMAIN] ? {[SPAN_DOMAIN]: filters[SPAN_DOMAIN]} : {}),
   };
 
-  const extraQuery = getResourceTypeFilter(undefined, DEFAULT_RESOURCE_TYPES);
+  const extraQuery = [
+    ...getResourceTypeFilter(undefined, DEFAULT_RESOURCE_TYPES),
+    ...(filters[USER_GEO_SUBREGION]
+      ? [`user.geo.subregion:[${filters[USER_GEO_SUBREGION].join(',')}]`]
+      : []),
+  ];
 
   return (
     <Fragment>

+ 2 - 0
static/app/views/insights/browser/resources/components/tables/resourceSummaryTable.tsx

@@ -34,6 +34,7 @@ const {
   SPAN_SELF_TIME,
   HTTP_RESPONSE_CONTENT_LENGTH,
   TRANSACTION,
+  USER_GEO_SUBREGION,
 } = SpanMetricsField;
 
 type Row = {
@@ -55,6 +56,7 @@ function ResourceSummaryTable() {
   const {data, isLoading, pageLinks} = useResourcePagesQuery(groupId, {
     sort,
     cursor,
+    subregions: filters[USER_GEO_SUBREGION],
     renderBlockingStatus: filters[RESOURCE_RENDER_BLOCKING_STATUS],
   });
 

+ 8 - 1
static/app/views/insights/browser/resources/components/tables/resourceTable.tsx

@@ -25,6 +25,7 @@ import {
   RESOURCE_THROUGHPUT_UNIT,
 } from 'sentry/views/insights/browser/resources/settings';
 import {ResourceSpanOps} from 'sentry/views/insights/browser/resources/types';
+import {useResourceModuleFilters} from 'sentry/views/insights/browser/resources/utils/useResourceFilters';
 import type {ValidSort} from 'sentry/views/insights/browser/resources/utils/useResourceSort';
 import {DurationCell} from 'sentry/views/insights/common/components/tableCells/durationCell';
 import {renderHeadCell} from 'sentry/views/insights/common/components/tableCells/renderHeadCell';
@@ -79,6 +80,7 @@ function ResourceTable({sort, defaultResourceTypes}: Props) {
   const location = useLocation();
   const organization = useOrganization();
   const cursor = decodeScalar(location.query?.[QueryParameterNames.SPANS_CURSOR]);
+  const filters = useResourceModuleFilters();
   const {setPageInfo, pageAlert} = usePageAlert();
 
   const {data, isLoading, pageLinks} = useResourcesQuery({
@@ -130,7 +132,11 @@ function ResourceTable({sort, defaultResourceTypes}: Props) {
 
     if (key === SPAN_DESCRIPTION) {
       const fileExtension = row[SPAN_DESCRIPTION].split('.').pop() || '';
-
+      const extraLinkQueryParams = {};
+      if (filters[SpanMetricsField.USER_GEO_SUBREGION]) {
+        extraLinkQueryParams[SpanMetricsField.USER_GEO_SUBREGION] =
+          filters[SpanMetricsField.USER_GEO_SUBREGION];
+      }
       return (
         <DescriptionWrapper>
           <ResourceIcon fileExtension={fileExtension} spanOp={row[SPAN_OP]} />
@@ -140,6 +146,7 @@ function ResourceTable({sort, defaultResourceTypes}: Props) {
             spanOp={row[SPAN_OP]}
             description={row[SPAN_DESCRIPTION]}
             group={row[SPAN_GROUP]}
+            extraLinkQueryParams={extraLinkQueryParams}
           />
         </DescriptionWrapper>
       );

+ 11 - 2
static/app/views/insights/browser/resources/queries/useResourcePageQuery.ts

@@ -1,6 +1,6 @@
 import type {Sort} from 'sentry/utils/discover/fields';
 import {useSpanTransactionMetrics} from 'sentry/views/insights/common/queries/useSpanTransactionMetrics';
-import {SpanMetricsField} from 'sentry/views/insights/types';
+import {SpanMetricsField, type SubregionCode} from 'sentry/views/insights/types';
 
 const {HTTP_RESPONSE_CONTENT_LENGTH, RESOURCE_RENDER_BLOCKING_STATUS} = SpanMetricsField;
 
@@ -9,8 +9,14 @@ export const useResourcePagesQuery = (
   {
     sort,
     cursor,
+    subregions,
     renderBlockingStatus,
-  }: {sort: Sort; cursor?: string; renderBlockingStatus?: string}
+  }: {
+    sort: Sort;
+    cursor?: string;
+    renderBlockingStatus?: string;
+    subregions?: SubregionCode[];
+  }
 ) => {
   return useSpanTransactionMetrics(
     {
@@ -18,6 +24,9 @@ export const useResourcePagesQuery = (
       ...(renderBlockingStatus
         ? {[RESOURCE_RENDER_BLOCKING_STATUS]: renderBlockingStatus}
         : {}),
+      ...(subregions
+        ? {[SpanMetricsField.USER_GEO_SUBREGION]: `[${subregions.join(',')}]`}
+        : {}),
     },
     [sort],
     cursor,

+ 15 - 1
static/app/views/insights/browser/resources/utils/useResourceFilters.ts

@@ -1,8 +1,11 @@
 import pick from 'lodash/pick';
 
+import {decodeList} from 'sentry/utils/queryString';
 import {useLocation} from 'sentry/utils/useLocation';
 import type {ResourceSpanOps} from 'sentry/views/insights/browser/resources/types';
+import type {SubregionCode} from 'sentry/views/insights/types';
 
+// TODO - we should probably just use SpanMetricsField here
 export enum BrowserStarfishFields {
   SPAN_OP = 'span.op',
   TRANSACTION = 'transaction',
@@ -10,6 +13,7 @@ export enum BrowserStarfishFields {
   GROUP_ID = 'groupId',
   DESCRIPTION = 'description',
   RESOURCE_RENDER_BLOCKING_STATUS = 'resource.render_blocking_status',
+  USER_GEO_SUBREGION = 'user.geo.subregion',
 }
 
 export type ModuleFilters = {
@@ -22,12 +26,13 @@ export type ModuleFilters = {
   [BrowserStarfishFields.SPAN_OP]?: ResourceSpanOps;
   [BrowserStarfishFields.TRANSACTION]?: string;
   [BrowserStarfishFields.SPAN_DOMAIN]?: string;
+  [BrowserStarfishFields.USER_GEO_SUBREGION]?: SubregionCode[];
 };
 
 export const useResourceModuleFilters = () => {
   const location = useLocation<ModuleFilters>();
 
-  return pick(location.query, [
+  const filters = pick(location.query, [
     BrowserStarfishFields.SPAN_DOMAIN,
     BrowserStarfishFields.SPAN_OP,
     BrowserStarfishFields.TRANSACTION,
@@ -35,4 +40,13 @@ export const useResourceModuleFilters = () => {
     BrowserStarfishFields.DESCRIPTION,
     BrowserStarfishFields.RESOURCE_RENDER_BLOCKING_STATUS,
   ]);
+
+  const subregions = decodeList(
+    location.query[BrowserStarfishFields.USER_GEO_SUBREGION]
+  ) as SubregionCode[];
+  if (subregions.length) {
+    filters[BrowserStarfishFields.USER_GEO_SUBREGION] = subregions;
+  }
+
+  return filters;
 };

+ 8 - 0
static/app/views/insights/browser/resources/views/resourceSummaryPage.tsx

@@ -29,6 +29,7 @@ import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon';
 import {useSpanMetrics} from 'sentry/views/insights/common/queries/useDiscover';
 import {useModuleBreadcrumbs} from 'sentry/views/insights/common/utils/useModuleBreadcrumbs';
 import {useModuleURL} from 'sentry/views/insights/common/utils/useModuleURL';
+import SubregionSelector from 'sentry/views/insights/common/views/spans/selectors/subregionSelector';
 import {SampleList} from 'sentry/views/insights/common/views/spanSummaryPage/sampleList';
 import {ModuleName, SpanMetricsField} from 'sentry/views/insights/types';
 import {TraceViewSources} from 'sentry/views/performance/newTraceDetails/traceMetadataHeader';
@@ -55,6 +56,11 @@ function ResourceSummary() {
     {
       search: MutableSearch.fromQueryObject({
         'span.group': groupId,
+        ...(filters[SpanMetricsField.USER_GEO_SUBREGION]
+          ? {
+              [SpanMetricsField.USER_GEO_SUBREGION]: `[${filters[SpanMetricsField.USER_GEO_SUBREGION].join(',')}]`,
+            }
+          : {}),
       }),
       fields: [
         `avg(${SPAN_SELF_TIME})`,
@@ -123,6 +129,7 @@ function ResourceSummary() {
                   <RenderBlockingSelector
                     value={filters[RESOURCE_RENDER_BLOCKING_STATUS] || ''}
                   />
+                  <SubregionSelector />
                 </ToolRibbon>
                 <ResourceInfo
                   isLoading={isLoading}
@@ -154,6 +161,7 @@ function ResourceSummary() {
             <ModuleLayout.Full>
               <SampleList
                 transactionRoute={webVitalsModuleURL}
+                subregions={filters[SpanMetricsField.USER_GEO_SUBREGION]}
                 groupId={groupId}
                 moduleName={ModuleName.RESOURCE}
                 transactionName={transaction as string}

+ 14 - 10
static/app/views/insights/browser/resources/views/resourcesLandingPage.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, {Fragment} from 'react';
 import styled from '@emotion/styled';
 
 import {Breadcrumbs} from 'sentry/components/breadcrumbs';
@@ -27,6 +27,7 @@ import {ModulesOnboarding} from 'sentry/views/insights/common/components/modules
 import {ToolRibbon} from 'sentry/views/insights/common/components/ribbon';
 import {useModuleBreadcrumbs} from 'sentry/views/insights/common/utils/useModuleBreadcrumbs';
 import {DomainSelector} from 'sentry/views/insights/common/views/spans/selectors/domainSelector';
+import SubregionSelector from 'sentry/views/insights/common/views/spans/selectors/subregionSelector';
 import {ModuleName} from 'sentry/views/insights/types';
 
 const {SPAN_OP, SPAN_DOMAIN} = BrowserStarfishFields;
@@ -64,15 +65,18 @@ function ResourcesLandingPage() {
                 <ModulePageFilterBar
                   moduleName={ModuleName.RESOURCE}
                   extraFilters={
-                    <DomainSelector
-                      moduleName={ModuleName.RESOURCE}
-                      emptyOptionLocation="top"
-                      value={filters[SPAN_DOMAIN] || ''}
-                      additionalQuery={[
-                        ...DEFAULT_RESOURCE_FILTERS,
-                        `${SPAN_OP}:[${DEFAULT_RESOURCE_TYPES.join(',')}]`,
-                      ]}
-                    />
+                    <Fragment>
+                      <DomainSelector
+                        moduleName={ModuleName.RESOURCE}
+                        emptyOptionLocation="top"
+                        value={filters[SPAN_DOMAIN] || ''}
+                        additionalQuery={[
+                          ...DEFAULT_RESOURCE_FILTERS,
+                          `${SPAN_OP}:[${DEFAULT_RESOURCE_TYPES.join(',')}]`,
+                        ]}
+                      />
+                      <SubregionSelector />
+                    </Fragment>
                   }
                 />
               </ToolRibbon>

+ 8 - 2
static/app/views/insights/browser/webVitals/components/charts/performanceScoreBreakdownChart.tsx

@@ -15,9 +15,11 @@ import {applyStaticWeightsToTimeseries} from 'sentry/views/insights/browser/webV
 import type {BrowserType} from 'sentry/views/insights/browser/webVitals/utils/queryParameterDecoders/browserType';
 import {PERFORMANCE_SCORE_WEIGHTS} from 'sentry/views/insights/browser/webVitals/utils/scoreThresholds';
 import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
+import type {SubregionCode} from 'sentry/views/insights/types';
 
 type Props = {
   browserTypes?: BrowserType[];
+  subregions?: SubregionCode[];
   transaction?: string;
 };
 
@@ -43,14 +45,18 @@ export const formatTimeSeriesResultsToChartData = (
   });
 };
 
-export function PerformanceScoreBreakdownChart({transaction, browserTypes}: Props) {
+export function PerformanceScoreBreakdownChart({
+  transaction,
+  browserTypes,
+  subregions,
+}: Props) {
   const theme = useTheme();
   const segmentColors = [...theme.charts.getColorPalette(3).slice(0, 5)];
 
   const pageFilters = usePageFilters();
 
   const {data: timeseriesData, isLoading: isTimeseriesLoading} =
-    useProjectWebVitalsScoresTimeseriesQuery({transaction, browserTypes});
+    useProjectWebVitalsScoresTimeseriesQuery({transaction, browserTypes, subregions});
 
   const period = pageFilters.selection.datetime.period;
   const performanceScoreSubtext = (period && DEFAULT_RELATIVE_PERIODS[period]) ?? '';

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