Skip to content

Commit

Permalink
ref(insights): Replace usage of <ReleaseSeries> in `<InsightsTimeSe…
Browse files Browse the repository at this point in the history
…riesWidget>` (#86129)

...with a hook (`useReleaseStats`)

This will be needed for the new release bubbles feature as we will show
releases on the mini widgets instead of only on fullscreen. Using
`<ReleaseSeries>` renderer will cause duplicate requests to the API.

ref #85779
  • Loading branch information
billyvg authored Mar 3, 2025
1 parent 735880e commit ec3a6f3
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 30 deletions.
3 changes: 3 additions & 0 deletions static/app/utils/queryClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,11 @@ function parsePageParam(dir: 'previous' | 'next') {
export function useInfiniteApiQuery<TResponseData>({
queryKey,
enabled,
staleTime,
}: {
queryKey: ApiQueryKey;
enabled?: boolean;
staleTime?: number;
}) {
const api = useApi({persistInFlight: PERSIST_IN_FLIGHT});
const query = useInfiniteQuery({
Expand All @@ -299,6 +301,7 @@ export function useInfiniteApiQuery<TResponseData>({
getNextPageParam: parsePageParam('next'),
initialPageParam: undefined,
enabled: enabled ?? true,
staleTime,
});

return query;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
import type {PageFilters} from 'sentry/types/core';
import {useInfiniteApiQuery} from 'sentry/utils/queryClient';
import useOrganization from 'sentry/utils/useOrganization';

interface ReleaseMetaBasic {
date: string;
version: string;
}

/**
* Fetches *ALL* releases (e.g. all pages worth)
*/
export function useReleaseStats({datetime, environments, projects}: PageFilters) {
const organization = useOrganization();

const {
isLoading,
isFetching,
fetchNextPage,
hasNextPage,
isPending,
isError,
error,
data,
} = useInfiniteApiQuery<ReleaseMetaBasic[]>({
queryKey: [
`/organizations/${organization.slug}/releases/stats/`,
{
query: {
environment: environments,
project: projects,
...normalizeDateTimeParams(datetime),
},
},
],
staleTime: Infinity,
});

if (!isFetching && hasNextPage) {
fetchNextPage();
}

const releases =
data?.pages.flatMap(([pageData]) =>
pageData.map(({date, version}) => ({timestamp: date, version}))
) ?? [];

return {
isLoading,
isPending,
isError,
error,
releases,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
jest.mock('sentry/utils/useProjects');
jest.mock('sentry/views/insights/common/queries/useOnboardingProject');
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

const requestMocks: Record<string, jest.Mock> = {};

Expand Down Expand Up @@ -171,6 +174,13 @@ const setupMocks = () => {
reloadProjects: jest.fn(),
placeholders: [],
});
jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});
};

const setupMockRequests = (organization: Organization) => {
Expand Down
11 changes: 11 additions & 0 deletions static/app/views/insights/cache/views/cacheLandingPage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
jest.mock('sentry/utils/useProjects');
jest.mock('sentry/views/insights/common/queries/useOnboardingProject');
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

const requestMocks = {
missRateChart: jest.fn(),
Expand Down Expand Up @@ -79,6 +82,14 @@ describe('CacheLandingPage', function () {
initiallyLoaded: false,
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

beforeEach(function () {
jest.clearAllMocks();
setRequestMocks(organization);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import styled from '@emotion/styled';

import {openInsightChartModal} from 'sentry/actionCreators/modal';
import {Button} from 'sentry/components/button';
import ReleaseSeries from 'sentry/components/charts/releaseSeries';
import {CHART_PALETTE} from 'sentry/constants/chartPalette';
import {IconExpand} from 'sentry/icons';
import {t} from 'sentry/locale';
Expand All @@ -13,6 +12,7 @@ import {
TimeSeriesWidgetVisualization,
type TimeSeriesWidgetVisualizationProps,
} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';

import {
Expand All @@ -38,8 +38,7 @@ export interface InsightsTimeSeriesWidgetProps {

export function InsightsTimeSeriesWidget(props: InsightsTimeSeriesWidgetProps) {
const pageFilters = usePageFilters();
const {start, end, period, utc} = pageFilters.selection.datetime;
const {projects, environments} = pageFilters.selection;
const {releases} = useReleaseStats(pageFilters.selection);

const visualizationProps: TimeSeriesWidgetVisualizationProps = {
visualizationType: props.visualizationType,
Expand Down Expand Up @@ -108,33 +107,12 @@ export function InsightsTimeSeriesWidget(props: InsightsTimeSeriesWidgetProps) {
openInsightChartModal({
title: props.title,
children: (
<ReleaseSeries
start={start}
end={end}
queryExtra={undefined}
period={period}
utc={utc}
projects={projects}
environments={environments}
>
{({releases}) => {
return (
<ModalChartContainer>
<TimeSeriesWidgetVisualization
{...visualizationProps}
releases={
releases
? releases.map(release => ({
timestamp: release.date,
version: release.version,
}))
: []
}
/>
</ModalChartContainer>
);
}}
</ReleaseSeries>
<ModalChartContainer>
<TimeSeriesWidgetVisualization
{...visualizationProps}
releases={releases ?? []}
/>
</ModalChartContainer>
),
});
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
jest.mock('sentry/utils/useProjects');
jest.mock('sentry/views/insights/common/queries/useOnboardingProject');
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('DatabaseLandingPage', function () {
const organization = OrganizationFixture({features: ['insights-initial-modules']});
Expand Down Expand Up @@ -60,6 +63,14 @@ describe('DatabaseLandingPage', function () {
key: '',
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

beforeEach(function () {
MockApiClient.addMockResponse({
url: '/organizations/org-slug/sdk-updates/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import {DatabaseSpanSummaryPage} from 'sentry/views/insights/database/views/data

jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('DatabaseSpanSummaryPage', function () {
const organization = OrganizationFixture({
Expand Down Expand Up @@ -44,6 +47,14 @@ describe('DatabaseSpanSummaryPage', function () {
key: '',
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

beforeEach(function () {
jest.clearAllMocks();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import {HTTPDomainSummaryPage} from 'sentry/views/insights/http/views/httpDomain

jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('HTTPSummaryPage', function () {
const organization = OrganizationFixture({features: ['insights-initial-modules']});
Expand Down Expand Up @@ -52,6 +55,14 @@ describe('HTTPSummaryPage', function () {
key: '',
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

beforeEach(function () {
jest.clearAllMocks();

Expand Down
11 changes: 11 additions & 0 deletions static/app/views/insights/http/views/httpLandingPage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
jest.mock('sentry/utils/useProjects');
jest.mock('sentry/views/insights/common/queries/useOnboardingProject');
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('HTTPLandingPage', function () {
const organization = OrganizationFixture({
Expand Down Expand Up @@ -75,6 +78,14 @@ describe('HTTPLandingPage', function () {
initiallyLoaded: false,
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

beforeEach(function () {
jest.clearAllMocks();

Expand Down
11 changes: 11 additions & 0 deletions static/app/views/insights/queues/charts/latencyChart.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@ import {OrganizationFixture} from 'sentry-fixture/organization';

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

import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';
import {LatencyChart} from 'sentry/views/insights/queues/charts/latencyChart';
import {Referrer} from 'sentry/views/insights/queues/referrers';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('latencyChart', () => {
const organization = OrganizationFixture();

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

let eventsStatsMock: jest.Mock;

beforeEach(() => {
Expand Down
11 changes: 11 additions & 0 deletions static/app/views/insights/queues/charts/throughputChart.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@ import {OrganizationFixture} from 'sentry-fixture/organization';

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

import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';
import {ThroughputChart} from 'sentry/views/insights/queues/charts/throughputChart';
import {Referrer} from 'sentry/views/insights/queues/referrers';

jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('throughputChart', () => {
const organization = OrganizationFixture();

let eventsStatsMock!: jest.Mock;

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

beforeEach(() => {
eventsStatsMock = MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/events-stats/`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestin
import {useLocation} from 'sentry/utils/useLocation';
import usePageFilters from 'sentry/utils/usePageFilters';
import useProjects from 'sentry/utils/useProjects';
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';
import PageWithProviders from 'sentry/views/insights/queues/views/destinationSummaryPage';

jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
jest.mock('sentry/utils/useProjects');
jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('destinationSummaryPage', () => {
const organization = OrganizationFixture({
Expand Down Expand Up @@ -54,6 +56,14 @@ describe('destinationSummaryPage', () => {
initiallyLoaded: false,
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

let eventsMock: jest.Mock;
let eventsStatsMock: jest.Mock;

Expand Down
10 changes: 10 additions & 0 deletions static/app/views/insights/queues/views/queuesLandingPage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {render, screen} from 'sentry-test/reactTestingLibrary';
import {useLocation} from 'sentry/utils/useLocation';
import usePageFilters from 'sentry/utils/usePageFilters';
import useProjects from 'sentry/utils/useProjects';
import {useReleaseStats} from 'sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats';
import QueuesLandingPage from 'sentry/views/insights/queues/views/queuesLandingPage';

jest.mock('sentry/utils/useLocation');
jest.mock('sentry/utils/usePageFilters');
jest.mock('sentry/utils/useProjects');
jest.mock('sentry/views/dashboards/widgets/timeSeriesWidget/useReleaseStats');

describe('queuesLandingPage', () => {
const organization = OrganizationFixture({
Expand Down Expand Up @@ -58,6 +60,14 @@ describe('queuesLandingPage', () => {
initiallyLoaded: false,
});

jest.mocked(useReleaseStats).mockReturnValue({
isLoading: false,
isPending: false,
isError: false,
error: null,
releases: [],
});

let eventsMock: jest.Mock;
let eventsStatsMock: jest.Mock;

Expand Down

0 comments on commit ec3a6f3

Please sign in to comment.