From 22146732741c67302426d6a8410b3073f9f89266 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Mon, 4 Aug 2025 10:33:27 -0700 Subject: [PATCH 1/2] add new hook and pagination for fetching repo token list --- .../useInfiniteRepositoryTokens.spec.tsx | 265 ++++++++++++++++++ .../hooks/useInfiniteRepositoryTokens.tsx | 113 ++++++++ .../repoTokenTable/repoTokenTable.spec.tsx | 112 ++++++++ .../tokens/repoTokenTable/repoTokenTable.tsx | 19 +- .../tokens/repoTokenTable/tableBody.tsx | 9 - .../app/views/codecov/tokens/tokens.spec.tsx | 62 +++- static/app/views/codecov/tokens/tokens.tsx | 61 ++-- 7 files changed, 602 insertions(+), 39 deletions(-) create mode 100644 static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.spec.tsx create mode 100644 static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx create mode 100644 static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.spec.tsx diff --git a/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.spec.tsx b/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.spec.tsx new file mode 100644 index 00000000000000..7e3a945ee501a9 --- /dev/null +++ b/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.spec.tsx @@ -0,0 +1,265 @@ +import {OrganizationFixture} from 'sentry-fixture/organization'; + +import {makeTestQueryClient} from 'sentry-test/queryClient'; +import {renderHook, waitFor} from 'sentry-test/reactTestingLibrary'; + +import {CodecovContext} from 'sentry/components/codecov/context/codecovContext'; +import {QueryClientProvider} from 'sentry/utils/queryClient'; +import {OrganizationContext} from 'sentry/views/organizationContext'; + +import {useInfiniteRepositoryTokens} from './useInfiniteRepositoryTokens'; + +const mockRepositoryTokensResponse = { + pageInfo: { + endCursor: 'cursor123', + hasNextPage: true, + hasPreviousPage: false, + startCursor: 'cursor000', + }, + results: [ + { + name: 'test-repo-one', + token: 'sk_test_token_12345abcdef', + }, + { + name: 'test-repo-two', + token: 'sk_test_token_67890ghijkl', + }, + ], + totalCount: 25, +}; + +const emptyRepositoryTokensResponse = { + pageInfo: { + endCursor: null, + hasNextPage: false, + hasPreviousPage: false, + startCursor: null, + }, + results: [], + totalCount: 0, +}; + +const codecovContextValue = { + integratedOrgId: 'org123', + repository: 'test-repo', + branch: 'main', + codecovPeriod: '30d', + changeContextValue: jest.fn(), +}; + +describe('useInfiniteRepositoryTokens', () => { + beforeEach(() => { + MockApiClient.clearMockResponses(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('fetches repository tokens with no params and returns successful response', async () => { + const organization = OrganizationFixture({slug: 'test-org'}); + + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/prevent/owner/${codecovContextValue.integratedOrgId}/repositories/tokens/`, + body: mockRepositoryTokensResponse, + }); + + const wrapper = ({children}: {children: React.ReactNode}) => ( + + + + {children} + + + + ); + + const {result} = renderHook( + () => + useInfiniteRepositoryTokens({ + cursor: undefined, + navigation: undefined, + }), + { + wrapper, + } + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.data).toHaveLength(2); + expect(result.current.totalCount).toBe(25); + expect(result.current.startCursor).toBe('cursor000'); + expect(result.current.endCursor).toBe('cursor123'); + + // Verifies that the data is transformed correctly + expect(result.current.data[0]).toEqual({ + name: 'test-repo-one', + token: 'sk_test_token_12345abcdef', + }); + expect(result.current.data[1]).toEqual({ + name: 'test-repo-two', + token: 'sk_test_token_67890ghijkl', + }); + }); + + it('fetches repository tokens with navigation and cursor props', async () => { + const organization = OrganizationFixture({slug: 'test-org'}); + + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/prevent/owner/${codecovContextValue.integratedOrgId}/repositories/tokens/`, + body: mockRepositoryTokensResponse, + match: [ + MockApiClient.matchQuery({ + cursor: 'next-cursor', + navigation: 'next', + }), + ], + }); + + const wrapper = ({children}: {children: React.ReactNode}) => ( + + + + {children} + + + + ); + + const {result} = renderHook( + () => + useInfiniteRepositoryTokens({ + cursor: 'next-cursor', + navigation: 'next', + }), + { + wrapper, + } + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.data).toHaveLength(2); + expect(result.current.totalCount).toBe(25); + expect(result.current.hasNextPage).toBe(true); + }); + + it('handles empty results response', async () => { + const organization = OrganizationFixture({slug: 'test-org'}); + + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/prevent/owner/${codecovContextValue.integratedOrgId}/repositories/tokens/`, + body: emptyRepositoryTokensResponse, + }); + + const wrapper = ({children}: {children: React.ReactNode}) => ( + + + + {children} + + + + ); + + const {result} = renderHook( + () => + useInfiniteRepositoryTokens({ + cursor: undefined, + navigation: undefined, + }), + { + wrapper, + } + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBe(true); + }); + + expect(result.current.data).toHaveLength(0); + expect(result.current.totalCount).toBe(0); + expect(result.current.startCursor).toBeNull(); + expect(result.current.endCursor).toBeNull(); + expect(result.current.hasNextPage).toBe(false); + expect(result.current.hasPreviousPage).toBe(false); + }); + + it('handles API errors gracefully', async () => { + const organization = OrganizationFixture({slug: 'test-org'}); + + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/prevent/owner/${codecovContextValue.integratedOrgId}/repositories/tokens/`, + statusCode: 500, + body: {error: 'Internal Server Error'}, + }); + + const wrapper = ({children}: {children: React.ReactNode}) => ( + + + + {children} + + + + ); + + const {result} = renderHook( + () => + useInfiniteRepositoryTokens({ + cursor: undefined, + navigation: undefined, + }), + { + wrapper, + } + ); + + await waitFor(() => { + expect(result.current.isError).toBe(true); + }); + + expect(result.current.error).toBeDefined(); + expect(result.current.data).toHaveLength(0); + expect(result.current.totalCount).toBe(0); + }); + + it('is disabled when integratedOrgId is not provided', () => { + const organization = OrganizationFixture({slug: 'test-org'}); + + const codecovContextWithoutOrgId = { + ...codecovContextValue, + integratedOrgId: '', + }; + + const wrapper = ({children}: {children: React.ReactNode}) => ( + + + + {children} + + + + ); + + const {result} = renderHook( + () => + useInfiniteRepositoryTokens({ + cursor: undefined, + navigation: undefined, + }), + { + wrapper, + } + ); + + expect(result.current.data).toHaveLength(0); + expect(result.current.totalCount).toBe(0); + }); +}); diff --git a/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx b/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx new file mode 100644 index 00000000000000..2212be9d0da616 --- /dev/null +++ b/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx @@ -0,0 +1,113 @@ +import {useMemo} from 'react'; + +import type {ApiResult} from 'sentry/api'; +import {useCodecovContext} from 'sentry/components/codecov/context/codecovContext'; +import { + fetchDataQuery, + type InfiniteData, + type QueryKeyEndpointOptions, + useInfiniteQuery, +} from 'sentry/utils/queryClient'; +import useOrganization from 'sentry/utils/useOrganization'; + +type RepositoryTokenItem = { + name: string; + token: string; +}; + +interface RepositoryTokens { + pageInfo: { + endCursor: string; + hasNextPage: boolean; + hasPreviousPage: boolean; + startCursor: string; + }; + results: RepositoryTokenItem[]; + totalCount: number; +} + +type QueryKey = [url: string, endpointOptions: QueryKeyEndpointOptions]; + +export function useInfiniteRepositoryTokens({ + cursor, + navigation, +}: { + cursor: string | undefined; + navigation: 'next' | 'prev' | undefined; +}) { + const {integratedOrgId} = useCodecovContext(); + const organization = useOrganization(); + + const {data, ...rest} = useInfiniteQuery< + ApiResult, + Error, + InfiniteData>, + QueryKey + >({ + queryKey: [ + `/organizations/${organization.slug}/prevent/owner/${integratedOrgId}/repositories/tokens/`, + { + query: { + cursor: cursor ?? undefined, + navigation: navigation ?? undefined, + }, + }, + ], + queryFn: async ({ + queryKey: [url, {query}], + client, + signal, + meta, + }): Promise> => { + const result = await fetchDataQuery({ + queryKey: [ + url, + { + query: { + ...query, + ...(cursor ? {cursor} : {}), + ...(navigation ? {navigation} : {}), + }, + }, + ], + client, + signal, + meta, + }); + + return result as ApiResult; + }, + getNextPageParam: ([lastPage]) => { + return lastPage.pageInfo?.hasNextPage ? lastPage.pageInfo.endCursor : undefined; + }, + getPreviousPageParam: ([firstPage]) => { + return firstPage.pageInfo?.hasPreviousPage + ? firstPage.pageInfo.startCursor + : undefined; + }, + initialPageParam: undefined, + enabled: Boolean(integratedOrgId), + }); + + const memoizedData = useMemo( + () => + data?.pages?.flatMap(([pageData]) => + pageData.results.map(({name, token}) => { + return { + name, + token, + }; + }) + ) ?? [], + [data] + ); + + return { + data: memoizedData, + totalCount: data?.pages?.[0]?.[0]?.totalCount ?? 0, + startCursor: data?.pages?.[0]?.[0]?.pageInfo?.startCursor, + endCursor: data?.pages?.[0]?.[0]?.pageInfo?.endCursor, + // TODO: only provide the values that we're interested in + ...rest, + }; +} diff --git a/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.spec.tsx b/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.spec.tsx new file mode 100644 index 00000000000000..e88fecf9f2c2c4 --- /dev/null +++ b/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.spec.tsx @@ -0,0 +1,112 @@ +import {render, screen} from 'sentry-test/reactTestingLibrary'; + +import RepoTokenTable, {DEFAULT_SORT} from './repoTokenTable'; + +jest.mock('sentry/actionCreators/modal', () => ({ + openTokenRegenerationConfirmationModal: jest.fn(), +})); + +jest.mock('sentry/components/confirm', () => { + return function MockConfirm({children}: {children: React.ReactNode}) { + return
{children}
; + }; +}); + +const mockData = [ + { + name: 'sentry-frontend', + token: 'sk_test_token_12345abcdef', + }, + { + name: 'sentry-backend', + token: 'sk_test_token_67890ghijkl', + }, +]; + +const defaultProps = { + response: { + data: mockData, + isLoading: false, + error: null, + }, + sort: DEFAULT_SORT, +}; + +describe('RepoTokenTable', () => { + beforeEach(() => { + MockApiClient.clearMockResponses(); + }); + + it('renders table with repository tokens data', () => { + render(); + + expect(screen.getByLabelText('Repository Tokens Table')).toBeInTheDocument(); + + // Check table headers + expect(screen.getByText('Repository Name')).toBeInTheDocument(); + expect(screen.getByText('Token')).toBeInTheDocument(); + + // Check table data + expect(screen.getByText('sentry-frontend')).toBeInTheDocument(); + expect(screen.getByText('sentry-backend')).toBeInTheDocument(); + expect(screen.getByText('sk_test_token_12345abcdef')).toBeInTheDocument(); + expect(screen.getByText('sk_test_token_67890ghijkl')).toBeInTheDocument(); + + // Check regenerate buttons + const regenerateButtons = screen.getAllByText('Regenerate token'); + expect(regenerateButtons).toHaveLength(2); + }); + + it('renders empty table when no data is provided', () => { + const emptyProps = { + ...defaultProps, + response: { + data: [], + isLoading: false, + error: null, + }, + }; + + render(); + + expect(screen.getByLabelText('Repository Tokens Table')).toBeInTheDocument(); + expect(screen.getByText('Repository Name')).toBeInTheDocument(); + expect(screen.getByText('Token')).toBeInTheDocument(); + + // Should not have any repository data + expect(screen.queryByText('sentry-frontend')).not.toBeInTheDocument(); + expect(screen.queryByText('sentry-backend')).not.toBeInTheDocument(); + }); + + it('renders table with single repository token', () => { + const singleDataProps = { + ...defaultProps, + response: { + data: [mockData[0]!], + isLoading: false, + error: null, + }, + }; + + render(); + + expect(screen.getByText('sentry-frontend')).toBeInTheDocument(); + expect(screen.getByText('sk_test_token_12345abcdef')).toBeInTheDocument(); + expect(screen.queryByText('sentry-backend')).not.toBeInTheDocument(); + + const regenerateButtons = screen.getAllByText('Regenerate token'); + expect(regenerateButtons).toHaveLength(1); + }); + + it('renders regenerate buttons that can be interacted with', () => { + render(); + + const regenerateButtons = screen.getAllByText('Regenerate token'); + expect(regenerateButtons).toHaveLength(2); + + // Check that buttons are clickable + regenerateButtons.forEach(button => { + expect(button.closest('button')).toBeEnabled(); + }); + }); +}); diff --git a/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.tsx b/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.tsx index a77abc36a908d7..9f972cca4e14ac 100644 --- a/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.tsx +++ b/static/app/views/codecov/tokens/repoTokenTable/repoTokenTable.tsx @@ -1,20 +1,16 @@ -import GridEditable, { - COL_WIDTH_UNDEFINED, - type GridColumnHeader, -} from 'sentry/components/tables/gridEditable'; +import GridEditable, {type GridColumnHeader} from 'sentry/components/tables/gridEditable'; import {t} from 'sentry/locale'; import type {Sort} from 'sentry/utils/discover/fields'; import {renderTableBody} from 'sentry/views/codecov/tokens/repoTokenTable/tableBody'; import {renderTableHeader} from 'sentry/views/codecov/tokens/repoTokenTable/tableHeader'; type RepoTokenTableResponse = { - createdAt: string; name: string; token: string; }; -export type Row = Pick; -export type Column = GridColumnHeader<'name' | 'token' | 'createdAt' | 'regenerateToken'>; +export type Row = Pick; +export type Column = GridColumnHeader<'name' | 'token' | 'regenerateToken'>; type ValidField = (typeof SORTABLE_FIELDS)[number]; @@ -27,16 +23,15 @@ export type ValidSort = Sort & { }; const COLUMNS_ORDER: Column[] = [ - {key: 'name', name: t('Repository Name'), width: 350}, - {key: 'token', name: t('Token'), width: 275}, - {key: 'createdAt', name: t('Created Date'), width: COL_WIDTH_UNDEFINED}, + {key: 'name', name: t('Repository Name'), width: 400}, + {key: 'token', name: t('Token'), width: 350}, {key: 'regenerateToken', name: '', width: 100}, ]; -export const SORTABLE_FIELDS = ['name', 'createdAt'] as const; +export const SORTABLE_FIELDS = ['name'] as const; export const DEFAULT_SORT: ValidSort = { - field: 'createdAt', + field: 'name', kind: 'desc', }; diff --git a/static/app/views/codecov/tokens/repoTokenTable/tableBody.tsx b/static/app/views/codecov/tokens/repoTokenTable/tableBody.tsx index 3e658b93e63d41..6a128084aae9d2 100644 --- a/static/app/views/codecov/tokens/repoTokenTable/tableBody.tsx +++ b/static/app/views/codecov/tokens/repoTokenTable/tableBody.tsx @@ -55,10 +55,6 @@ export function renderTableBody({column, row}: TableBodyProps) { return {value}; } - if (key === 'createdAt') { - return {value}; - } - return {value}; } @@ -69,8 +65,3 @@ const StyledButton = styled(Button)` export const AlignmentContainer = styled('div')<{alignment: string}>` text-align: ${p => (p.alignment === 'left' ? 'left' : 'right')}; `; - -const DateContainer = styled('div')` - color: ${p => p.theme.tokens.content.muted}; - text-align: 'left'; -`; diff --git a/static/app/views/codecov/tokens/tokens.spec.tsx b/static/app/views/codecov/tokens/tokens.spec.tsx index c1f523f13bde21..b6b22944eeb768 100644 --- a/static/app/views/codecov/tokens/tokens.spec.tsx +++ b/static/app/views/codecov/tokens/tokens.spec.tsx @@ -9,17 +9,55 @@ import { import CodecovQueryParamsProvider from 'sentry/components/codecov/container/codecovParamsProvider'; import TokensPage from 'sentry/views/codecov/tokens/tokens'; +jest.mock('sentry/components/pagination', () => { + return function MockPagination() { + return
Pagination Component
; + }; +}); + const mockIntegrations = [ {name: 'some-org-name', id: '1'}, {name: 'test-org', id: '2'}, ]; +const mockRepositoryTokensResponse = { + pageInfo: { + endCursor: 'cursor123', + hasNextPage: true, + hasPreviousPage: false, + startCursor: 'cursor000', + }, + results: [ + { + name: 'test2', + token: 'test2Token', + }, + { + name: 'test-repo', + token: 'test-repo-token', + }, + ], + totalCount: 2, +}; + const mockApiCall = () => { MockApiClient.addMockResponse({ url: `/organizations/org-slug/integrations/`, method: 'GET', body: mockIntegrations, }); + + MockApiClient.addMockResponse({ + url: `/organizations/org-slug/prevent/owner/1/repositories/tokens/`, + method: 'GET', + body: mockRepositoryTokensResponse, + }); + + MockApiClient.addMockResponse({ + url: `/organizations/org-slug/prevent/owner/2/repositories/tokens/`, + method: 'GET', + body: mockRepositoryTokensResponse, + }); }; describe('TokensPage', () => { @@ -102,6 +140,29 @@ describe('TokensPage', () => { }); }); + it('renders the pagination component', async () => { + mockApiCall(); + render( + + + , + { + initialRouterConfig: { + location: { + pathname: '/codecov/tokens/', + query: { + integratedOrgId: '1', + }, + }, + }, + } + ); + + await waitFor(() => { + expect(screen.getByText('Pagination Component')).toBeInTheDocument(); + }); + }); + it('renders repository tokens and related data', async () => { mockApiCall(); render( @@ -125,7 +186,6 @@ describe('TokensPage', () => { }); expect(screen.getByText('test2')).toBeInTheDocument(); expect(screen.getByText('test2Token')).toBeInTheDocument(); - expect(screen.getByText('Mar 19, 2024 6:33:30 PM CET')).toBeInTheDocument(); expect(await screen.findAllByText('Regenerate token')).toHaveLength(2); }); diff --git a/static/app/views/codecov/tokens/tokens.tsx b/static/app/views/codecov/tokens/tokens.tsx index 6ed7fdf7366a44..8bfcb9e961152e 100644 --- a/static/app/views/codecov/tokens/tokens.tsx +++ b/static/app/views/codecov/tokens/tokens.tsx @@ -1,17 +1,21 @@ +import {useCallback} from 'react'; import styled from '@emotion/styled'; import {useCodecovContext} from 'sentry/components/codecov/context/codecovContext'; import {IntegratedOrgSelector} from 'sentry/components/codecov/integratedOrgSelector/integratedOrgSelector'; import {integratedOrgIdToName} from 'sentry/components/codecov/integratedOrgSelector/utils'; import PageFilterBar from 'sentry/components/organizations/pageFilterBar'; +import Pagination from 'sentry/components/pagination'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Integration} from 'sentry/types/integrations'; import {useApiQuery} from 'sentry/utils/queryClient'; import {decodeSorts} from 'sentry/utils/queryString'; import {useLocation} from 'sentry/utils/useLocation'; +import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; +import {useInfiniteRepositoryTokens} from './repoTokenTable/hooks/useInfiniteRepositoryTokens'; import type {ValidSort} from './repoTokenTable/repoTokenTable'; import RepoTokenTable, { DEFAULT_SORT, @@ -21,6 +25,7 @@ import RepoTokenTable, { export default function TokensPage() { const {integratedOrgId} = useCodecovContext(); const organization = useOrganization(); + const navigate = useNavigate(); const {data: integrations = []} = useApiQuery( [ `/organizations/${organization.slug}/integrations/`, @@ -33,23 +38,38 @@ export default function TokensPage() { const sorts: [ValidSort] = [ decodeSorts(location.query?.sort).find(isAValidSort) ?? DEFAULT_SORT, ]; + const response = useInfiniteRepositoryTokens({ + cursor: location.query?.cursor as string | undefined, + navigation: location.query?.navigation as 'next' | 'prev' | undefined, + }); - const response = { - data: [ - { - name: 'test', - token: 'testToken', - createdAt: 'Mar 20, 2024 6:33:30 PM CET', - }, - { - name: 'test2', - token: 'test2Token', - createdAt: 'Mar 19, 2024 6:33:30 PM CET', - }, - ], - isLoading: false, - error: null, - }; + const handleCursor = useCallback( + ( + _cursor: string | undefined, + path: string, + query: Record, + delta: number + ) => { + // Without these guards, the pagination cursor can get stuck on an incorrect value. + const navigation = delta === -1 ? 'prev' : 'next'; + const goPrevPage = navigation === 'prev' && response.hasPreviousPage; + const goNextPage = navigation === 'next' && response.hasNextPage; + + navigate({ + pathname: path, + query: { + ...query, + cursor: goPrevPage + ? response.startCursor + : goNextPage + ? response.endCursor + : undefined, + navigation, + }, + }); + }, + [navigate, response] + ); return ( @@ -63,6 +83,9 @@ export default function TokensPage() { {t("Use them for uploading reports to all Sentry Prevent's features.")}

+ {/* We don't need to use the pageLinks prop because Codecov handles pagination using our own cursor implementation. But we need to + put a dummy value here because otherwise the component wouldn't render. */} +
); } @@ -70,7 +93,7 @@ export default function TokensPage() { const LayoutGap = styled('div')` display: grid; gap: ${space(1)}; - max-width: 1200px; + max-width: 1000px; `; const HeaderValue = styled('div')` @@ -78,3 +101,7 @@ const HeaderValue = styled('div')` font-size: ${p => p.theme.headerFontSize}; font-weight: ${p => p.theme.fontWeight.bold}; `; + +const StyledPagination = styled(Pagination)` + margin-top: 0px; +`; From 66d51998f66a7e0692c387a2ffad83a1ba0d3d41 Mon Sep 17 00:00:00 2001 From: Ajay Singh Date: Mon, 4 Aug 2025 16:08:10 -0700 Subject: [PATCH 2/2] update to pageData --- .../branchSelector/useInfiniteRepositoryBranches.tsx | 10 +++++----- .../codecov/repoSelector/useInfiniteRepositories.tsx | 10 +++++----- .../views/codecov/tests/queries/useGetTestResults.ts | 10 +++++----- .../hooks/useInfiniteRepositoryTokens.tsx | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/static/app/components/codecov/branchSelector/useInfiniteRepositoryBranches.tsx b/static/app/components/codecov/branchSelector/useInfiniteRepositoryBranches.tsx index 8bc8d103db94e5..400d3be6495ec5 100644 --- a/static/app/components/codecov/branchSelector/useInfiniteRepositoryBranches.tsx +++ b/static/app/components/codecov/branchSelector/useInfiniteRepositoryBranches.tsx @@ -70,12 +70,12 @@ export function useInfiniteRepositoryBranches({term}: Props) { return result as ApiResult; }, - getNextPageParam: ([lastPage]) => { - return lastPage.pageInfo?.hasNextPage ? lastPage.pageInfo.endCursor : undefined; + getNextPageParam: ([pageData]) => { + return pageData.pageInfo?.hasNextPage ? pageData.pageInfo.endCursor : undefined; }, - getPreviousPageParam: ([firstPage]) => { - return firstPage.pageInfo?.hasPreviousPage - ? firstPage.pageInfo.startCursor + getPreviousPageParam: ([pageData]) => { + return pageData.pageInfo?.hasPreviousPage + ? pageData.pageInfo.startCursor : undefined; }, initialPageParam: undefined, diff --git a/static/app/components/codecov/repoSelector/useInfiniteRepositories.tsx b/static/app/components/codecov/repoSelector/useInfiniteRepositories.tsx index 9812c796c6f783..820d95f0d7dc53 100644 --- a/static/app/components/codecov/repoSelector/useInfiniteRepositories.tsx +++ b/static/app/components/codecov/repoSelector/useInfiniteRepositories.tsx @@ -72,12 +72,12 @@ export function useInfiniteRepositories({term}: Props) { return result as ApiResult; }, - getNextPageParam: ([lastPage]) => { - return lastPage.pageInfo?.hasNextPage ? lastPage.pageInfo.endCursor : undefined; + getNextPageParam: ([pageData]) => { + return pageData.pageInfo?.hasNextPage ? pageData.pageInfo.endCursor : undefined; }, - getPreviousPageParam: ([firstPage]) => { - return firstPage.pageInfo?.hasPreviousPage - ? firstPage.pageInfo.startCursor + getPreviousPageParam: ([pageData]) => { + return pageData.pageInfo?.hasPreviousPage + ? pageData.pageInfo.startCursor : undefined; }, initialPageParam: undefined, diff --git a/static/app/views/codecov/tests/queries/useGetTestResults.ts b/static/app/views/codecov/tests/queries/useGetTestResults.ts index ae919e30580ee2..5c23313b74781f 100644 --- a/static/app/views/codecov/tests/queries/useGetTestResults.ts +++ b/static/app/views/codecov/tests/queries/useGetTestResults.ts @@ -144,12 +144,12 @@ export function useInfiniteTestResults({ return result as ApiResult; }, - getNextPageParam: ([lastPage]) => { - return lastPage.pageInfo?.hasNextPage ? lastPage.pageInfo.endCursor : undefined; + getNextPageParam: ([pageData]) => { + return pageData.pageInfo?.hasNextPage ? pageData.pageInfo.endCursor : undefined; }, - getPreviousPageParam: ([firstPage]) => { - return firstPage.pageInfo?.hasPreviousPage - ? firstPage.pageInfo.startCursor + getPreviousPageParam: ([pageData]) => { + return pageData.pageInfo?.hasPreviousPage + ? pageData.pageInfo.startCursor : undefined; }, initialPageParam: null, diff --git a/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx b/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx index 2212be9d0da616..b8849fd87da311 100644 --- a/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx +++ b/static/app/views/codecov/tokens/repoTokenTable/hooks/useInfiniteRepositoryTokens.tsx @@ -77,12 +77,12 @@ export function useInfiniteRepositoryTokens({ return result as ApiResult; }, - getNextPageParam: ([lastPage]) => { - return lastPage.pageInfo?.hasNextPage ? lastPage.pageInfo.endCursor : undefined; + getNextPageParam: ([pageData]) => { + return pageData.pageInfo?.hasNextPage ? pageData.pageInfo.endCursor : undefined; }, - getPreviousPageParam: ([firstPage]) => { - return firstPage.pageInfo?.hasPreviousPage - ? firstPage.pageInfo.startCursor + getPreviousPageParam: ([pageData]) => { + return pageData.pageInfo?.hasPreviousPage + ? pageData.pageInfo.startCursor : undefined; }, initialPageParam: undefined,