fix: gracefully handle unknown fields in SSE optimistic record updates

https://sonarly.com/issue/21148?type=bug

When an SSE database event delivers a record update containing custom fields not yet in the frontend's cached metadata, `computeOptimisticRecordFromInput` throws a fatal error that breaks real-time update processing for the session.

Fix: Converted the `throw new Error(...)` in `computeOptimisticRecordFromInput` (line 73-77) to `console.warn(...)`.

**Why this is safe:** The function's main processing loop (line 80) iterates only over `objectMetadataItem.fields`, not over `recordInput` keys. Unknown fields in `recordInput` are naturally skipped and never included in the output. The `throw` was a defensive assertion added in commit `29745c67568` for mutation-triggered optimistic updates where the frontend controls the input. When SSE real-time events were added (commit `d51c988a9e`), this function started receiving server-controlled data via `useTriggerOptimisticEffectFromSseUpdateEvents`, which passes `event.properties.after` directly — including custom fields that may not yet be in the frontend's stale metadata cache.

**What this fixes:** When an SSE event delivers a record containing custom fields (e.g., `testMs` on the `opportunity` object) and the frontend's metadata cache hasn't refreshed yet, the function now logs a warning and continues processing known fields instead of crashing the entire SSE event stream handler.

Updated the test from asserting a throw to asserting a `console.warn` call and verifying that known fields (`city: 'Paris'`) are still correctly processed while unknown fields (`unknwon`, `foo`, `bar`) are skipped.
This commit is contained in:
Sonarly Claude Code
2026-04-02 11:30:03 +00:00
parent 268543a08e
commit e3057e5613
2 changed files with 25 additions and 19 deletions
@@ -252,26 +252,32 @@ describe('computeOptimisticRecordFromInput', () => {
});
});
it('should throw an error if recordInput contains fields unrelated to the current objectMetadata', () => {
it('should warn and skip unknown fields if recordInput contains fields unrelated to the current objectMetadata', () => {
const cache = new InMemoryCache();
const personObjectMetadataItem = getMockObjectMetadataItemOrThrow('person');
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
expect(() =>
computeOptimisticRecordFromInput({
currentWorkspaceMember,
objectMetadataItems: getTestEnrichedObjectMetadataItemsMock(),
objectMetadataItem: personObjectMetadataItem,
recordInput: {
unknwon: 'unknown',
foo: 'foo',
bar: 'bar',
city: 'Paris',
},
cache,
objectPermissionsByObjectMetadataId: {},
}),
).toThrowErrorMatchingInlineSnapshot(
`"Should never occur, encountered unknown fields unknwon, foo, bar in objectMetadataItem person"`,
const result = computeOptimisticRecordFromInput({
currentWorkspaceMember,
objectMetadataItems: getTestEnrichedObjectMetadataItemsMock(),
objectMetadataItem: personObjectMetadataItem,
recordInput: {
unknwon: 'unknown',
foo: 'foo',
bar: 'bar',
city: 'Paris',
},
cache,
objectPermissionsByObjectMetadataId: {},
});
expect(consoleSpy).toHaveBeenCalledWith(
'computeOptimisticRecordFromInput: skipping unknown fields unknwon, foo, bar in objectMetadataItem person',
);
expect(result).toEqual({
city: 'Paris',
});
consoleSpy.mockRestore();
});
});
@@ -71,8 +71,8 @@ export const computeOptimisticRecordFromInput = ({
},
);
if (unknownRecordInputFields.length > 0) {
throw new Error(
`Should never occur, encountered unknown fields ${unknownRecordInputFields.join(', ')} in objectMetadataItem ${objectMetadataItem.nameSingular}`,
console.warn(
`computeOptimisticRecordFromInput: skipping unknown fields ${unknownRecordInputFields.join(', ')} in objectMetadataItem ${objectMetadataItem.nameSingular}`,
);
}