Skip to content

Commit

Permalink
Merge pull request #552 from jnowakow/snapshots
Browse files Browse the repository at this point in the history
Add snapshot functionality
  • Loading branch information
luacmartins authored Jun 13, 2024
2 parents 3cabc12 + 4ba3b9a commit 56c3994
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
46 changes: 45 additions & 1 deletion lib/Onyx.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable no-continue */
import _ from 'underscore';
import lodashPick from 'lodash/pick';
import * as Logger from './Logger';
import cache from './OnyxCache';
import createDeferredTask from './createDeferredTask';
Expand Down Expand Up @@ -631,6 +632,46 @@ function clear(keysToPreserve: OnyxKey[] = []): Promise<void> {
.then(() => undefined);
}

function updateSnapshots(data: OnyxUpdate[]) {
const snapshotCollectionKey = OnyxUtils.getSnapshotKey();
if (!snapshotCollectionKey) return;

const promises: Array<() => Promise<void>> = [];

const snapshotCollection = OnyxUtils.getCachedCollection(snapshotCollectionKey);

Object.entries(snapshotCollection).forEach(([snapshotKey, snapshotValue]) => {
// Snapshots may not be present in cache. We don't know how to update them so we skip.
if (!snapshotValue) {
return;
}

let updatedData = {};

data.forEach(({key, value}) => {
// snapshots are normal keys so we want to skip update if they are written to Onyx
if (OnyxUtils.isCollectionMemberKey(snapshotCollectionKey, key)) {
return;
}

if (typeof snapshotValue !== 'object' || !('data' in snapshotValue)) {
return;
}

const snapshotData = snapshotValue.data;
if (!snapshotData || !snapshotData[key]) {
return;
}

updatedData = {...updatedData, [key]: lodashPick(value, Object.keys(snapshotData[key]))};
});

promises.push(() => merge(snapshotKey, {data: updatedData}));
});

return Promise.all(promises.map((p) => p()));
}

/**
* Insert API responses and lifecycle data into Onyx
*
Expand Down Expand Up @@ -679,7 +720,10 @@ function update(data: OnyxUpdate[]): Promise<void> {
}
});

return clearPromise.then(() => Promise.all(promises.map((p) => p()))).then(() => undefined);
return clearPromise
.then(() => Promise.all(promises.map((p) => p())))
.then(() => updateSnapshots(data))
.then(() => undefined);
}

const Onyx = {
Expand Down
11 changes: 11 additions & 0 deletions lib/OnyxUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ let defaultKeyStates: Record<OnyxKey, OnyxValue<OnyxKey>> = {};
let batchUpdatesPromise: Promise<void> | null = null;
let batchUpdatesQueue: Array<() => void> = [];

let snapshotKey: OnyxKey | null = null;

function getSnapshotKey(): OnyxKey | null {
return snapshotKey;
}

/**
* Getter - returns the merge queue.
*/
Expand Down Expand Up @@ -119,6 +125,10 @@ function initStoreValues(keys: DeepRecord<string, OnyxKey>, initialKeyStates: Pa

// Let Onyx know about which keys are safe to evict
evictionAllowList = safeEvictionKeys;

if (typeof keys.COLLECTION === 'object' && typeof keys.COLLECTION.SNAPSHOT === 'string') {
snapshotKey = keys.COLLECTION.SNAPSHOT;
}
}

/**
Expand Down Expand Up @@ -1142,6 +1152,7 @@ const OnyxUtils = {
prepareKeyValuePairsForStorage,
applyMerge,
initializeWithDefaultKeyStates,
getSnapshotKey,
};

export default OnyxUtils;
25 changes: 25 additions & 0 deletions tests/unit/onyxTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const ONYX_KEYS = {
TEST_POLICY: 'testPolicy_',
TEST_UPDATE: 'testUpdate_',
ANIMALS: 'animals_',
SNAPSHOT: 'snapshot_',
},
};

Expand Down Expand Up @@ -1368,4 +1369,28 @@ describe('Onyx', () => {
connectionIDs.map((id) => Onyx.disconnect(id));
});
});

it('should update Snapshot when its data changed', 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 Onyx.update([{key: cat, value: finalValue, onyxMethod: Onyx.METHOD.MERGE}]);

expect(callback).toBeCalledTimes(2);
expect(callback).toHaveBeenNthCalledWith(1, {data: {[cat]: initialValue}}, snapshot1);
expect(callback).toHaveBeenNthCalledWith(2, {data: {[cat]: finalValue}}, snapshot1);
});
});

0 comments on commit 56c3994

Please sign in to comment.