Skip to content

Commit 3f6c70f

Browse files
authored
refactor: remove bind args validation errors result and shaper function, throw them instead (#337)
This PR removes the shaper `handleBindArgsValidationErrorsShape` util function, and also removes `bindArgsValidationErrors` from the result. Instead, if bound args validation errors occur, throw them on the server, using a new `ActionBindArgsValidationError` error class.
1 parent 919bb56 commit 3f6c70f

14 files changed

+156
-881
lines changed

packages/next-safe-action/src/__tests__/action-callbacks.test.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ test("action with input schemas and server error calls `onError` and `onSettled`
165165

166166
test("action with validation errors calls `onError` and `onSettled` callbacks with correct arguments", async () => {
167167
let executed = 0;
168-
const inputs = ["invalid_uuid", -30, { username: "j" }] as const;
168+
const inputs = [crypto.randomUUID(), 30, { username: "j" }] as const;
169169

170170
const action = ac
171171
.schema(z.object({ username: z.string().min(3) }))
@@ -194,14 +194,6 @@ test("action with validation errors calls `onError` and `onSettled` callbacks wi
194194
_errors: ["String must contain at least 3 character(s)"],
195195
},
196196
},
197-
bindArgsValidationErrors: [
198-
{
199-
_errors: ["Invalid uuid"],
200-
},
201-
{
202-
_errors: ["Number must be greater than 0"],
203-
},
204-
],
205197
},
206198
clientInput: inputs[2],
207199
bindArgsClientInputs: inputs.slice(0, 2),
@@ -222,14 +214,6 @@ test("action with validation errors calls `onError` and `onSettled` callbacks wi
222214
_errors: ["String must contain at least 3 character(s)"],
223215
},
224216
},
225-
bindArgsValidationErrors: [
226-
{
227-
_errors: ["Invalid uuid"],
228-
},
229-
{
230-
_errors: ["Number must be greater than 0"],
231-
},
232-
],
233217
},
234218
clientInput: inputs[2],
235219
bindArgsClientInputs: inputs.slice(0, 2),

packages/next-safe-action/src/__tests__/bind-args-validation-errors.test.ts

Lines changed: 32 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,18 @@
33
import assert from "node:assert";
44
import { test } from "node:test";
55
import { z } from "zod";
6-
import { createSafeActionClient, flattenBindArgsValidationErrors, formatBindArgsValidationErrors } from "..";
6+
import { createSafeActionClient, DEFAULT_SERVER_ERROR_MESSAGE } from "..";
7+
import { ActionBindArgsValidationError } from "../validation-errors";
78

89
// Default client tests.
910

1011
const dac = createSafeActionClient();
1112

12-
test("action with invalid bind args input gives back an object with correct `bindArgsValidationErrors` (default formatted shape)", async () => {
13-
const bindArgsSchemas: [age: z.ZodNumber, userId: z.ZodString, product: z.ZodObject<{ id: z.ZodString }>] = [
14-
z.number().positive(),
15-
z.string().uuid(),
16-
z.object({
17-
id: z.string().uuid(),
18-
}),
19-
];
20-
21-
const action = dac.bindArgsSchemas(bindArgsSchemas).action(async () => {
22-
return {
23-
ok: true,
24-
};
13+
test("action with invalid bind args input and valid main input gives back a server error", async () => {
14+
const schema = z.object({
15+
username: z.string().min(3),
2516
});
2617

27-
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" });
28-
29-
const expectedResult = {
30-
bindArgsValidationErrors: [
31-
{
32-
_errors: ["Number must be greater than 0"],
33-
},
34-
{},
35-
{
36-
id: {
37-
_errors: ["Invalid uuid"],
38-
},
39-
},
40-
],
41-
};
42-
43-
assert.deepStrictEqual(actualResult, expectedResult);
44-
});
45-
46-
test("action with invalid bind args input gives back an object with correct `bindArgsValidationErrors` (default formatted shape overridden by custom flattened shape)", async () => {
4718
const bindArgsSchemas: [age: z.ZodNumber, userId: z.ZodString, product: z.ZodObject<{ id: z.ZodString }>] = [
4819
z.number().positive(),
4920
z.string().uuid(),
@@ -53,129 +24,40 @@ test("action with invalid bind args input gives back an object with correct `bin
5324
];
5425

5526
const action = dac
56-
.bindArgsSchemas(bindArgsSchemas, {
57-
handleBindArgsValidationErrorsShape: async (ve) => flattenBindArgsValidationErrors(ve),
58-
})
27+
.schema(schema)
28+
.bindArgsSchemas(bindArgsSchemas)
5929
.action(async () => {
6030
return {
6131
ok: true,
6232
};
6333
});
6434

65-
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" });
35+
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" }, { username: "johndoe" });
6636

6737
const expectedResult = {
68-
bindArgsValidationErrors: [
69-
{
70-
formErrors: ["Number must be greater than 0"],
71-
fieldErrors: {},
72-
},
73-
{
74-
formErrors: [],
75-
fieldErrors: {},
76-
},
77-
{
78-
formErrors: [],
79-
fieldErrors: {
80-
id: ["Invalid uuid"],
81-
},
82-
},
83-
],
38+
serverError: DEFAULT_SERVER_ERROR_MESSAGE,
8439
};
8540

8641
assert.deepStrictEqual(actualResult, expectedResult);
8742
});
8843

89-
// Formatted shape tests (same as default).
90-
91-
const foac = createSafeActionClient({
92-
defaultValidationErrorsShape: "formatted",
93-
});
94-
95-
test("action with invalid bind args input gives back an object with correct `bindArgsValidationErrors` (set formatted shape)", async () => {
96-
const bindArgsSchemas: [age: z.ZodNumber, userId: z.ZodString, product: z.ZodObject<{ id: z.ZodString }>] = [
97-
z.number().positive(),
98-
z.string().uuid(),
99-
z.object({
100-
id: z.string().uuid(),
101-
}),
102-
];
103-
104-
const action = foac.bindArgsSchemas(bindArgsSchemas).action(async () => {
105-
return {
106-
ok: true,
107-
};
108-
});
109-
110-
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" });
111-
112-
const expectedResult = {
113-
bindArgsValidationErrors: [
114-
{
115-
_errors: ["Number must be greater than 0"],
116-
},
117-
{},
118-
{
119-
id: {
120-
_errors: ["Invalid uuid"],
121-
},
122-
},
123-
],
124-
};
125-
126-
assert.deepStrictEqual(actualResult, expectedResult);
127-
});
44+
// Unmasked server error client.
12845

129-
test("action with invalid bind args input gives back an object with correct `bindArgsValidationErrors` (set formatted shape overridden by custom flattened shape)", async () => {
130-
const bindArgsSchemas: [age: z.ZodNumber, userId: z.ZodString, product: z.ZodObject<{ id: z.ZodString }>] = [
131-
z.number().positive(),
132-
z.string().uuid(),
133-
z.object({
134-
id: z.string().uuid(),
135-
}),
136-
];
137-
138-
const action = foac
139-
.bindArgsSchemas(bindArgsSchemas, {
140-
handleBindArgsValidationErrorsShape: async (ve) => flattenBindArgsValidationErrors(ve),
141-
})
142-
.action(async () => {
46+
const uac = createSafeActionClient({
47+
handleServerError(error) {
48+
if (error instanceof ActionBindArgsValidationError) {
14349
return {
144-
ok: true,
50+
bindArgsValidationErrors: error.validationErrors,
14551
};
146-
});
52+
}
14753

148-
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" });
149-
150-
const expectedResult = {
151-
bindArgsValidationErrors: [
152-
{
153-
formErrors: ["Number must be greater than 0"],
154-
fieldErrors: {},
155-
},
156-
{
157-
formErrors: [],
158-
fieldErrors: {},
159-
},
160-
{
161-
formErrors: [],
162-
fieldErrors: {
163-
id: ["Invalid uuid"],
164-
},
165-
},
166-
],
167-
};
168-
169-
assert.deepStrictEqual(actualResult, expectedResult);
170-
});
171-
172-
// Flattened shape tests.
173-
174-
const flac = createSafeActionClient({
175-
defaultValidationErrorsShape: "flattened",
54+
return {
55+
message: error.message,
56+
};
57+
},
17658
});
17759

178-
test("action with invalid bind args input gives back an object with correct `bindArgsValidationErrors` (set flattened shape)", async () => {
60+
test("action with invalid bind args input gives back a server error object with correct `bindArgsValidationErrors` property", async () => {
17961
const bindArgsSchemas: [age: z.ZodNumber, userId: z.ZodString, product: z.ZodObject<{ id: z.ZodString }>] = [
18062
z.number().positive(),
18163
z.string().uuid(),
@@ -184,7 +66,7 @@ test("action with invalid bind args input gives back an object with correct `bin
18466
}),
18567
];
18668

187-
const action = flac.bindArgsSchemas(bindArgsSchemas).action(async () => {
69+
const action = uac.bindArgsSchemas(bindArgsSchemas).action(async () => {
18870
return {
18971
ok: true,
19072
};
@@ -193,60 +75,19 @@ test("action with invalid bind args input gives back an object with correct `bin
19375
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" });
19476

19577
const expectedResult = {
196-
bindArgsValidationErrors: [
197-
{
198-
formErrors: ["Number must be greater than 0"],
199-
fieldErrors: {},
200-
},
201-
{
202-
formErrors: [],
203-
fieldErrors: {},
204-
},
205-
{
206-
formErrors: [],
207-
fieldErrors: {
208-
id: ["Invalid uuid"],
78+
serverError: {
79+
bindArgsValidationErrors: [
80+
{
81+
_errors: ["Number must be greater than 0"],
20982
},
210-
},
211-
],
212-
};
213-
214-
assert.deepStrictEqual(actualResult, expectedResult);
215-
});
216-
217-
test("action with invalid bind args input gives back an object with correct `bindArgsValidationErrors` (set flattened shape overridden by custom formatted shape)", async () => {
218-
const bindArgsSchemas: [age: z.ZodNumber, userId: z.ZodString, product: z.ZodObject<{ id: z.ZodString }>] = [
219-
z.number().positive(),
220-
z.string().uuid(),
221-
z.object({
222-
id: z.string().uuid(),
223-
}),
224-
];
225-
226-
const action = flac
227-
.bindArgsSchemas(bindArgsSchemas, {
228-
handleBindArgsValidationErrorsShape: async (ve) => formatBindArgsValidationErrors(ve),
229-
})
230-
.action(async () => {
231-
return {
232-
ok: true,
233-
};
234-
});
235-
236-
const actualResult = await action(-123, crypto.randomUUID(), { id: "invalid_uuid" });
237-
238-
const expectedResult = {
239-
bindArgsValidationErrors: [
240-
{
241-
_errors: ["Number must be greater than 0"],
242-
},
243-
{},
244-
{
245-
id: {
246-
_errors: ["Invalid uuid"],
83+
{},
84+
{
85+
id: {
86+
_errors: ["Invalid uuid"],
87+
},
24788
},
248-
},
249-
],
89+
],
90+
},
25091
};
25192

25293
assert.deepStrictEqual(actualResult, expectedResult);

0 commit comments

Comments
 (0)