Skip to content

Commit 183bfc6

Browse files
feat(performance): Add Queue module chart components (#69356)
Adds latency and throughput charts with tests for the queues module
1 parent e1bc552 commit 183bfc6

File tree

4 files changed

+186
-0
lines changed

4 files changed

+186
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {OrganizationFixture} from 'sentry-fixture/organization';
2+
3+
import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
4+
5+
import useOrganization from 'sentry/utils/useOrganization';
6+
import {LatencyChart} from 'sentry/views/performance/queues/charts/latencyChart';
7+
8+
jest.mock('sentry/utils/useOrganization');
9+
10+
describe('latencyChart', () => {
11+
const organization = OrganizationFixture();
12+
jest.mocked(useOrganization).mockReturnValue(organization);
13+
14+
let eventsStatsMock;
15+
16+
beforeEach(() => {
17+
eventsStatsMock = MockApiClient.addMockResponse({
18+
url: `/organizations/${organization.slug}/events-stats/`,
19+
method: 'GET',
20+
body: {
21+
data: [],
22+
},
23+
});
24+
});
25+
it('renders', async () => {
26+
render(<LatencyChart />);
27+
screen.getByText('Avg Latency');
28+
expect(eventsStatsMock).toHaveBeenCalledWith(
29+
'/organizations/org-slug/events-stats/',
30+
expect.objectContaining({
31+
query: expect.objectContaining({
32+
yAxis: [
33+
'avg_if(span.self_time,span.op,queue.submit.celery)',
34+
'avg_if(span.self_time,span.op,queue.task.celery)',
35+
'count_op(queue.submit.celery)',
36+
'count_op(queue.task.celery)',
37+
],
38+
}),
39+
})
40+
);
41+
await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
42+
});
43+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import {DEFAULT_RELATIVE_PERIODS} from 'sentry/constants';
2+
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
3+
import {t} from 'sentry/locale';
4+
import {decodeScalar} from 'sentry/utils/queryString';
5+
import {useLocation} from 'sentry/utils/useLocation';
6+
import usePageFilters from 'sentry/utils/usePageFilters';
7+
import {CHART_HEIGHT} from 'sentry/views/performance/database/settings';
8+
import {useQueuesTimeSeriesQuery} from 'sentry/views/performance/queues/queries/useQueuesTimeSeriesQuery';
9+
import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
10+
import ChartPanel from 'sentry/views/starfish/components/chartPanel';
11+
12+
interface Props {
13+
error?: Error | null;
14+
}
15+
16+
export function LatencyChart({error}: Props) {
17+
const {query} = useLocation();
18+
const destination = decodeScalar(query.destination);
19+
const pageFilters = usePageFilters();
20+
const period = pageFilters.selection.datetime.period;
21+
const chartSubtext = (period && DEFAULT_RELATIVE_PERIODS[period]) ?? '';
22+
const {data, isLoading} = useQueuesTimeSeriesQuery({destination});
23+
return (
24+
<ChartPanel title={t('Avg Latency')} subtitle={chartSubtext}>
25+
<Chart
26+
height={CHART_HEIGHT}
27+
grid={{
28+
left: '0',
29+
right: '0',
30+
top: '12px',
31+
bottom: '0',
32+
}}
33+
data={
34+
[
35+
{
36+
seriesName: t('Average Processing Latency'),
37+
data: data['avg_if(span.self_time,span.op,queue.task.celery)'].data,
38+
},
39+
] ?? []
40+
}
41+
loading={isLoading}
42+
error={error}
43+
chartColors={CHART_PALETTE[2].slice(1)}
44+
type={ChartType.AREA}
45+
/>
46+
</ChartPanel>
47+
);
48+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {OrganizationFixture} from 'sentry-fixture/organization';
2+
3+
import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
4+
5+
import useOrganization from 'sentry/utils/useOrganization';
6+
import {ThroughputChart} from 'sentry/views/performance/queues/charts/throughputChart';
7+
8+
jest.mock('sentry/utils/useOrganization');
9+
10+
describe('throughputChart', () => {
11+
const organization = OrganizationFixture();
12+
jest.mocked(useOrganization).mockReturnValue(organization);
13+
14+
let eventsStatsMock;
15+
16+
beforeEach(() => {
17+
eventsStatsMock = MockApiClient.addMockResponse({
18+
url: `/organizations/${organization.slug}/events-stats/`,
19+
method: 'GET',
20+
body: {
21+
data: [],
22+
},
23+
});
24+
});
25+
it('renders', async () => {
26+
render(<ThroughputChart />);
27+
screen.getByText('Published vs Processed');
28+
expect(eventsStatsMock).toHaveBeenCalledWith(
29+
'/organizations/org-slug/events-stats/',
30+
expect.objectContaining({
31+
query: expect.objectContaining({
32+
yAxis: [
33+
'avg_if(span.self_time,span.op,queue.submit.celery)',
34+
'avg_if(span.self_time,span.op,queue.task.celery)',
35+
'count_op(queue.submit.celery)',
36+
'count_op(queue.task.celery)',
37+
],
38+
}),
39+
})
40+
);
41+
await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
42+
});
43+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {DEFAULT_RELATIVE_PERIODS} from 'sentry/constants';
2+
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
3+
import {t} from 'sentry/locale';
4+
import {decodeScalar} from 'sentry/utils/queryString';
5+
import {useLocation} from 'sentry/utils/useLocation';
6+
import usePageFilters from 'sentry/utils/usePageFilters';
7+
import {CHART_HEIGHT} from 'sentry/views/performance/database/settings';
8+
import {useQueuesTimeSeriesQuery} from 'sentry/views/performance/queues/queries/useQueuesTimeSeriesQuery';
9+
import Chart, {ChartType} from 'sentry/views/starfish/components/chart';
10+
import ChartPanel from 'sentry/views/starfish/components/chartPanel';
11+
12+
interface Props {
13+
error?: Error | null;
14+
}
15+
16+
export function ThroughputChart({error}: Props) {
17+
const {query} = useLocation();
18+
const destination = decodeScalar(query.destination);
19+
const pageFilters = usePageFilters();
20+
const period = pageFilters.selection.datetime.period;
21+
const chartSubtext = (period && DEFAULT_RELATIVE_PERIODS[period]) ?? '';
22+
const {data, isLoading} = useQueuesTimeSeriesQuery({destination});
23+
return (
24+
<ChartPanel title={t('Published vs Processed')} subtitle={chartSubtext}>
25+
<Chart
26+
height={CHART_HEIGHT}
27+
grid={{
28+
left: '0',
29+
right: '0',
30+
top: '12px',
31+
bottom: '0',
32+
}}
33+
data={
34+
[
35+
{
36+
seriesName: t('Published'),
37+
data: data['count_op(queue.submit.celery)'].data,
38+
},
39+
{
40+
seriesName: t('Processed'),
41+
data: data['count_op(queue.task.celery)'].data,
42+
},
43+
] ?? []
44+
}
45+
loading={isLoading}
46+
error={error}
47+
chartColors={CHART_PALETTE[2].slice(1, 3)}
48+
type={ChartType.LINE}
49+
/>
50+
</ChartPanel>
51+
);
52+
}

0 commit comments

Comments
 (0)