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>
This commit is contained in:
Dave Ryan
2025-06-27 02:49:47 -05:00
committed by GitHub
parent 86e48ae70b
commit 2da6d924de
2 changed files with 64 additions and 4 deletions

View File

@@ -937,11 +937,19 @@ export type PointFieldValidation = Validate<
>
export const point: PointFieldValidation = (value = ['', ''], { req: { t }, required }) => {
const lng = parseFloat(String(value![0]))
const lat = parseFloat(String(value![1]))
if (value === null) {
if (required) {
return t('validation:required')
}
return true
}
const lng = parseFloat(String(value[0]))
const lat = parseFloat(String(value[1]))
if (
required &&
((value![0] && value![1] && typeof lng !== 'number' && typeof lat !== 'number') ||
((value[0] && value[1] && typeof lng !== 'number' && typeof lat !== 'number') ||
Number.isNaN(lng) ||
Number.isNaN(lat) ||
(Array.isArray(value) && value.length !== 2))
@@ -949,7 +957,7 @@ export const point: PointFieldValidation = (value = ['', ''], { req: { t }, requ
return t('validation:requiresTwoNumbers')
}
if ((value![1] && Number.isNaN(lng)) || (value![0] && Number.isNaN(lat))) {
if ((value[1] && Number.isNaN(lng)) || (value[0] && Number.isNaN(lat))) {
return t('validation:invalidInput')
}

View File

@@ -1396,6 +1396,58 @@ describe('Fields', () => {
expect(doc.localized).toEqual(localized)
expect(doc.group).toMatchObject(group)
})
it('should throw validation error when "required" field is set to null', async () => {
if (payload.db.name === 'sqlite') {
return
}
// first create the point field
doc = await payload.create({
collection: 'point-fields',
data: {
localized,
point,
},
})
// try to update the required field to null
await expect(() =>
payload.update({
collection: 'point-fields',
data: {
point: null,
},
id: doc.id,
}),
).rejects.toThrow('The following field is invalid: Location')
})
it('should not throw validation error when non-"required" field is set to null', async () => {
if (payload.db.name === 'sqlite') {
return
}
// first create the point field
doc = await payload.create({
collection: 'point-fields',
data: {
localized,
point,
},
})
expect(doc.localized).toEqual(localized)
// try to update the non-required field to null
const updatedDoc = await payload.update({
collection: 'point-fields',
data: {
localized: null,
},
id: doc.id,
})
expect(updatedDoc.localized).toEqual(undefined)
})
})
describe('checkbox', () => {