Skip to content

Commit e43f77c

Browse files
authored
ref(timeseries): Use /events-timeseries/ in Web Vitals module (#99736)
More work to replace `/events-stats/` usage with the new `/events-timeseries/` endpoint. This PR makes that update in the Web Vitals sections of Insights. There are a few places where the frontend does additional data processing or checking. That code expects output from `/events-stats/` and in most places I opted to just leave it alone, to keep the changes minimal for now. These UIs will be heavily affected by incoming refactoring anyway, so there's not much gain there. The most glaring differences are: 1. Having to iterate through `timeSeries` to find the right one rather than looking by key. IMO this is minor, and is offset by the other cases where the output can be passed straight to charts with no alteration 2. `useFetchSpanTimeSeries` does _not_ guarantee that every requested `yAxis` will be present, it doesn't do any backfilling! Therefore, a bit more error checking is needed while iterating. In practice this isn't a problem, since the server will return the empty time series, but it matters while running specs
1 parent 1923791 commit e43f77c

13 files changed

+155
-90
lines changed

static/app/views/insights/browser/webVitals/components/charts/performanceScoreBreakdownChart.spec.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {OrganizationFixture} from 'sentry-fixture/organization';
22
import {PageFilterStateFixture} from 'sentry-fixture/pageFilters';
3+
import {TimeSeriesFixture} from 'sentry-fixture/timeSeries';
34

45
import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
56

@@ -33,14 +34,18 @@ describe('PerformanceScoreBreakdownChartWidget', () => {
3334
});
3435

3536
eventsStatsMock = MockApiClient.addMockResponse({
36-
url: `/organizations/${organization.slug}/events-stats/`,
37+
url: `/organizations/${organization.slug}/events-timeseries/`,
3738
body: {
38-
'performance_score(measurements.score.lcp)': {
39-
data: [[1743348600, [{count: 0.6106921965623204}]]],
40-
},
41-
'performance_score(measurements.score.fcp)': {
42-
data: [[1743435000, [{count: 0.7397871866098699}]]],
43-
},
39+
timeSeries: [
40+
TimeSeriesFixture({
41+
yAxis: 'performance_score(measurements.score.lcp)',
42+
values: [{timestamp: 1743348600000, value: 0.6106921965623204}],
43+
}),
44+
TimeSeriesFixture({
45+
yAxis: 'performance_score(measurements.score.fcp)',
46+
values: [{timestamp: 1743435000000, value: 0.7397871866098699}],
47+
}),
48+
],
4449
},
4550
});
4651
});
@@ -63,7 +68,7 @@ describe('PerformanceScoreBreakdownChartWidget', () => {
6368
await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
6469

6570
expect(eventsStatsMock).toHaveBeenCalledWith(
66-
'/organizations/org-slug/events-stats/',
71+
'/organizations/org-slug/events-timeseries/',
6772
expect.objectContaining({
6873
method: 'GET',
6974
query: expect.objectContaining({

static/app/views/insights/browser/webVitals/components/charts/webVitalStatusLineChart.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import styled from '@emotion/styled';
22

33
import {space} from 'sentry/styles/space';
4+
import {useFetchSpanTimeSeries} from 'sentry/utils/timeSeries/useFetchEventsTimeSeries';
45
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
56
import type {Plottable} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/plottable';
67
import {Thresholds} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/thresholds';
@@ -18,8 +19,6 @@ import {
1819
} from 'sentry/views/insights/browser/webVitals/utils/scoreThresholds';
1920
// eslint-disable-next-line no-restricted-imports
2021
import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget';
21-
import type {DiscoverSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
22-
import {useSpanSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
2322
import type {SubregionCode} from 'sentry/views/insights/types';
2423
import {SpanFields} from 'sentry/views/insights/types';
2524

@@ -56,24 +55,25 @@ export function WebVitalStatusLineChart({
5655
data: timeseriesData,
5756
isLoading: isTimeseriesLoading,
5857
error: timeseriesError,
59-
} = useSpanSeries(
58+
} = useFetchSpanTimeSeries(
6059
{
61-
search,
60+
query: search,
6261
yAxis: webVital ? [`p75(measurements.${webVital})`] : [],
6362
enabled: !!webVital,
6463
},
6564
referrer
6665
);
6766

68-
const webVitalSeries: DiscoverSeries = webVital
69-
? timeseriesData?.[`p75(measurements.${webVital})`]
70-
: {data: [], meta: {fields: {}, units: {}}, seriesName: ''};
67+
const timeSeries = timeseriesData?.timeSeries || [];
68+
const webVitalTimeSeries = webVital
69+
? timeSeries.find(ts => ts.yAxis === `p75(measurements.${webVital})`)
70+
: undefined;
7171

72-
const includePoorThreshold = webVitalSeries.data?.some(
73-
({value}) => value > webVitalMedian
72+
const includePoorThreshold = webVitalTimeSeries?.values.some(
73+
({value}) => (value || 0) > webVitalMedian
7474
);
75-
const includeMehThreshold = webVitalSeries.data?.some(
76-
({value}) => value >= webVitalP90
75+
const includeMehThreshold = webVitalTimeSeries?.values.some(
76+
({value}) => (value || 0) >= webVitalP90
7777
);
7878

7979
const thresholdsPlottable = new Thresholds({
@@ -98,7 +98,7 @@ export function WebVitalStatusLineChart({
9898
showLegend="never"
9999
isLoading={isTimeseriesLoading}
100100
error={timeseriesError}
101-
series={[webVitalSeries]}
101+
timeSeries={webVitalTimeSeries ? [webVitalTimeSeries] : []}
102102
extraPlottables={extraPlottables}
103103
queryInfo={{
104104
search,

static/app/views/insights/browser/webVitals/components/pageOverviewSidebar.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ describe('PageOverviewSidebar', () => {
4747
});
4848

4949
MockApiClient.addMockResponse({
50-
url: `/organizations/${organization.slug}/events-stats/`,
50+
url: `/organizations/${organization.slug}/events-timeseries/`,
5151
body: {
52-
data: [],
52+
timeSeries: [],
5353
},
5454
});
5555

static/app/views/insights/browser/webVitals/components/pageOverviewSidebar.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,13 @@ export function PageOverviewSidebar({
8787

8888
const shouldDoublePeriod = false;
8989

90+
const countTimeSeries = data?.timeSeries?.find(ts => ts.yAxis === 'count()');
91+
const countData = countTimeSeries
92+
? countTimeSeries.values.map(v => ({name: v.timestamp, value: v.value || 0}))
93+
: [];
94+
9095
const {countDiff, currentSeries, currentCount, initialCount} = processSeriesData(
91-
data['count()'].data,
96+
countData,
9297
isLoading,
9398
pageFilters.selection.datetime,
9499
shouldDoublePeriod
@@ -101,13 +106,20 @@ export function PageOverviewSidebar({
101106
},
102107
];
103108

109+
const inpTimeSeries = data?.timeSeries?.find(
110+
ts => ts.yAxis === 'count_scores(measurements.score.inp)'
111+
);
112+
const inpData = inpTimeSeries
113+
? inpTimeSeries.values.map(v => ({name: v.timestamp, value: v.value || 0}))
114+
: [];
115+
104116
const {
105117
countDiff: inpCountDiff,
106118
currentSeries: currentInpSeries,
107119
currentCount: currentInpCount,
108120
initialCount: initialInpCount,
109121
} = processSeriesData(
110-
data['count_scores(measurements.score.inp)'].data,
122+
inpData,
111123
isLoading,
112124
pageFilters.selection.datetime,
113125
shouldDoublePeriod

static/app/views/insights/browser/webVitals/components/pageOverviewWebVitalsDetailPanel.spec.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,9 @@ describe('PageOverviewWebVitalsDetailPanel', () => {
6060
});
6161

6262
MockApiClient.addMockResponse({
63-
url: `/organizations/${organization.slug}/events-stats/`,
63+
url: `/organizations/${organization.slug}/events-timeseries/`,
6464
body: {
65-
data: [
66-
[1543449600, [20, 12]],
67-
[1543449601, [10, 5]],
68-
],
65+
timeSeries: [],
6966
},
7067
});
7168
});

static/app/views/insights/browser/webVitals/components/webVitalsDetailPanel.spec.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@ describe('WebVitalsDetailPanel', () => {
3535
},
3636
});
3737
eventsStatsMock = MockApiClient.addMockResponse({
38-
url: `/organizations/${organization.slug}/events-stats/`,
38+
url: `/organizations/${organization.slug}/events-timeseries/`,
3939
body: {
40-
data: [
41-
[1543449600, [20, 12]],
42-
[1543449601, [10, 5]],
43-
],
40+
timeSeries: [],
4441
},
4542
});
4643
});

static/app/views/insights/browser/webVitals/queries/rawWebVitalsQueries/useProjectRawWebVitalsValuesTimeseriesQuery.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {getInterval} from 'sentry/components/charts/utils';
2+
import {useFetchSpanTimeSeries} from 'sentry/utils/timeSeries/useFetchEventsTimeSeries';
23
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
34
import usePageFilters from 'sentry/utils/usePageFilters';
45
import {Referrer} from 'sentry/views/insights/browser/webVitals/referrers';
56
import {DEFAULT_QUERY_FILTER} from 'sentry/views/insights/browser/webVitals/settings';
67
import type {BrowserType} from 'sentry/views/insights/browser/webVitals/utils/queryParameterDecoders/browserType';
7-
import {useSpanSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
88
import {SpanFields, type SubregionCode} from 'sentry/views/insights/types';
99

1010
type Props = {
@@ -31,9 +31,9 @@ export const useProjectRawWebVitalsValuesTimeseriesQuery = ({
3131
search.addDisjunctionFilterValues(SpanFields.USER_GEO_SUBREGION, subregions);
3232
}
3333

34-
const result = useSpanSeries(
34+
const result = useFetchSpanTimeSeries(
3535
{
36-
search: [DEFAULT_QUERY_FILTER, search.formatString()].join(' ').trim(),
36+
query: [DEFAULT_QUERY_FILTER, search.formatString()].join(' ').trim(),
3737
interval: getInterval(pageFilters.selection.datetime, 'spans-low'),
3838
yAxis: [
3939
'p75(measurements.lcp)',

static/app/views/insights/browser/webVitals/queries/storedScoreQueries/useProjectWebVitalsScoresTimeseriesQuery.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type {SeriesDataUnit} from 'sentry/types/echarts';
22
import type {Tag} from 'sentry/types/group';
3+
import {useFetchSpanTimeSeries} from 'sentry/utils/timeSeries/useFetchEventsTimeSeries';
34
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
45
import {Referrer} from 'sentry/views/insights/browser/webVitals/referrers';
56
import {DEFAULT_QUERY_FILTER} from 'sentry/views/insights/browser/webVitals/settings';
67
import type {BrowserType} from 'sentry/views/insights/browser/webVitals/utils/queryParameterDecoders/browserType';
7-
import {useSpanSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
88
import {SpanFields, type SubregionCode} from 'sentry/views/insights/types';
99

1010
type Props = {
@@ -45,9 +45,9 @@ export const useProjectWebVitalsScoresTimeseriesQuery = ({
4545
search.addDisjunctionFilterValues(SpanFields.BROWSER_NAME, browserTypes);
4646
}
4747

48-
const result = useSpanSeries(
48+
const result = useFetchSpanTimeSeries(
4949
{
50-
search: [DEFAULT_QUERY_FILTER, search.formatString()].join(' ').trim(),
50+
query: [DEFAULT_QUERY_FILTER, search.formatString()].join(' ').trim(),
5151
yAxis: [
5252
'performance_score(measurements.score.lcp)',
5353
'performance_score(measurements.score.fcp)',
@@ -63,13 +63,22 @@ export const useProjectWebVitalsScoresTimeseriesQuery = ({
6363
const multiplyBy100 = (data: SeriesDataUnit[]) =>
6464
data.map(({name, value}) => ({name, value: value * 100}));
6565

66+
const timeSeries = result.data?.timeSeries || [];
67+
68+
const getSeriesData = (yAxis: string) => {
69+
const series = timeSeries.find(ts => ts.yAxis === yAxis);
70+
return series
71+
? series.values.map(v => ({name: v.timestamp, value: v.value || 0}))
72+
: [];
73+
};
74+
6675
const data: WebVitalsScoreBreakdown = {
67-
lcp: multiplyBy100(result.data['performance_score(measurements.score.lcp)'].data),
68-
fcp: multiplyBy100(result.data['performance_score(measurements.score.fcp)'].data),
69-
cls: multiplyBy100(result.data['performance_score(measurements.score.cls)'].data),
70-
ttfb: multiplyBy100(result.data['performance_score(measurements.score.ttfb)'].data),
71-
inp: multiplyBy100(result.data['performance_score(measurements.score.inp)'].data),
72-
total: result.data['count()'].data,
76+
lcp: multiplyBy100(getSeriesData('performance_score(measurements.score.lcp)')),
77+
fcp: multiplyBy100(getSeriesData('performance_score(measurements.score.fcp)')),
78+
cls: multiplyBy100(getSeriesData('performance_score(measurements.score.cls)')),
79+
ttfb: multiplyBy100(getSeriesData('performance_score(measurements.score.ttfb)')),
80+
inp: multiplyBy100(getSeriesData('performance_score(measurements.score.inp)')),
81+
total: getSeriesData('count()'),
7382
};
7483

7584
return {...result, data};

static/app/views/insights/browser/webVitals/views/pageOverview.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('PageOverview', () => {
6565
},
6666
});
6767
MockApiClient.addMockResponse({
68-
url: `/organizations/${organization.slug}/events-stats/`,
68+
url: `/organizations/${organization.slug}/events-timeseries/`,
6969
body: {},
7070
});
7171
MockApiClient.addMockResponse({

static/app/views/insights/browser/webVitals/views/webVitalsLandingPage.spec.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {OrganizationFixture} from 'sentry-fixture/organization';
22
import {PageFilterStateFixture} from 'sentry-fixture/pageFilters';
33
import {ProjectFixture} from 'sentry-fixture/project';
4+
import {TimeSeriesFixture} from 'sentry-fixture/timeSeries';
45

56
import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
67

@@ -49,14 +50,28 @@ describe('WebVitalsLandingPage', () => {
4950
});
5051

5152
MockApiClient.addMockResponse({
52-
url: `/organizations/${organization.slug}/events-stats/`,
53+
url: `/organizations/${organization.slug}/events-timeseries/`,
5354
body: {
54-
'performance_score(measurements.score.lcp)': {
55-
data: [[1743348600, [{count: 0.6106921965623204}]]],
56-
},
57-
'performance_score(measurements.score.fcp)': {
58-
data: [[1743435000, [{count: 0.7397871866098699}]]],
59-
},
55+
timeSeries: [
56+
TimeSeriesFixture({
57+
yAxis: 'performance_score(measurements.score.lcp)',
58+
values: [
59+
{
60+
timestamp: 1743348600000,
61+
value: 0.6106921965623204,
62+
},
63+
],
64+
}),
65+
TimeSeriesFixture({
66+
yAxis: 'performance_score(measurements.score.fcp)',
67+
values: [
68+
{
69+
timestamp: 1743348600000,
70+
value: 0.7397871866098699,
71+
},
72+
],
73+
}),
74+
],
6075
},
6176
});
6277
});

0 commit comments

Comments
 (0)