fix: validate blocknote JSON in rich text fields (#18902)
## Summary - **Backend**: Add JSON validation for the `blocknote` subfield in rich text API inputs — rejects values that aren't valid JSON or aren't arrays (BlockNote content is always `PartialBlock[]`). This prevents corrupted data from being persisted to the database. - **Frontend**: Replace all 5 unprotected `JSON.parse` calls on blocknote content with the safe `parseJson` utility from `twenty-shared`. Invalid content now degrades gracefully (empty block / empty string / unchanged passthrough) instead of crashing the app. - **Tests**: Added integration tests for invalid blocknote JSON (both GraphQL and REST), unit tests for the new validation, and updated existing test constants to use valid BlockNote JSON. ## Context A user reported a `SyntaxError: Expected ',' or ']' after array element` crash caused by malformed blocknote JSON stored in the database. The data had `"children":[]` nested inside the `content` array instead of as a sibling property. The API accepted this invalid JSON because it only validated that `blocknote` was a string, not that it contained valid JSON. On the frontend, 5 call sites used bare `JSON.parse` with no error handling, causing a white-screen crash. ## Test plan - [x] Unit tests pass: `validate-rich-text-field-or-throw.util.spec.ts` (10/10) - [x] Integration tests pass: `rich-text-field-create-input-validation` (8/8) - [ ] Verify creating a note with valid rich text still works end-to-end - [ ] Verify API returns clear error when blocknote contains invalid JSON - [ ] Verify frontend renders empty block instead of crashing when encountering corrupted data 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
+9
-1
@@ -2,4 +2,12 @@
|
||||
|
||||
exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":"not-a-rich-text"} 1`] = `"Expected type "RichTextCreateInput" to be an object."`;
|
||||
|
||||
exports[`Create input validation - RICH_TEXT Rest create input - failure RICH_TEXT - should fail with : {"richTextField":"not-a-rich-text"} 1`] = `"["Invalid rich text v2 value 'not-a-rich-text' for field \\"richTextField\\" - Should be an object"]"`;
|
||||
exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":{"blocknote":"[{\\"id\\":\\"1\\",\\"type\\":\\"paragraph\\",\\"props\\":{},\\"content\\":[{\\"type\\":\\"text\\",\\"text\\":\\"test\\"},\\"children\\":[]}]"}} 1`] = `"Invalid blocknote value for field "richTextField.blocknote" - must contain valid JSON"`;
|
||||
|
||||
exports[`Create input validation - RICH_TEXT Gql create input - failure RICH_TEXT - should fail with : {"richTextField":{"blocknote":"invalid-json"}} 1`] = `"Invalid blocknote value for field "richTextField.blocknote" - must contain valid JSON"`;
|
||||
|
||||
exports[`Create input validation - RICH_TEXT Rest create input - failure RICH_TEXT - should fail with : {"richTextField":"not-a-rich-text"} 1`] = `"["Invalid object value 'not-a-rich-text' for field \\"richTextField\\""]"`;
|
||||
|
||||
exports[`Create input validation - RICH_TEXT Rest create input - failure RICH_TEXT - should fail with : {"richTextField":{"blocknote":"[{\\"id\\":\\"1\\",\\"type\\":\\"paragraph\\",\\"props\\":{},\\"content\\":[{\\"type\\":\\"text\\",\\"text\\":\\"test\\"},\\"children\\":[]}]"}} 1`] = `"["Invalid blocknote value for field \\"richTextField.blocknote\\" - must contain valid JSON"]"`;
|
||||
|
||||
exports[`Create input validation - RICH_TEXT Rest create input - failure RICH_TEXT - should fail with : {"richTextField":{"blocknote":"invalid-json"}} 1`] = `"["Invalid blocknote value for field \\"richTextField.blocknote\\" - must contain valid JSON"]"`;
|
||||
|
||||
+15
@@ -230,6 +230,21 @@ export const failingCreateInputByFieldMetadataType: {
|
||||
richTextField: 'not-a-rich-text',
|
||||
},
|
||||
},
|
||||
{
|
||||
input: {
|
||||
richTextField: {
|
||||
blocknote: 'invalid-json',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: {
|
||||
richTextField: {
|
||||
blocknote:
|
||||
'[{"id":"1","type":"paragraph","props":{},"content":[{"type":"text","text":"test"},"children":[]}]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
[FieldMetadataType.POSITION]: [
|
||||
{
|
||||
|
||||
+4
-2
@@ -373,13 +373,15 @@ export const successfulCreateInputByFieldMetadataType: {
|
||||
{
|
||||
input: {
|
||||
richTextField: {
|
||||
blocknote: 'test',
|
||||
blocknote:
|
||||
'[{"type":"paragraph","content":[{"type":"text","text":"test"}]}]',
|
||||
markdown: 'test',
|
||||
},
|
||||
},
|
||||
validateInput: (record: Record<string, any>) => {
|
||||
return (
|
||||
record.richTextField.blocknote === 'test' &&
|
||||
record.richTextField.blocknote ===
|
||||
'[{"type":"paragraph","content":[{"type":"text","text":"test"}]}]' &&
|
||||
record.richTextField.markdown === 'test'
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user