Skip to content

Commit

Permalink
Fix invalid data check in query/mutation fn (#1975)
Browse files Browse the repository at this point in the history
* check specifically for undefined in data for throwing errors

* add changeset

* add biome as default formatter for typescript react

* improve data checks in query/mutation fns

* add tests

* test fix

* add hugeletters to contributs list of react-query package

* dont throw in mutations on undefined

* fix changeset

* fix mutation tests

---------

Co-authored-by: Evgenii Perminov <[email protected]>
  • Loading branch information
HugeLetters and Evgenii Perminov authored Nov 13, 2024
1 parent d29e53f commit 621792f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .changeset/spotty-flies-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"openapi-react-query": patch
---

- Fixed empty value check in queryFn: only throws error for undefined, other falsy values are allowed
- Fixed empty value check in mutationFn: allow falsy values
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"[typescript][typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
2 changes: 1 addition & 1 deletion docs/scripts/update-contributors.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ const CONTRIBUTORS = {
"armandabric",
"illright",
]),
"openapi-react-query": new Set(["drwpow", "kerwanp", "yoshi2no"]),
"openapi-react-query": new Set(["drwpow", "kerwanp", "yoshi2no", "HugeLetters"]),
"swr-openapi": new Set(["htunnicliff"]),
"openapi-metadata": new Set(["kerwanp", "drwpow"]),
};
Expand Down
8 changes: 5 additions & 3 deletions packages/openapi-react-query/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ export default function createClient<Paths extends {}, Media extends MediaType =
const mth = method.toUpperCase() as Uppercase<typeof method>;
const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
const { data, error } = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any
if (error || !data) {
if (error) {
throw error;
}

return data;
};

Expand All @@ -141,10 +142,11 @@ export default function createClient<Paths extends {}, Media extends MediaType =
const mth = method.toUpperCase() as Uppercase<typeof method>;
const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
const { data, error } = await fn(path, init as InitWithUnknowns<typeof init>);
if (error || !data) {
if (error) {
throw error;
}
return data;

return data as Exclude<typeof data, undefined>;
},
...options,
},
Expand Down
92 changes: 92 additions & 0 deletions packages/openapi-react-query/test/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,50 @@ describe("client", () => {
expect(data).toBeUndefined();
});

it("should resolve data properly and have error as null when queryFn returns null", async () => {
const fetchClient = createFetchClient<paths>({ baseUrl });
const client = createClient(fetchClient);

useMockRequestHandler({
baseUrl,
method: "get",
path: "/string-array",
status: 200,
body: null,
});

const { result } = renderHook(() => client.useQuery("get", "/string-array"), { wrapper });

await waitFor(() => expect(result.current.isFetching).toBe(false));

const { data, error } = result.current;

expect(data).toBeNull();
expect(error).toBeNull();
});

it("should resolve error properly and have undefined data when queryFn returns undefined", async () => {
const fetchClient = createFetchClient<paths>({ baseUrl });
const client = createClient(fetchClient);

useMockRequestHandler({
baseUrl,
method: "get",
path: "/string-array",
status: 200,
body: undefined,
});

const { result } = renderHook(() => client.useQuery("get", "/string-array"), { wrapper });

await waitFor(() => expect(result.current.isFetching).toBe(false));

const { data, error } = result.current;

expect(error).toBeInstanceOf(Error);
expect(data).toBeUndefined();
});

it("should infer correct data and error type", async () => {
const fetchClient = createFetchClient<paths>({ baseUrl, fetch: fetchInfinite });
const client = createClient(fetchClient);
Expand Down Expand Up @@ -560,6 +604,54 @@ describe("client", () => {
expect(error?.message).toBe("Something went wrong");
});

it("should resolve data properly and have error as null when mutationFn returns null", async () => {
const fetchClient = createFetchClient<paths>({ baseUrl });
const client = createClient(fetchClient);

useMockRequestHandler({
baseUrl,
method: "put",
path: "/comment",
status: 200,
body: null,
});

const { result } = renderHook(() => client.useMutation("put", "/comment"), { wrapper });

result.current.mutate({ body: { message: "Hello", replied_at: 0 } });

await waitFor(() => expect(result.current.isPending).toBe(false));

const { data, error } = result.current;

expect(data).toBeNull();
expect(error).toBeNull();
});

it("should resolve data properly and have error as null when mutationFn returns undefined", async () => {
const fetchClient = createFetchClient<paths>({ baseUrl });
const client = createClient(fetchClient);

useMockRequestHandler({
baseUrl,
method: "put",
path: "/comment",
status: 200,
body: undefined,
});

const { result } = renderHook(() => client.useMutation("put", "/comment"), { wrapper });

result.current.mutate({ body: { message: "Hello", replied_at: 0 } });

await waitFor(() => expect(result.current.isPending).toBe(false));

const { data, error } = result.current;

expect(error).toBeNull();
expect(data).toBeUndefined();
});

it("should use provided custom queryClient", async () => {
const fetchClient = createFetchClient<paths>({ baseUrl });
const client = createClient(fetchClient);
Expand Down

0 comments on commit 621792f

Please sign in to comment.