Skip to content

Commit 2da6d92

Browse files
dave-wwgDave Ryan
andauthored
fix: validate "null" value for point field as true when its not required (#12908)
### What? This PR solves an issue with validation of the `point` field in Payload CMS. If the value is `null` and the field is not required, the validation will return `true` before trying to examine the contents of the field ### Why? If the point field is given a value, and saved, it is then impossible to successfully "unset" the point field, either through the CMS UI or through a hook like `beforeChange`. Trying to do so will throw this error: ``` [17:09:41] ERROR: Cannot read properties of null (reading '0') err: { "type": "TypeError", "message": "Cannot read properties of null (reading '0')", "stack": TypeError: Cannot read properties of null (reading '0') at point (webpack-internal:///(rsc)/./node_modules/.pnpm/payload@3.43.0_graphql@16.10.0_typescript@5.7.3/node_modules/payload/dist/fields/validations.js:622:40) ``` because a value of `null` will not be changed to the default value of `['','']`, which in any case does not pass MongoDB validation either. ``` [17:22:49] ERROR: Cast to [Number] failed for value "[ NaN, NaN ]" (type string) at path "location.coordinates.0" because of "CastError" err: { "type": "CastError", "message": "Cast to [Number] failed for value \"[ NaN, NaN ]\" (type string) at path \"location.coordinates.0\" because of \"CastError\"", "stack": CastError: Cast to [Number] failed for value "[ NaN, NaN ]" (type string) at path "location.coordinates.0" because of "CastError" at SchemaArray.cast (webpack-internal:///(rsc)/./node_modules/.pnpm/mongoose@8.15.1_@aws-sdk+credential-providers@3.778.0/node_modules/mongoose/lib/schema/array.js:414:15) ``` ### How? This adds a check to the top of the `point` validation function and returns early before trying to examine the contents of the point field --------- Co-authored-by: Dave Ryan <dmr@Daves-MacBook-Pro.local>
1 parent 86e48ae commit 2da6d92

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

packages/payload/src/fields/validations.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -937,19 +937,27 @@ export type PointFieldValidation = Validate<
937937
>
938938

939939
export const point: PointFieldValidation = (value = ['', ''], { req: { t }, required }) => {
940-
const lng = parseFloat(String(value![0]))
941-
const lat = parseFloat(String(value![1]))
940+
if (value === null) {
941+
if (required) {
942+
return t('validation:required')
943+
}
944+
945+
return true
946+
}
947+
948+
const lng = parseFloat(String(value[0]))
949+
const lat = parseFloat(String(value[1]))
942950
if (
943951
required &&
944-
((value![0] && value![1] && typeof lng !== 'number' && typeof lat !== 'number') ||
952+
((value[0] && value[1] && typeof lng !== 'number' && typeof lat !== 'number') ||
945953
Number.isNaN(lng) ||
946954
Number.isNaN(lat) ||
947955
(Array.isArray(value) && value.length !== 2))
948956
) {
949957
return t('validation:requiresTwoNumbers')
950958
}
951959

952-
if ((value![1] && Number.isNaN(lng)) || (value![0] && Number.isNaN(lat))) {
960+
if ((value[1] && Number.isNaN(lng)) || (value[0] && Number.isNaN(lat))) {
953961
return t('validation:invalidInput')
954962
}
955963

test/fields/int.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,58 @@ describe('Fields', () => {
13961396
expect(doc.localized).toEqual(localized)
13971397
expect(doc.group).toMatchObject(group)
13981398
})
1399+
1400+
it('should throw validation error when "required" field is set to null', async () => {
1401+
if (payload.db.name === 'sqlite') {
1402+
return
1403+
}
1404+
// first create the point field
1405+
doc = await payload.create({
1406+
collection: 'point-fields',
1407+
data: {
1408+
localized,
1409+
point,
1410+
},
1411+
})
1412+
1413+
// try to update the required field to null
1414+
await expect(() =>
1415+
payload.update({
1416+
collection: 'point-fields',
1417+
data: {
1418+
point: null,
1419+
},
1420+
id: doc.id,
1421+
}),
1422+
).rejects.toThrow('The following field is invalid: Location')
1423+
})
1424+
1425+
it('should not throw validation error when non-"required" field is set to null', async () => {
1426+
if (payload.db.name === 'sqlite') {
1427+
return
1428+
}
1429+
// first create the point field
1430+
doc = await payload.create({
1431+
collection: 'point-fields',
1432+
data: {
1433+
localized,
1434+
point,
1435+
},
1436+
})
1437+
1438+
expect(doc.localized).toEqual(localized)
1439+
1440+
// try to update the non-required field to null
1441+
const updatedDoc = await payload.update({
1442+
collection: 'point-fields',
1443+
data: {
1444+
localized: null,
1445+
},
1446+
id: doc.id,
1447+
})
1448+
1449+
expect(updatedDoc.localized).toEqual(undefined)
1450+
})
13991451
})
14001452

14011453
describe('checkbox', () => {

0 commit comments

Comments
 (0)