From 11652daf8dbcc177ade5c8e60fd076bb1778854e Mon Sep 17 00:00:00 2001 From: nicholas-codecov Date: Fri, 13 Dec 2024 09:16:07 -0500 Subject: [PATCH 1/3] migrate useSelfHostedHasAdmins to SelfHostedHasAdminsQueryOpts --- ... => SelfHostedHasAdminsQueryOpts.test.tsx} | 35 ++++++++---- .../SelfHostedHasAdminsQueryOpts.ts | 50 ++++++++++++++++ src/services/selfHosted/index.ts | 1 - .../selfHosted/useSelfHostedHasAdmins.ts | 57 ------------------- 4 files changed, 74 insertions(+), 69 deletions(-) rename src/services/selfHosted/{useSelfHostedHasAdmins.test.tsx => SelfHostedHasAdminsQueryOpts.test.tsx} (54%) create mode 100644 src/services/selfHosted/SelfHostedHasAdminsQueryOpts.ts delete mode 100644 src/services/selfHosted/useSelfHostedHasAdmins.ts diff --git a/src/services/selfHosted/useSelfHostedHasAdmins.test.tsx b/src/services/selfHosted/SelfHostedHasAdminsQueryOpts.test.tsx similarity index 54% rename from src/services/selfHosted/useSelfHostedHasAdmins.test.tsx rename to src/services/selfHosted/SelfHostedHasAdminsQueryOpts.test.tsx index 29c317832a..169116532f 100644 --- a/src/services/selfHosted/useSelfHostedHasAdmins.test.tsx +++ b/src/services/selfHosted/SelfHostedHasAdminsQueryOpts.test.tsx @@ -1,48 +1,61 @@ -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + QueryClientProvider as QueryClientProviderV5, + QueryClient as QueryClientV5, + useQuery as useQueryV5, +} from '@tanstack/react-queryV5' import { renderHook, waitFor } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' -import { PropsWithChildren } from 'react' import { z } from 'zod' import { HasAdminsSchema, - useSelfHostedHasAdmins, -} from './useSelfHostedHasAdmins' + SelfHostedHasAdminsQueryOpts, +} from './SelfHostedHasAdminsQueryOpts' -const queryClient = new QueryClient({ +const queryClientV5 = new QueryClientV5({ defaultOptions: { queries: { retry: false } }, }) -const wrapper: React.FC = ({ children }) => ( - {children} +const wrapper: React.FC = ({ children }) => ( + + {children} + ) const server = setupServer() beforeAll(() => { server.listen() }) + beforeEach(() => { server.resetHandlers() - queryClient.clear() + queryClientV5.clear() }) + afterAll(() => { server.close() }) -describe('useSelfHostedHasAdmins', () => { - function setup({ data }: { data: z.infer }) { +interface SetupArgs { + data: z.infer +} + +describe('SelfHostedHasAdminsQueryOpts', () => { + function setup({ data }: SetupArgs) { server.use( graphql.query('HasAdmins', () => { return HttpResponse.json({ data }) }) ) } + describe('when called', () => { it('returns the user info', async () => { setup({ data: { config: { hasAdmins: true } } }) const { result } = renderHook( - () => useSelfHostedHasAdmins({ provider: 'gl' }), + () => useQueryV5(SelfHostedHasAdminsQueryOpts({ provider: 'gl' })), { wrapper } ) + await waitFor(() => expect(result.current.data).toEqual(true)) }) }) diff --git a/src/services/selfHosted/SelfHostedHasAdminsQueryOpts.ts b/src/services/selfHosted/SelfHostedHasAdminsQueryOpts.ts new file mode 100644 index 0000000000..d86bbfba2c --- /dev/null +++ b/src/services/selfHosted/SelfHostedHasAdminsQueryOpts.ts @@ -0,0 +1,50 @@ +import { + keepPreviousData, + queryOptions as queryOptionsV5, +} from '@tanstack/react-queryV5' +import { z } from 'zod' + +import Api from 'shared/api' +import { rejectNetworkError } from 'shared/api/helpers' + +export const HasAdminsSchema = z.object({ + config: z + .object({ + hasAdmins: z.boolean().nullable(), + }) + .nullable(), +}) + +const query = `query HasAdmins { config { hasAdmins } }` + +interface SelfHostedHasAdminsQueryArgs { + provider: string +} + +export const SelfHostedHasAdminsQueryOpts = ({ + provider, +}: SelfHostedHasAdminsQueryArgs) => { + return queryOptionsV5({ + queryKey: ['HasAdmins', provider], + queryFn: () => + Api.graphql({ + provider, + query, + }).then((res) => { + const parsedRes = HasAdminsSchema.safeParse(res?.data) + + if (!parsedRes.success) { + return rejectNetworkError({ + status: 404, + data: {}, + dev: 'SelfHostedHasAdminsQueryOpts - 404 schema parsing failed', + error: parsedRes.error, + }) + } + + return !!parsedRes?.data?.config?.hasAdmins + }), + // this is how TSQuery V5 handles keepPreviousData + placeholderData: keepPreviousData, + }) +} diff --git a/src/services/selfHosted/index.ts b/src/services/selfHosted/index.ts index 5e95f2a5bb..a7a9941144 100644 --- a/src/services/selfHosted/index.ts +++ b/src/services/selfHosted/index.ts @@ -1,4 +1,3 @@ export * from './useSelfHostedCurrentUser' export * from './useSelfHostedSeatsConfig' -export * from './useSelfHostedHasAdmins' export * from './useSelfHostedSeatsAndLicense' diff --git a/src/services/selfHosted/useSelfHostedHasAdmins.ts b/src/services/selfHosted/useSelfHostedHasAdmins.ts deleted file mode 100644 index 42ac4e39c8..0000000000 --- a/src/services/selfHosted/useSelfHostedHasAdmins.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { z } from 'zod' - -import Api from 'shared/api' -import { NetworkErrorObject, rejectNetworkError } from 'shared/api/helpers' - -export const HasAdminsSchema = z.object({ - config: z - .object({ - hasAdmins: z.boolean().nullable(), - }) - .nullable(), -}) - -function fetchHasAdmins({ provider }: { provider: string }) { - const query = ` - query HasAdmins { - config { - hasAdmins - } - } - ` - return Api.graphql({ - provider, - query, - }) -} -export const useSelfHostedHasAdmins = ( - { provider }: { provider: string }, - options = {} -) => { - const opts = { - select: (data: z.infer) => data?.config?.hasAdmins, - keepPreviousData: true, - ...options, - } - return useQuery({ - queryKey: ['hasAdmins', provider], - queryFn: () => - fetchHasAdmins({ - provider, - }).then((res) => { - const parsedRes = HasAdminsSchema.safeParse(res?.data) - - if (!parsedRes.success) { - return rejectNetworkError({ - status: 404, - data: {}, - dev: 'useSelfHostedHasAdmins - 404 schema parsing failed', - } satisfies NetworkErrorObject) - } - - return parsedRes?.data - }), - ...opts, - }) -} From 608f422e0081deaa5e88cfed35cf55f23df5e6aa Mon Sep 17 00:00:00 2001 From: nicholas-codecov Date: Fri, 13 Dec 2024 09:16:31 -0500 Subject: [PATCH 2/3] update usage in MissingDesignatedAdmins --- .../MissingDesignatedAdmins.test.jsx | 33 +++++++++++++------ .../MissingDesignatedAdmins.tsx | 8 ++--- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.test.jsx b/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.test.jsx index 3ce829d440..9e94c41f95 100644 --- a/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.test.jsx +++ b/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.test.jsx @@ -1,7 +1,12 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { + QueryClientProvider as QueryClientProviderV5, + QueryClient as QueryClientV5, +} from '@tanstack/react-queryV5' import { render, screen } from '@testing-library/react' import { graphql, HttpResponse } from 'msw' import { setupServer } from 'msw/node' +import { Suspense } from 'react' import { MemoryRouter, Route } from 'react-router-dom' import config from 'config' @@ -17,6 +22,23 @@ const mockApiCloud = { config: undefined } const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } }, }) +const queryClientV5 = new QueryClientV5({ + defaultOptions: { queries: { retry: false } }, +}) + +const wrapper = + (initialEntries = ['/gh/test-org/test-repo/pull/12']) => + ({ children }) => ( + + + + + Loading}>{children} + + + + + ) const server = setupServer() beforeAll(() => { @@ -25,6 +47,7 @@ beforeAll(() => { afterEach(() => { queryClient.clear() + queryClientV5.clear() server.resetHandlers() }) @@ -32,16 +55,6 @@ afterAll(() => { server.close() }) -const wrapper = - (initialEntries = ['/gh/test-org/test-repo/pull/12']) => - ({ children }) => ( - - - {children} - - - ) - describe('MissingDesignatedAdmins', () => { function setup(overrideData) { server.use( diff --git a/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.tsx b/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.tsx index 30cf1f123e..e59700e141 100644 --- a/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.tsx +++ b/src/shared/GlobalBanners/MissingDesignatedAdmins/MissingDesignatedAdmins.tsx @@ -1,8 +1,9 @@ +import { useSuspenseQuery as useSuspenseQueryV5 } from '@tanstack/react-queryV5' import { useParams } from 'react-router-dom' import config from 'config' -import { useSelfHostedHasAdmins } from 'services/selfHosted' +import { SelfHostedHasAdminsQueryOpts } from 'services/selfHosted/SelfHostedHasAdminsQueryOpts' import { Provider } from 'shared/api/helpers' import A from 'ui/A' import Banner from 'ui/Banner' @@ -33,9 +34,8 @@ interface URLParams { const MissingDesignatedAdmins = () => { const { provider } = useParams() - const { data: hasAdmins, isFetching } = useSelfHostedHasAdmins( - { provider }, - { enabled: !!provider && !!config.IS_SELF_HOSTED } + const { data: hasAdmins, isFetching } = useSuspenseQueryV5( + SelfHostedHasAdminsQueryOpts({ provider }) ) // This hook is purely side stepping the complexity rule here. const hideBanner = useHideBanner({ From 70d7b969aa694540eaa932cec3073beb2e56665c Mon Sep 17 00:00:00 2001 From: nicholas-codecov Date: Fri, 13 Dec 2024 09:16:45 -0500 Subject: [PATCH 3/3] make App tests happy --- src/App.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.test.tsx b/src/App.test.tsx index df9baf8032..9793a4473d 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -224,7 +224,7 @@ describe('App', () => { return HttpResponse.json({ data: {} }) }), graphql.query('HasAdmins', () => { - return HttpResponse.json({ data: {} }) + return HttpResponse.json({ data: { config: null } }) }), graphql.query('owner', () => { return HttpResponse.json({ data: { owner: { isAdmin: true } } })