Skip to content

Commit d6fe4d4

Browse files
authored
feat(metrics): Add metrics to series transformer (#31783)
1 parent 4b5fbf6 commit d6fe4d4

File tree

6 files changed

+475
-2
lines changed

6 files changed

+475
-2
lines changed

static/app/utils/metrics/metricsRequest.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import {shouldFetchPreviousPeriod} from 'sentry/components/charts/utils';
99
import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
1010
import {t} from 'sentry/locale';
1111
import {MetricsApiResponse} from 'sentry/types';
12+
import {Series} from 'sentry/types/echarts';
1213
import {getPeriod} from 'sentry/utils/getPeriod';
1314

1415
import {TableData} from '../discover/discoverQuery';
1516

17+
import {transformMetricsResponseToSeries} from './transformMetricsResponseToSeries';
1618
import {transformMetricsResponseToTable} from './transformMetricsResponseToTable';
1719

1820
const propNamesToIgnore = ['api', 'children'];
@@ -28,6 +30,8 @@ export type MetricsRequestRenderProps = {
2830
reloading: boolean;
2931
response: MetricsApiResponse | null;
3032
responsePrevious: MetricsApiResponse | null;
33+
seriesData?: Series[];
34+
seriesDataPrevious?: Series[];
3135
tableData?: TableData;
3236
};
3337

@@ -36,10 +40,14 @@ type DefaultProps = {
3640
* Include data for previous period
3741
*/
3842
includePrevious: boolean;
43+
/**
44+
* Transform the response data to be something ingestible by charts
45+
*/
46+
includeSeriesData: boolean;
3947
/**
4048
* Transform the response data to be something ingestible by GridEditable tables
4149
*/
42-
includeTabularData?: boolean;
50+
includeTabularData: boolean;
4351
/**
4452
* If true, no request will be made
4553
*/
@@ -67,6 +75,7 @@ type State = {
6775
class MetricsRequest extends React.Component<Props, State> {
6876
static defaultProps: DefaultProps = {
6977
includePrevious: false,
78+
includeSeriesData: false,
7079
includeTabularData: false,
7180
isDisabled: false,
7281
};
@@ -203,7 +212,8 @@ class MetricsRequest extends React.Component<Props, State> {
203212

204213
render() {
205214
const {reloading, errored, error, response, responsePrevious, pageLinks} = this.state;
206-
const {children, isDisabled, includeTabularData} = this.props;
215+
const {children, isDisabled, includeTabularData, includeSeriesData, includePrevious} =
216+
this.props;
207217

208218
const loading = response === null && !isDisabled && !error;
209219

@@ -219,6 +229,13 @@ class MetricsRequest extends React.Component<Props, State> {
219229
tableData: includeTabularData
220230
? transformMetricsResponseToTable({response})
221231
: undefined,
232+
seriesData: includeSeriesData
233+
? transformMetricsResponseToSeries(response)
234+
: undefined,
235+
seriesDataPrevious:
236+
includeSeriesData && includePrevious
237+
? transformMetricsResponseToSeries(responsePrevious)
238+
: undefined,
222239
});
223240
}
224241
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {MetricsApiResponse} from 'sentry/types';
2+
import {Series} from 'sentry/types/echarts';
3+
4+
export function transformMetricsResponseToSeries(
5+
response: MetricsApiResponse | null
6+
): Series[] {
7+
return (
8+
response?.groups.flatMap(group =>
9+
Object.keys(group.series).map(field => ({
10+
seriesName: `${field}${Object.entries(group.by)
11+
.map(([key, value]) => `|${key}:${value}`)
12+
.join('')}`,
13+
data: response.intervals.map((interval, index) => ({
14+
name: interval,
15+
value: group.series[field][index] ?? 0,
16+
})),
17+
}))
18+
) ?? []
19+
);
20+
}

tests/fixtures/js-stubs/sessions.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,3 +707,93 @@ export function UserTotalCountByProjectIn24h() {
707707
],
708708
};
709709
}
710+
711+
export function SessionUserCountByStatusByRelease() {
712+
return {
713+
start: '2022-01-15T00:00:00Z',
714+
end: '2022-01-29T00:00:00Z',
715+
query: '',
716+
intervals: [
717+
'2022-01-15T00:00:00Z',
718+
'2022-01-16T00:00:00Z',
719+
'2022-01-17T00:00:00Z',
720+
'2022-01-18T00:00:00Z',
721+
'2022-01-19T00:00:00Z',
722+
'2022-01-20T00:00:00Z',
723+
'2022-01-21T00:00:00Z',
724+
'2022-01-22T00:00:00Z',
725+
'2022-01-23T00:00:00Z',
726+
'2022-01-24T00:00:00Z',
727+
'2022-01-25T00:00:00Z',
728+
'2022-01-26T00:00:00Z',
729+
'2022-01-27T00:00:00Z',
730+
'2022-01-28T00:00:00Z',
731+
],
732+
groups: [
733+
{
734+
by: {'session.status': 'crashed', release: '1'},
735+
totals: {'sum(session)': 34, 'count_unique(user)': 1},
736+
series: {
737+
'sum(session)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 11, 0, 0, 0],
738+
'count_unique(user)': [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
739+
},
740+
},
741+
{
742+
by: {'session.status': 'abnormal', release: '1'},
743+
totals: {'sum(session)': 1, 'count_unique(user)': 1},
744+
series: {
745+
'sum(session)': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
746+
'count_unique(user)': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
747+
},
748+
},
749+
{
750+
by: {'session.status': 'errored', release: '1'},
751+
totals: {'sum(session)': 451, 'count_unique(user)': 2},
752+
series: {
753+
'sum(session)': [0, 0, 0, 0, 0, 37, 0, 0, 0, 335, 79, 0, 0, 0],
754+
'count_unique(user)': [0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 0, 0, 0],
755+
},
756+
},
757+
{
758+
by: {'session.status': 'healthy', release: '1'},
759+
totals: {'sum(session)': 5058, 'count_unique(user)': 3},
760+
series: {
761+
'sum(session)': [0, 0, 0, 0, 0, 2503, 661, 0, 0, 1464, 430, 0, 0, 0],
762+
'count_unique(user)': [0, 0, 0, 0, 0, 3, 3, 0, 0, 1, 1, 0, 0, 0],
763+
},
764+
},
765+
{
766+
by: {'session.status': 'crashed', release: '2'},
767+
totals: {'sum(session)': 35, 'count_unique(user)': 2},
768+
series: {
769+
'sum(session)': [1, 0, 0, 0, 0, 0, 0, 0, 0, 23, 11, 0, 0, 0],
770+
'count_unique(user)': [1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0],
771+
},
772+
},
773+
{
774+
by: {'session.status': 'abnormal', release: '2'},
775+
totals: {'sum(session)': 1, 'count_unique(user)': 1},
776+
series: {
777+
'sum(session)': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
778+
'count_unique(user)': [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
779+
},
780+
},
781+
{
782+
by: {'session.status': 'errored', release: '2'},
783+
totals: {'sum(session)': 452, 'count_unique(user)': 1},
784+
series: {
785+
'sum(session)': [1, 0, 0, 0, 0, 37, 0, 0, 0, 335, 79, 0, 0, 0],
786+
'count_unique(user)': [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0],
787+
},
788+
},
789+
{
790+
by: {'session.status': 'healthy', release: '2'},
791+
totals: {'sum(session)': 5059, 'count_unique(user)': 10},
792+
series: {
793+
'sum(session)': [1, 0, 0, 0, 0, 2503, 661, 0, 0, 1464, 430, 0, 0, 0],
794+
'count_unique(user)': [1, 0, 0, 0, 0, 10, 3, 0, 0, 4, 3, 0, 0, 0],
795+
},
796+
},
797+
],
798+
};
799+
}

tests/fixtures/js-stubs/types.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ type TestStubFixtures = {
104104
SessionTotalCountByProjectIn24h: SimpleStub;
105105
SessionUserCountByStatus: SimpleStub;
106106
SessionUserCountByStatus2: SimpleStub;
107+
SessionUserCountByStatusByRelease: SimpleStub;
107108
SessionUserStatusCountByProjectInPeriod: SimpleStub;
108109
SessionUserStatusCountByReleaseInPeriod: SimpleStub;
109110
SesssionTotalCountByReleaseIn24h: SimpleStub;

tests/js/spec/utils/metrics/metricsRequest.spec.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import {mountWithTheme, waitFor} from 'sentry-test/reactTestingLibrary';
22

33
import MetricsRequest from 'sentry/utils/metrics/metricsRequest';
4+
import {transformMetricsResponseToSeries} from 'sentry/utils/metrics/transformMetricsResponseToSeries';
5+
6+
jest.mock('sentry/utils/metrics/transformMetricsResponseToSeries', () => ({
7+
transformMetricsResponseToSeries: jest.fn().mockReturnValue([]),
8+
}));
49

510
describe('MetricsRequest', () => {
611
const project = TestStubs.Project();
@@ -251,4 +256,30 @@ describe('MetricsRequest', () => {
251256
})
252257
);
253258
});
259+
260+
it('includes series data', () => {
261+
mountWithTheme(
262+
<MetricsRequest {...props} includeSeriesData includePrevious>
263+
{childrenMock}
264+
</MetricsRequest>
265+
);
266+
267+
expect(metricsMock).toHaveBeenCalledTimes(2);
268+
269+
expect(childrenMock).toHaveBeenLastCalledWith({
270+
error: null,
271+
errored: false,
272+
isLoading: true,
273+
loading: true,
274+
pageLinks: null,
275+
reloading: false,
276+
response: null,
277+
responsePrevious: null,
278+
seriesData: [],
279+
seriesDataPrevious: [],
280+
tableData: undefined,
281+
});
282+
283+
expect(transformMetricsResponseToSeries).toHaveBeenCalledWith(null);
284+
});
254285
});

0 commit comments

Comments
 (0)