Skip to content

Commit

Permalink
feat: Add mutation to update bundle cache settings (#3649)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholas-codecov authored Jan 13, 2025
1 parent daaed4b commit 6807f02
Show file tree
Hide file tree
Showing 2 changed files with 308 additions and 0 deletions.
197 changes: 197 additions & 0 deletions src/services/bundleAnalysis/useUpdateBundleCache.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import {
QueryClientProvider as QueryClientProviderV5,
QueryClient as QueryClientV5,
} from '@tanstack/react-queryV5'
import { act, renderHook, waitFor } from '@testing-library/react'
import { graphql, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'

import { useUpdateBundleCache } from './useUpdateBundleCache'

const mockSuccessfulResponse = {
data: {
updateBundleCacheConfig: {
results: [{ bundleName: 'bundle-1', isCached: true }],
error: null,
},
},
}

const mockParsingError = {
data: null,
errors: [{ message: 'Parsing error' }],
}

const mockUnauthenticatedError = {
data: {
updateBundleCacheConfig: {
results: null,
error: {
__typename: 'UnauthenticatedError',
message: 'Unauthenticated error',
},
},
},
}

const mockValidationError = {
data: {
updateBundleCacheConfig: {
results: null,
error: { __typename: 'ValidationError', message: 'Validation error' },
},
},
}

const queryClient = new QueryClientV5({
defaultOptions: { mutations: { retry: false } },
})

const wrapper: React.FC<React.PropsWithChildren> = ({ children }) => (
<QueryClientProviderV5 client={queryClient}>{children}</QueryClientProviderV5>
)

const server = setupServer()

beforeAll(() => {
server.listen()
})

afterEach(() => {
queryClient.clear()
server.resetHandlers()
})

afterAll(() => {
server.close()
})

interface SetupArgs {
isParsingError?: boolean
isUnauthenticatedError?: boolean
isValidationError?: boolean
}

describe('useUpdateBundleCache', () => {
function setup({
isParsingError = false,
isUnauthenticatedError = false,
isValidationError = false,
}: SetupArgs) {
server.use(
graphql.mutation('UpdateBundleCacheConfig', () => {
if (isParsingError) {
return HttpResponse.json(mockParsingError)
} else if (isUnauthenticatedError) {
return HttpResponse.json(mockUnauthenticatedError)
} else if (isValidationError) {
return HttpResponse.json(mockValidationError)
}
return HttpResponse.json(mockSuccessfulResponse)
})
)
}

describe('when the mutation is successful', () => {
it('returns the updated results', async () => {
setup({})
const { result } = renderHook(
() =>
useUpdateBundleCache({
provider: 'gh',
owner: 'owner',
repo: 'repo',
}),
{ wrapper }
)

act(() =>
result.current.mutate([{ bundleName: 'bundle-1', isCached: true }])
)
await waitFor(() => expect(result.current.isSuccess).toBe(true))

expect(result.current.data).toEqual([
{ bundleName: 'bundle-1', isCached: true },
])
})
})

describe('when the mutation fails', () => {
describe('when the mutation fails with a parsing error', () => {
it('returns a parsing error', async () => {
setup({ isParsingError: true })
const { result } = renderHook(
() =>
useUpdateBundleCache({
provider: 'gh',
owner: 'owner',
repo: 'repo',
}),
{ wrapper }
)

act(() =>
result.current.mutate([{ bundleName: 'bundle-1', isCached: true }])
)
await waitFor(() => expect(result.current.isError).toBe(true))

expect(result.current.error).toEqual({
data: {},
dev: 'useUpdateBundleCache - 400 failed to parse data',
status: 400,
})
})
})

describe('when the mutation fails with an unauthenticated error', () => {
it('returns an unauthenticated error', async () => {
setup({ isUnauthenticatedError: true })
const { result } = renderHook(
() =>
useUpdateBundleCache({
provider: 'gh',
owner: 'owner',
repo: 'repo',
}),
{ wrapper }
)

act(() =>
result.current.mutate([{ bundleName: 'bundle-1', isCached: true }])
)
await waitFor(() => expect(result.current.isError).toBe(true))

expect(result.current.error).toEqual({
error: 'UnauthenticatedError',
message: 'Unauthenticated error',
})
})
})

describe('when the mutation fails with a validation error', () => {
it('returns a validation error', async () => {
setup({ isValidationError: true })
const { result } = renderHook(
() =>
useUpdateBundleCache({
provider: 'gh',
owner: 'owner',
repo: 'repo',
}),
{ wrapper }
)

act(() =>
result.current.mutate([{ bundleName: 'bundle-1', isCached: true }])
)

await waitFor(() => expect(result.current.isError).toBe(true))

expect(result.current.error).toEqual({
error: 'ValidationError',
message: 'Validation error',
})
})
})
})
})
111 changes: 111 additions & 0 deletions src/services/bundleAnalysis/useUpdateBundleCache.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useMutation as useMutationV5 } from '@tanstack/react-queryV5'
import { z } from 'zod'

import Api from 'shared/api'
import { rejectNetworkError } from 'shared/api/helpers'

const UpdateBundleCacheInputSchema = z.array(
z.object({
bundleName: z.string(),
isCached: z.boolean(),
})
)

const MutationErrorSchema = z.discriminatedUnion('__typename', [
z.object({
__typename: z.literal('UnauthenticatedError'),
message: z.string(),
}),
z.object({
__typename: z.literal('ValidationError'),
message: z.string(),
}),
])

const MutationRequestSchema = z.object({
updateBundleCacheConfig: z
.object({
results: UpdateBundleCacheInputSchema.nullable(),
error: MutationErrorSchema.nullable(),
})
.nullable(),
})

const query = `
mutation UpdateBundleCacheConfig(
$owner: String!
$repo: String!
$bundles: [BundleCacheConfigInput!]!
) {
updateBundleCacheConfig(
input: { owner: $owner, repoName: $repo, bundles: $bundles }
) {
results {
bundleName
isCached
}
error {
__typename
... on UnauthenticatedError {
message
}
... on ValidationError {
message
}
}
}
}`

interface UseUpdateBundleCacheArgs {
provider: string
owner: string
repo: string
}

export const useUpdateBundleCache = ({
provider,
owner,
repo,
}: UseUpdateBundleCacheArgs) => {
return useMutationV5({
throwOnError: false,
mutationFn: (input: z.infer<typeof UpdateBundleCacheInputSchema>) => {
return Api.graphqlMutation({
provider,
query,
variables: { owner, repo, bundles: input },
mutationPath: 'updateBundleCache',
}).then((res) => {
const parsedData = MutationRequestSchema.safeParse(res.data)

if (!parsedData.success) {
return rejectNetworkError({
status: 400,
error: parsedData.error,
data: {},
dev: 'useUpdateBundleCache - 400 failed to parse data',
})
}

const updateBundleCacheConfig = parsedData.data.updateBundleCacheConfig
if (
updateBundleCacheConfig?.error?.__typename === 'UnauthenticatedError'
) {
return Promise.reject({
error: 'UnauthenticatedError',
message: updateBundleCacheConfig?.error?.message,
})
}

if (updateBundleCacheConfig?.error?.__typename === 'ValidationError') {
return Promise.reject({
error: 'ValidationError',
message: updateBundleCacheConfig?.error?.message,
})
}

return updateBundleCacheConfig?.results ?? []
})
},
})
}

0 comments on commit 6807f02

Please sign in to comment.