Browse Source

feat(webvitals): Escape webvital query filters using MutableSearch (#68220)

Transaction names with asterisks break queries in the page overview.
This fix escapes asterisks in transaction names before querying.
edwardgou-sentry 11 months ago
parent
commit
1fc699564e

+ 53 - 0
static/app/views/performance/browser/webVitals/pageOverview.spec.tsx

@@ -131,4 +131,57 @@ describe('PageOverview', function () {
       )
     );
   });
+
+  it('escapes transaction name before querying discover', async () => {
+    const organizationWithInp = OrganizationFixture({
+      features: [
+        'starfish-browser-webvitals',
+        'performance-database-view',
+        'starfish-browser-webvitals-replace-fid-with-inp',
+      ],
+    });
+    jest.mocked(useOrganization).mockReturnValue(organizationWithInp);
+    jest.mocked(useLocation).mockReturnValue({
+      pathname: '',
+      search: '',
+      query: {
+        useStoredScores: 'true',
+        transaction: '/page-with-a-*/',
+        type: 'interactions',
+      },
+      hash: '',
+      state: undefined,
+      action: 'PUSH',
+      key: '',
+    });
+    render(<PageOverview />);
+    await waitFor(() =>
+      expect(eventsMock).toHaveBeenCalledWith(
+        '/organizations/org-slug/events/',
+        expect.objectContaining({
+          query: expect.objectContaining({
+            dataset: 'spansIndexed',
+            field: [
+              'measurements.inp',
+              'measurements.score.inp',
+              'measurements.score.weight.inp',
+              'measurements.score.total',
+              'span_id',
+              'timestamp',
+              'profile_id',
+              'replay.id',
+              'user',
+              'origin.transaction',
+              'project',
+              'browser.name',
+              'span.self_time',
+              'span.description',
+            ],
+            query:
+              'span.op:ui.interaction.click measurements.score.weight.inp:>0 origin.transaction:"/page-with-a-\\*/"',
+          }),
+        })
+      )
+    );
+  });
 });

+ 12 - 3
static/app/views/performance/browser/webVitals/utils/queries/rawWebVitalsQueries/useProjectRawWebVitalsQuery.tsx

@@ -2,6 +2,7 @@ import type {Tag} from 'sentry/types';
 import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
 import EventView from 'sentry/utils/discover/eventView';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -16,6 +17,13 @@ export const useProjectRawWebVitalsQuery = ({transaction, tag, dataset}: Props =
   const organization = useOrganization();
   const pageFilters = usePageFilters();
   const location = useLocation();
+  const search = new MutableSearch([]);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
+  if (tag) {
+    search.addFilterValue(tag.key, tag.name);
+  }
 
   const projectEventView = EventView.fromNewQueryWithPageFilters(
     {
@@ -38,9 +46,10 @@ export const useProjectRawWebVitalsQuery = ({transaction, tag, dataset}: Props =
       query: [
         'transaction.op:[pageload,""]',
         'span.op:[ui.interaction.click,""]',
-        ...(transaction ? [`transaction:"${transaction}"`] : []),
-        ...(tag ? [`{tag.key}:"${tag.name}"`] : []),
-      ].join(' '),
+        search.formatString(),
+      ]
+        .join(' ')
+        .trim(),
       version: 2,
       dataset: dataset ?? DiscoverDatasets.METRICS,
     },

+ 9 - 5
static/app/views/performance/browser/webVitals/utils/queries/rawWebVitalsQueries/useProjectRawWebVitalsTimeseriesQuery.tsx

@@ -6,6 +6,7 @@ import EventView from 'sentry/utils/discover/eventView';
 import type {DiscoverQueryProps} from 'sentry/utils/discover/genericDiscoverQuery';
 import {useGenericDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -35,6 +36,13 @@ export const useProjectRawWebVitalsTimeseriesQuery = ({
   const pageFilters = usePageFilters();
   const location = useLocation();
   const organization = useOrganization();
+  const search = new MutableSearch(['transaction.op:pageload']);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
+  if (tag) {
+    search.addFilterValue(tag.key, tag.name);
+  }
   const projectTimeSeriesEventView = EventView.fromNewQueryWithPageFilters(
     {
       yAxis: [
@@ -46,11 +54,7 @@ export const useProjectRawWebVitalsTimeseriesQuery = ({
         'count()',
       ],
       name: 'Web Vitals',
-      query: [
-        'transaction.op:pageload',
-        transaction ? `transaction:"${transaction}"` : '',
-        tag ? `${tag.key}:"${tag.name}"` : '',
-      ].join(' '),
+      query: search.formatString(),
       version: 2,
       fields: [],
       interval: getInterval(pageFilters.selection.datetime, 'low'),

+ 9 - 2
static/app/views/performance/browser/webVitals/utils/queries/rawWebVitalsQueries/useProjectRawWebVitalsValuesTimeseriesQuery.tsx

@@ -6,6 +6,7 @@ import EventView from 'sentry/utils/discover/eventView';
 import type {DiscoverQueryProps} from 'sentry/utils/discover/genericDiscoverQuery';
 import {useGenericDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -22,6 +23,10 @@ export const useProjectRawWebVitalsValuesTimeseriesQuery = ({
   const pageFilters = usePageFilters();
   const location = useLocation();
   const organization = useOrganization();
+  const search = new MutableSearch([]);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
   const projectTimeSeriesEventView = EventView.fromNewQueryWithPageFilters(
     {
       yAxis: [
@@ -38,8 +43,10 @@ export const useProjectRawWebVitalsValuesTimeseriesQuery = ({
       query: [
         'transaction.op:[pageload,""]',
         'span.op:[ui.interaction.click,""]',
-        ...(transaction ? [`transaction:"${transaction}"`] : []),
-      ].join(' '),
+        search.formatString(),
+      ]
+        .join(' ')
+        .trim(),
       version: 2,
       fields: [],
       interval: getInterval(pageFilters.selection.datetime, 'low'),

+ 4 - 5
static/app/views/performance/browser/webVitals/utils/queries/rawWebVitalsQueries/useTransactionRawSamplesWebVitalsQuery.tsx

@@ -2,6 +2,7 @@ import type {ReactText} from 'react';
 
 import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
 import EventView from 'sentry/utils/discover/eventView';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -73,11 +74,9 @@ export const useTransactionRawSamplesWebVitalsQuery = ({
         'project',
       ],
       name: 'Web Vitals',
-      query: [
-        'transaction.op:pageload',
-        `transaction:"${transaction}"`,
-        ...(query ? [query] : []),
-      ].join(' '),
+      query: new MutableSearch(['transaction.op:pageload', ...(query ? [query] : [])])
+        .addStringFilter(`transaction:"${transaction}"`)
+        .formatString(),
       orderby: mapWebVitalToOrderBy(orderBy) ?? withProfiles ? '-profile.id' : undefined,
       version: 2,
     },

+ 9 - 5
static/app/views/performance/browser/webVitals/utils/queries/rawWebVitalsQueries/useTransactionRawWebVitalsQuery.tsx

@@ -2,6 +2,7 @@ import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
 import EventView from 'sentry/utils/discover/eventView';
 import type {Sort} from 'sentry/utils/discover/fields';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -32,6 +33,13 @@ export const useTransactionRawWebVitalsQuery = ({
 
   const sort = useWebVitalsSort({sortName, defaultSort});
 
+  const search = new MutableSearch([
+    'transaction.op:pageload',
+    ...(query ? [query] : []),
+  ]);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
   const eventView = EventView.fromNewQueryWithPageFilters(
     {
       fields: [
@@ -50,11 +58,7 @@ export const useTransactionRawWebVitalsQuery = ({
         'count()',
       ],
       name: 'Web Vitals',
-      query: [
-        'transaction.op:pageload',
-        ...(transaction ? [`transaction:"${transaction}"`] : []),
-        ...(query ? [query] : []),
-      ].join(' '),
+      query: search.formatString(),
       version: 2,
       dataset: DiscoverDatasets.METRICS,
     },

+ 12 - 3
static/app/views/performance/browser/webVitals/utils/queries/storedScoreQueries/useProjectWebVitalsScoresQuery.tsx

@@ -2,6 +2,7 @@ import type {Tag} from 'sentry/types';
 import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
 import EventView from 'sentry/utils/discover/eventView';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -29,6 +30,13 @@ export const useProjectWebVitalsScoresQuery = ({
   const shouldReplaceFidWithInp = useReplaceFidWithInpSetting();
   const inpOrFid = shouldReplaceFidWithInp ? 'inp' : 'fid';
 
+  const search = new MutableSearch([]);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
+  if (tag) {
+    search.addFilterValue(tag.key, tag.name);
+  }
   const projectEventView = EventView.fromNewQueryWithPageFilters(
     {
       fields: [
@@ -58,9 +66,10 @@ export const useProjectWebVitalsScoresQuery = ({
       query: [
         'transaction.op:[pageload,""]',
         'span.op:[ui.interaction.click,""]',
-        ...(transaction ? [`transaction:"${transaction}"`] : []),
-        ...(tag ? [`${tag.key}:"${tag.name}"`] : []),
-      ].join(' '),
+        search.formatString(),
+      ]
+        .join(' ')
+        .trim(),
       version: 2,
       dataset: dataset ?? DiscoverDatasets.METRICS,
     },

+ 12 - 4
static/app/views/performance/browser/webVitals/utils/queries/storedScoreQueries/useProjectWebVitalsScoresTimeseriesQuery.tsx

@@ -6,6 +6,7 @@ import EventView from 'sentry/utils/discover/eventView';
 import type {DiscoverQueryProps} from 'sentry/utils/discover/genericDiscoverQuery';
 import {useGenericDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -35,6 +36,13 @@ export const useProjectWebVitalsScoresTimeseriesQuery = ({
   const pageFilters = usePageFilters();
   const location = useLocation();
   const organization = useOrganization();
+  const search = new MutableSearch([
+    'has:measurements.score.total',
+    ...(tag ? [`${tag.key}:"${tag.name}"`] : []),
+  ]);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
   const projectTimeSeriesEventView = EventView.fromNewQueryWithPageFilters(
     {
       yAxis: [
@@ -56,10 +64,10 @@ export const useProjectWebVitalsScoresTimeseriesQuery = ({
       query: [
         'transaction.op:[pageload,""]',
         'span.op:[ui.interaction.click,""]',
-        'has:measurements.score.total',
-        ...(transaction ? [`transaction:"${transaction}"`] : []),
-        ...(tag ? [`${tag.key}:"${tag.name}"`] : []),
-      ].join(' '),
+        search.formatString(),
+      ]
+        .join(' ')
+        .trim(),
       version: 2,
       fields: [],
       interval: getInterval(pageFilters.selection.datetime, 'low'),

+ 5 - 3
static/app/views/performance/browser/webVitals/utils/queries/storedScoreQueries/useTransactionSamplesWebVitalsScoresQuery.tsx

@@ -2,6 +2,7 @@ import type {ReactText} from 'react';
 
 import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
 import EventView from 'sentry/utils/discover/eventView';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -78,12 +79,13 @@ export const useTransactionSamplesWebVitalsScoresQuery = ({
           : []),
       ],
       name: 'Web Vitals',
-      query: [
+      query: new MutableSearch([
         'transaction.op:pageload',
-        `transaction:"${transaction}"`,
         'has:measurements.score.total',
         ...(query ? [query] : []),
-      ].join(' '),
+      ])
+        .addStringFilter(`transaction:"${transaction}"`)
+        .formatString(),
       orderby: mapWebVitalToOrderBy(orderBy) ?? withProfiles ? '-profile.id' : undefined,
       version: 2,
     },

+ 12 - 4
static/app/views/performance/browser/webVitals/utils/queries/storedScoreQueries/useTransactionWebVitalsScoresQuery.tsx

@@ -2,6 +2,7 @@ import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
 import EventView from 'sentry/utils/discover/eventView';
 import type {Sort} from 'sentry/utils/discover/fields';
 import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePageFilters from 'sentry/utils/usePageFilters';
@@ -37,6 +38,13 @@ export const useTransactionWebVitalsScoresQuery = ({
 
   const sort = useWebVitalsSort({sortName, defaultSort});
 
+  const search = new MutableSearch([
+    'avg(measurements.score.total):>=0',
+    ...(query ? [query] : []),
+  ]);
+  if (transaction) {
+    search.addFilterValue('transaction', transaction);
+  }
   const eventView = EventView.fromNewQueryWithPageFilters(
     {
       fields: [
@@ -64,10 +72,10 @@ export const useTransactionWebVitalsScoresQuery = ({
       query: [
         'transaction.op:[pageload,""]',
         'span.op:[ui.interaction.click,""]',
-        'avg(measurements.score.total):>=0',
-        ...(transaction ? [`transaction:"${transaction}"`] : []),
-        ...(query ? [query] : []),
-      ].join(' '),
+        search.formatString(),
+      ]
+        .join(' ')
+        .trim(),
       version: 2,
       dataset: DiscoverDatasets.METRICS,
     },