Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion lib/OnyxUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,15 @@ function updateSnapshots<TKey extends OnyxKey>(data: Array<OnyxUpdate<TKey>>, me

let updatedData: Record<string, unknown> = {};

for (const {key, value} of data) {
for (const {key, value, onyxMethod} of data) {
if (typeof key !== 'string') {
// clear/multiset entries legitimately carry no key; snapshots have nothing to update for them
if (onyxMethod !== METHOD.CLEAR && onyxMethod !== METHOD.MULTI_SET) {
Logger.logInfo(`Invalid ${typeof key} key (method: ${onyxMethod}, key: ${String(key).slice(0, 50)}) provided in Onyx update. Skipping snapshot update for this entry.`);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Logger.logInfo(`Invalid ${typeof key} key (method: ${onyxMethod}, key: ${String(key).slice(0, 50)}) provided in Onyx update. Skipping snapshot update for this entry.`);
Logger.logWarn(`Invalid ${typeof key} key (method: ${onyxMethod}, key: ${String(key).slice(0, 50)}) provided in Onyx update. Skipping snapshot update for this entry.`);

}
continue;
Comment on lines +1214 to +1219

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, add Log message, same as it's done in Onyx.update

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +1215 to +1219

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Expand keyless multiset updates for snapshots

When an Onyx.update batch uses a keyless METHOD.MULTI_SET, the update handler in lib/Onyx.ts still applies every key from the entry's value, but this new guard drops the whole entry before updateSnapshots can inspect those keys. If any of the multiset keys is present in a cached snapshot, the real Onyx value is updated while the snapshot keeps the old value, so search/snapshot consumers can show stale data after a successful update; handle MULTI_SET by expanding its payload into per-key snapshot updates instead of continuing here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so this is not a regression with this code, so we could choose to handle it in a follow-up. What we have here is strictly better than what we have on main, which errors in this case.

That said, we probably should update updateSnapshots to work with multiSet and clear, which would be strictly better than just avoiding a crash. Right @elirangoshen ?

}

// snapshots are normal keys so we want to skip update if they are written to Onyx
if (OnyxKeys.isCollectionMemberKey(snapshotCollectionKey, key)) {
continue;
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/onyxTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,33 @@ describe('Onyx', () => {
expect(callback.mock.calls[1][1]).toBe(ONYX_KEYS.COLLECTION.SNAPSHOT);
});

it('should skip update entries without a key when updating Snapshots instead of rejecting', async () => {
const cat = `${ONYX_KEYS.COLLECTION.ANIMALS}cat`;
const snapshot1 = `${ONYX_KEYS.COLLECTION.SNAPSHOT}1`;

const initialValue = {name: 'Fluffy'};
const finalValue = {name: 'Kitty'};

await Onyx.set(cat, initialValue);
await Onyx.set(snapshot1, {data: {[cat]: initialValue}});

const callback = jest.fn();

Onyx.connect({
key: ONYX_KEYS.COLLECTION.SNAPSHOT,
callback,
});

await waitForPromisesToResolve();

const keylessUpdate = {onyxMethod: Onyx.METHOD.MERGE, value: {name: 'Ghost'}} as unknown as OnyxUpdate<OnyxKey>;

await expect(Onyx.update([keylessUpdate, {key: cat, value: finalValue, onyxMethod: Onyx.METHOD.MERGE}])).resolves.not.toThrow();

// The valid update still lands in the snapshot.
expect(callback.mock.calls.at(-1)?.[0]).toEqual({[snapshot1]: {data: {[cat]: finalValue}}});
});

describe('update', () => {
let logInfoFn = jest.fn();

Expand Down
Loading