Skip to content

Commit 80b2254

Browse files
committed
Add collection key operation alerts and update type handling in Onyx
1 parent ee7c315 commit 80b2254

4 files changed

Lines changed: 44 additions & 6 deletions

File tree

lib/Onyx.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@ function disconnect(connection: Connection): void {
165165
* @param value value to store
166166
* @param options optional configuration object
167167
*/
168-
function set<TKey extends OnyxKey>(key: TKey extends CollectionKeyBase ? never : TKey, value: OnyxSetInput<TKey>, options?: SetOptions): Promise<void> {
168+
function set<TKey extends OnyxKey>(key: string extends CollectionKeyBase ? TKey : TKey extends CollectionKeyBase ? never : TKey, value: OnyxSetInput<TKey>, options?: SetOptions): Promise<void> {
169+
if (OnyxUtils.isCollectionKey(key as OnyxKey)) {
170+
Logger.logInfo(logMessages.collectionKeyOperationAlert(key as string, 'set'));
171+
return Promise.resolve();
172+
}
169173
return OnyxUtils.afterInit(() => OnyxUtils.setWithRetry({key, value, options}));
170174
}
171175

@@ -196,7 +200,11 @@ function multiSet(data: OnyxMultiSetInput): Promise<void> {
196200
* Onyx.merge(ONYXKEYS.POLICY, {id: 1}); // -> {id: 1}
197201
* Onyx.merge(ONYXKEYS.POLICY, {name: 'My Workspace'}); // -> {id: 1, name: 'My Workspace'}
198202
*/
199-
function merge<TKey extends OnyxKey>(key: TKey extends CollectionKeyBase ? never : TKey, changes: OnyxMergeInput<TKey>): Promise<void> {
203+
function merge<TKey extends OnyxKey>(key: string extends CollectionKeyBase ? TKey : TKey extends CollectionKeyBase ? never : TKey, changes: OnyxMergeInput<TKey>): Promise<void> {
204+
if (OnyxUtils.isCollectionKey(key as OnyxKey)) {
205+
Logger.logInfo(logMessages.collectionKeyOperationAlert(key as string, 'merge'));
206+
return Promise.resolve();
207+
}
200208
return OnyxUtils.afterInit(() => {
201209
const skippableCollectionMemberIDs = OnyxUtils.getSkippableCollectionMemberIDs();
202210
if (skippableCollectionMemberIDs.size) {

lib/logMessages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
const logMessages = {
22
incompatibleUpdateAlert: (key: string, operation: string, existingValueType?: string, newValueType?: string) =>
33
`Warning: Trying to apply "${operation}" with ${newValueType ?? 'unknown'} type to ${existingValueType ?? 'unknown'} type in the key "${key}"`,
4+
collectionKeyOperationAlert: (key: string, operation: 'set' | 'merge') =>
5+
`Trying to use "${operation}" with collection key "${key}". Use setCollection / mergeCollection instead.`,
46
};
57

68
export default logMessages;

lib/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ type OnyxSetCollectionInput<TKey extends OnyxKey> = Collection<TKey, OnyxInput<T
327327

328328
type OnyxMethodMap = typeof OnyxUtils.METHOD;
329329

330-
type ExpandOnyxKeys<TKey extends OnyxKey> = TKey extends CollectionKeyBase ? NoInfer<`${TKey}${string}`> : TKey;
330+
type ExpandOnyxKeys<TKey extends OnyxKey> = string extends CollectionKeyBase ? TKey : TKey extends CollectionKeyBase ? never : TKey;
331331

332332
/**
333333
* OnyxUpdate type includes all onyx methods used in OnyxMethodValueMap.

tests/unit/onyxTest.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -594,18 +594,19 @@ describe('Onyx', () => {
594594
});
595595
});
596596

597-
it('should overwrite an array key nested inside an object when using merge on a collection', () => {
597+
it('should overwrite an array key nested inside an object when using mergeCollection', () => {
598598
let testKeyValue: unknown;
599599
connection = Onyx.connect({
600600
key: ONYX_KEYS.COLLECTION.TEST_KEY,
601+
waitForCollectionCallback: true,
601602
initWithStoredValues: false,
602603
callback: (value) => {
603604
testKeyValue = value;
604605
},
605606
});
606607

607-
Onyx.merge(ONYX_KEYS.COLLECTION.TEST_KEY, {test_1: {something: [1, 2, 3]}});
608-
return Onyx.merge(ONYX_KEYS.COLLECTION.TEST_KEY, {test_1: {something: [4]}}).then(() => {
608+
Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {test_1: {something: [1, 2, 3]}});
609+
return Onyx.mergeCollection(ONYX_KEYS.COLLECTION.TEST_KEY, {test_1: {something: [4]}}).then(() => {
609610
expect(testKeyValue).toEqual({test_1: {something: [4]}});
610611
});
611612
});
@@ -3006,6 +3007,32 @@ describe('Onyx', () => {
30063007
expect(cache.get(ONYX_KEYS.RAM_ONLY_WITH_INITIAL_VALUE)).toEqual('default');
30073008
});
30083009
});
3010+
3011+
describe('collection key protection', () => {
3012+
it('set with a collection key should log an alert and not store data', async () => {
3013+
const logInfoSpy = jest.spyOn(Logger, 'logInfo');
3014+
3015+
await Onyx.set(ONYX_KEYS.COLLECTION.TEST_KEY as unknown as typeof ONYX_KEYS.TEST_KEY, {str: 'should not be stored'} as unknown as string);
3016+
3017+
expect(logInfoSpy).toHaveBeenCalledWith(expect.stringContaining('"set"'));
3018+
expect(logInfoSpy).toHaveBeenCalledWith(expect.stringContaining(ONYX_KEYS.COLLECTION.TEST_KEY));
3019+
expect(cache.get(ONYX_KEYS.COLLECTION.TEST_KEY)).toBeUndefined();
3020+
3021+
logInfoSpy.mockRestore();
3022+
});
3023+
3024+
it('merge with a collection key should log an alert and not store data', async () => {
3025+
const logInfoSpy = jest.spyOn(Logger, 'logInfo');
3026+
3027+
await Onyx.merge(ONYX_KEYS.COLLECTION.TEST_KEY as unknown as typeof ONYX_KEYS.TEST_KEY, {str: 'should not be stored'} as unknown as string);
3028+
3029+
expect(logInfoSpy).toHaveBeenCalledWith(expect.stringContaining('"merge"'));
3030+
expect(logInfoSpy).toHaveBeenCalledWith(expect.stringContaining(ONYX_KEYS.COLLECTION.TEST_KEY));
3031+
expect(cache.get(ONYX_KEYS.COLLECTION.TEST_KEY)).toBeUndefined();
3032+
3033+
logInfoSpy.mockRestore();
3034+
});
3035+
});
30093036
});
30103037

30113038
// Separate describe block for Onyx.init to control initialization during each test.
@@ -3111,6 +3138,7 @@ describe('Onyx.init', () => {
31113138
expect(cache.get(`${ONYX_KEYS.COLLECTION.TEST_KEY}entry1`)).toEqual('test_1');
31123139
});
31133140
});
3141+
31143142
});
31153143

31163144
// Separate describe block to control Onyx.init() per-test so we can pre-seed storage before init.

0 commit comments

Comments
 (0)