Skip to content

Commit 6af3a7e

Browse files
authored
Release v1.55.2 (#1319)
hotfix: handle null and undefined fields
2 parents bd31efb + 728cb35 commit 6af3a7e

File tree

6 files changed

+252
-9
lines changed

6 files changed

+252
-9
lines changed

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,5 @@
111111
"tsconfig-paths": "^4.2.0",
112112
"type-fest": "4.10.3"
113113
},
114-
"version": "1.55.1"
114+
"version": "1.55.2"
115115
}
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
import { describe, expect, it } from 'vitest'
2+
import type { SafeParseError } from 'zod'
3+
4+
import schema from '../../auth/schema'
5+
6+
describe('gathersg auth schema', () => {
7+
describe('valid cases', () => {
8+
it('accepts updatedBy with email and name', () => {
9+
const result = schema.safeParse({
10+
updatedBy: {
11+
12+
name: 'John Doe',
13+
},
14+
})
15+
expect(result.success).toBe(true)
16+
})
17+
18+
it('accepts createdBy with email and name', () => {
19+
const result = schema.safeParse({
20+
createdBy: {
21+
22+
name: 'Jane Doe',
23+
},
24+
})
25+
expect(result.success).toBe(true)
26+
})
27+
28+
it('accepts both updatedBy and createdBy when updatedBy has email', () => {
29+
const result = schema.safeParse({
30+
updatedBy: {
31+
32+
name: 'Updater',
33+
},
34+
createdBy: {
35+
36+
name: 'Creator',
37+
},
38+
})
39+
expect(result.success).toBe(true)
40+
})
41+
42+
it('accepts FormSG createdBy with formsg data', () => {
43+
const result = schema.safeParse({
44+
createdBy: {
45+
name: 'FormSG',
46+
},
47+
formsg: {
48+
formId: 'form-123',
49+
submissionId: 'submission-456',
50+
},
51+
})
52+
expect(result.success).toBe(true)
53+
})
54+
55+
it('accepts FormSG createdBy without email when formsg data is present', () => {
56+
const result = schema.safeParse({
57+
createdBy: {
58+
name: 'FormSG',
59+
},
60+
formsg: {
61+
formId: '64abc123def456',
62+
submissionId: 'sub-789xyz',
63+
},
64+
})
65+
expect(result.success).toBe(true)
66+
})
67+
})
68+
69+
describe('invalid cases - missing email', () => {
70+
it('rejects updatedBy without email', () => {
71+
const result = schema.safeParse({
72+
updatedBy: {
73+
name: 'John Doe',
74+
},
75+
})
76+
expect(result.success).toBe(false)
77+
})
78+
79+
it('rejects createdBy without email when not FormSG', () => {
80+
const result = schema.safeParse({
81+
createdBy: {
82+
name: 'Regular User',
83+
},
84+
})
85+
expect(result.success).toBe(false)
86+
if (!result.success) {
87+
const { issues } = (result as SafeParseError<unknown>).error
88+
expect(
89+
issues.some((i) => i.message.includes('createdBy.email is required')),
90+
).toBe(true)
91+
}
92+
})
93+
94+
it('rejects when both updatedBy and createdBy exist but updatedBy has no email', () => {
95+
const result = schema.safeParse({
96+
updatedBy: {
97+
name: 'Updater',
98+
},
99+
createdBy: {
100+
101+
name: 'Creator',
102+
},
103+
})
104+
expect(result.success).toBe(false)
105+
})
106+
107+
it('rejects empty object (neither updatedBy nor createdBy)', () => {
108+
const result = schema.safeParse({})
109+
expect(result.success).toBe(false)
110+
})
111+
112+
it('rejects null values', () => {
113+
const result = schema.safeParse({
114+
updatedBy: null,
115+
createdBy: null,
116+
})
117+
expect(result.success).toBe(false)
118+
})
119+
})
120+
121+
describe('FormSG invalid cases', () => {
122+
it('rejects FormSG createdBy without formId', () => {
123+
const result = schema.safeParse({
124+
createdBy: {
125+
name: 'FormSG',
126+
},
127+
formsg: {
128+
submissionId: 'submission-456',
129+
},
130+
})
131+
expect(result.success).toBe(false)
132+
})
133+
134+
it('rejects FormSG createdBy without submissionId', () => {
135+
const result = schema.safeParse({
136+
createdBy: {
137+
name: 'FormSG',
138+
},
139+
formsg: {
140+
formId: 'form-123',
141+
},
142+
})
143+
expect(result.success).toBe(false)
144+
})
145+
146+
it('rejects FormSG createdBy with empty formId', () => {
147+
const result = schema.safeParse({
148+
createdBy: {
149+
name: 'FormSG',
150+
},
151+
formsg: {
152+
formId: '',
153+
submissionId: 'submission-456',
154+
},
155+
})
156+
expect(result.success).toBe(false)
157+
})
158+
159+
it('rejects FormSG createdBy with empty submissionId', () => {
160+
const result = schema.safeParse({
161+
createdBy: {
162+
name: 'FormSG',
163+
},
164+
formsg: {
165+
formId: 'form-123',
166+
submissionId: '',
167+
},
168+
})
169+
expect(result.success).toBe(false)
170+
})
171+
172+
it('rejects FormSG createdBy without formsg object', () => {
173+
const result = schema.safeParse({
174+
createdBy: {
175+
name: 'FormSG',
176+
},
177+
})
178+
expect(result.success).toBe(false)
179+
})
180+
})
181+
182+
describe('empty string validation', () => {
183+
it('rejects updatedBy with empty email', () => {
184+
const result = schema.safeParse({
185+
updatedBy: {
186+
email: '',
187+
name: 'John Doe',
188+
},
189+
})
190+
expect(result.success).toBe(false)
191+
})
192+
193+
it('rejects updatedBy with empty name', () => {
194+
const result = schema.safeParse({
195+
updatedBy: {
196+
197+
name: '',
198+
},
199+
})
200+
expect(result.success).toBe(false)
201+
})
202+
203+
it('rejects createdBy with empty name', () => {
204+
const result = schema.safeParse({
205+
createdBy: {
206+
207+
name: '',
208+
},
209+
})
210+
expect(result.success).toBe(false)
211+
})
212+
})
213+
214+
describe('precedence logic', () => {
215+
it('prioritizes updatedBy.email when both updatedBy and createdBy exist', () => {
216+
// This should pass because updatedBy has email, even though createdBy doesn't
217+
const result = schema.safeParse({
218+
updatedBy: {
219+
220+
name: 'Updater',
221+
},
222+
createdBy: {
223+
name: 'Creator',
224+
},
225+
})
226+
expect(result.success).toBe(true)
227+
})
228+
229+
it('validates updatedBy.email presence when both exist', () => {
230+
// This should fail because updatedBy doesn't have email
231+
const result = schema.safeParse({
232+
updatedBy: {
233+
name: 'Updater',
234+
},
235+
createdBy: {
236+
237+
name: 'Creator',
238+
},
239+
})
240+
expect(result.success).toBe(false)
241+
})
242+
})
243+
})

packages/backend/src/apps/gathersg/auth/schema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ const schema = z
1212
email: z.string().min(1),
1313
name: z.string().min(1),
1414
})
15-
.optional(),
15+
.nullish(),
1616
createdBy: z
1717
.object({
1818
email: z.string().min(1).optional(),
1919
name: z.string().min(1).optional(),
2020
})
21-
.optional(),
21+
.nullish(),
2222
formsg: z
2323
.object({
2424
formId: z.string().min(1),
2525
submissionId: z.string().min(1),
2626
})
27-
.optional(),
27+
.nullish(),
2828
})
2929
.refine(
3030
(data) => {

packages/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "frontend",
3-
"version": "1.55.1",
3+
"version": "1.55.2",
44
"type": "module",
55
"scripts": {
66
"dev": "wait-on tcp:3000 && vite --host --force",

packages/types/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"name": "@plumber/types",
33
"description": "Shared types for plumber",
44
"types": "./index.d.ts",
5-
"version": "1.55.1"
5+
"version": "1.55.2"
66
}

0 commit comments

Comments
 (0)