diff --git a/ferry_cache/lib/src/cache.dart b/ferry_cache/lib/src/cache.dart index 7e1bf531..6ce4a081 100644 --- a/ferry_cache/lib/src/cache.dart +++ b/ferry_cache/lib/src/cache.dart @@ -13,6 +13,7 @@ class Cache { final Map typePolicies; final bool addTypename; final Store store; + final utils.DataIdResolver? dataIdFromObject; @visibleForTesting final BehaviorSubject< @@ -24,12 +25,14 @@ class Cache { Cache({ Store? store, + utils.DataIdResolver? dataIdFromObject, this.typePolicies = const {}, this.addTypename = true, Map?>> seedOptimisticPatches = const {}, }) : store = store ?? MemoryStore(), - optimisticPatchesStream = BehaviorSubject.seeded(seedOptimisticPatches); + optimisticPatchesStream = BehaviorSubject.seeded(seedOptimisticPatches), + dataIdFromObject = dataIdFromObject; /// Reads data for the given [dataId] from the [Store], merging in any data from optimistic patches @visibleForTesting @@ -62,6 +65,7 @@ class Cache { store, typePolicies, addTypename, + dataIdFromObject: dataIdFromObject, ).doOnDone(() => closed = true); return NeverStream() @@ -97,6 +101,7 @@ class Cache { store, typePolicies, addTypename, + dataIdFromObject: dataIdFromObject, ).doOnDone(() => closed = true); return NeverStream() @@ -129,6 +134,7 @@ class Cache { // TODO: don't cast to dynamic variables: (request.vars as dynamic)?.toJson(), typePolicies: typePolicies, + dataIdFromObject: dataIdFromObject, ); return json == null ? null : request.parseData(json); } @@ -147,6 +153,7 @@ class Cache { variables: (request.vars as dynamic)?.toJson(), typePolicies: typePolicies, addTypename: addTypename, + dataIdFromObject: dataIdFromObject, ); return json == null ? null : request.parseData(json); } @@ -177,6 +184,7 @@ class Cache { data: (data as dynamic)?.toJson(), typePolicies: typePolicies, addTypename: addTypename, + dataIdFromObject: dataIdFromObject, ); /// Normalizes [data] for the given fragment and writes it to the [Store]. @@ -206,6 +214,7 @@ class Cache { data: (data as dynamic)?.toJson(), typePolicies: typePolicies, addTypename: addTypename, + dataIdFromObject: dataIdFromObject, ); void _writeData( @@ -237,6 +246,7 @@ class Cache { // TODO: don't cast to dynamic (data as dynamic)?.toJson(), typePolicies: typePolicies, + dataIdFromObject: dataIdFromObject, ); /// Removes the entity from the [Store] as well as from any optimistic diff --git a/ferry_cache/lib/src/fragment_data_change_stream.dart b/ferry_cache/lib/src/fragment_data_change_stream.dart index 6eea3047..d3427511 100644 --- a/ferry_cache/lib/src/fragment_data_change_stream.dart +++ b/ferry_cache/lib/src/fragment_data_change_stream.dart @@ -2,6 +2,7 @@ import 'package:normalize/policies.dart'; import 'package:normalize/normalize.dart'; import 'package:collection/collection.dart'; import 'package:ferry_store/ferry_store.dart'; +import 'package:normalize/utils.dart'; import 'package:rxdart/rxdart.dart'; import 'package:ferry_exec/ferry_exec.dart'; @@ -9,15 +10,15 @@ import './utils/data_for_id_stream.dart'; /// Emits when the data for this fragment changes, returning a `Set` of changed IDs. Stream> fragmentDataChangeStream( - FragmentRequest request, - bool optimistic, - Stream?>>?> - optimisticPatchesStream, - Map? Function(String dataId) optimisticReader, - Store store, - Map typePolicies, - bool addTypename, -) { + FragmentRequest request, + bool optimistic, + Stream?>>?> + optimisticPatchesStream, + Map? Function(String dataId) optimisticReader, + Store store, + Map typePolicies, + bool addTypename, + {DataIdResolver? dataIdFromObject}) { final dataIds = {}; denormalizeFragment( @@ -33,6 +34,7 @@ Stream> fragmentDataChangeStream( typePolicies: typePolicies, addTypename: addTypename, returnPartialData: true, + dataIdFromObject: dataIdFromObject, ); /// IDs that have changed diff --git a/ferry_cache/lib/src/operation_data_change_stream.dart b/ferry_cache/lib/src/operation_data_change_stream.dart index 1108ed34..b7f34dbc 100644 --- a/ferry_cache/lib/src/operation_data_change_stream.dart +++ b/ferry_cache/lib/src/operation_data_change_stream.dart @@ -11,15 +11,15 @@ import './utils/operation_root_data.dart'; /// Emits when the data for this request changes, returning a `Set` of changed IDs. Stream> operationDataChangeStream( - OperationRequest request, - bool optimistic, - Stream?>>?> - optimisticPatchesStream, - Map? Function(String dataId) optimisticReader, - Store store, - Map typePolicies, - bool addTypename, -) { + OperationRequest request, + bool optimistic, + Stream?>>?> + optimisticPatchesStream, + Map? Function(String dataId) optimisticReader, + Store store, + Map typePolicies, + bool addTypename, + {DataIdResolver? dataIdFromObject}) { final operationDefinition = getOperationDefinition( request.operation.document, request.operation.operationName, @@ -43,6 +43,7 @@ Stream> operationDataChangeStream( typePolicies: typePolicies, addTypename: addTypename, returnPartialData: true, + dataIdFromObject: dataIdFromObject, ); /// IDs that have changed diff --git a/ferry_cache/test/sync_operations_test.dart b/ferry_cache/test/sync_operations_test.dart index bda3be3f..3342038d 100644 --- a/ferry_cache/test/sync_operations_test.dart +++ b/ferry_cache/test/sync_operations_test.dart @@ -44,6 +44,20 @@ void main() { expect(cache.readFragment(reviewFragmentReq), equals(reviewFragmentData)); }); + test('dataIdFromObject overrides cache.identify', () { + final cache = Cache(dataIdFromObject: (object) => 'OVERRIDE'); + expect(cache.identify(reviewFragmentData), equals('OVERRIDE')); + }); + + test('can read and write with a data id override', () { + final cache = Cache(dataIdFromObject: (object) => 'OVERRIDE'); + cache.writeFragment(reviewFragmentReq, reviewFragmentData); + + reviewFragmentReq.idFields['id'] = 'OVERRIDE'; + + expect(cache.readFragment(reviewFragmentReq), equals(reviewFragmentData)); + }); + test('can clear cache', () { final cache = Cache(); diff --git a/normalize/lib/src/denormalize_fragment.dart b/normalize/lib/src/denormalize_fragment.dart index 70e8aaf6..b172eef1 100644 --- a/normalize/lib/src/denormalize_fragment.dart +++ b/normalize/lib/src/denormalize_fragment.dart @@ -64,8 +64,8 @@ Map? denormalizeFragment({ final dataId = resolveDataId( data: { - ...idFields, '__typename': fragmentDefinition.typeCondition.on.name.value, + ...idFields, }, typePolicies: typePolicies, dataIdFromObject: dataIdFromObject, diff --git a/normalize/test/fragments_test.dart b/normalize/test/fragments_test.dart index e28e74b0..63854a8a 100644 --- a/normalize/test/fragments_test.dart +++ b/normalize/test/fragments_test.dart @@ -197,5 +197,28 @@ void main() { equals(normalizedMap), ); }); + + test('Override __typename on denormalize', () { + final fragment = parseString(''' + fragment user on Author { + id + name + } + '''); + final data = {'id': '1', 'name': 'Paul'}; + + final normalizedMap = { + 'NotAuthor:1': {'id': '1', '__typename': 'Author', 'name': 'Paul'}, + }; + + expect( + denormalizeFragment( + document: fragment, + idFields: {'id': '1', '__typename': 'NotAuthor'}, + read: (dataId) => normalizedMap[dataId], + ), + equals(data), + ); + }); }); }