-
Notifications
You must be signed in to change notification settings - Fork 123
OfflineMutationTypedLink - How to pass same client to OfflineMutationTypedLink #496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
You'd need to pass in the actual client into the offline mutation Link, so it can call the real request () method. |
In my OfflineMutationTypedLink({
required this.mutationQueueBox,
required this.serializers,
required this.cache,
required this.requestController,
// required this.client, <----- do I need to pass the client here? (client is OfflineClient type)
this.config = const OfflineClientConfig(),
}); If I pass it here, when I am declaring the OfflineClient({
required this.link,
required this.storeBox,
required this.mutationQueueBox,
required this.serializers,
this.offlineConfig,
required this.requestController,
this.typePolicies = const {},
this.updateCacheHandlers = const {},
this.defaultFetchPolicies = const {},
this.addTypename = true,
}) : cache = Cache(
store: HiveStore(storeBox),
typePolicies: typePolicies,
addTypename: addTypename,
) {
_typedLink = TypedLink.from([
RequestControllerTypedLink(requestController),
OfflineMutationTypedLink(
cache: cache,
mutationQueueBox: mutationQueueBox,
serializers: serializers,
requestController: requestController,
config: offlineConfig,
// client: this <----- here adding this also enters in recursive
),
if (addTypename) const AddTypenameTypedLink(),
if (updateCacheHandlers.isNotEmpty)
UpdateCacheTypedLink(
cache: cache,
updateCacheHandlers: updateCacheHandlers,
),
FetchPolicyTypedLink(
link: link,
cache: cache,
defaultFetchPolicies: defaultFetchPolicies,
)
]);
} Thanks |
There is a chicken - and egg problem here, since the link needs the client and the client needs the link. You could first create the link, then the client, and then set the Client in the link. |
Okay, thank you @knaeckeKami ! Finally I also discovered that my code when I had connection I was setting always void _handleOnConnect() async {
final queue = StreamQueue(Stream.fromIterable(mutationQueueBox.values));
while (await queue.hasNext) {
final json = await queue.next;
final req = serializers.deserialize(json) as OperationRequest<Object, Object>;
// Run unexecuted mutations
await client
.request(req)
.firstWhere((res) => res.dataSource == DataSource.Link && res.operationRequest == req);
}
} results to an Unhandled Exception on the UpdateChaceTypedLink, since it does not match the expected Type: [VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type '(CacheProxy, OperationResponse<GUpsertObservationData, GUpsertObservationVars>) => void' is not a subtype of type '(CacheProxy, OperationResponse<Object, Object>) => void' in type cast
#0 UpdateCacheTypedLink._updateCache
#1 _DoStreamSink.onData
do.dart:31
#2 _RootZone.runUnaryGuarded (dart:async/zone.dart:1593:10)
#3 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
#4 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#5 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:774:19)
#6 _StreamController._add (dart:async/stream_controller.dart:648:7)
#7 _StreamController.add (dart:async/stream_controller.dart:596:5)
#8 _DoStreamSink.onData
do.dart:40
#9 _RootZone.runUnaryGuarded (dart:async/zone.dart:1593:10)
#10 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:339:11)
#11 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:271:7)
#12 _SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:774:19)
#13 _StreamController._add (dart:async/stream_controller.dart:648:7)
#14 _StreamController.add (dart:async/stream_controller.dart:596:5)
#15 _DoStreamSink.onData
do.dart:40
#16 _RootZone.runUnaryGuarded (dart:async/zone.dart:1593:10) I could infere the type depending on the mutation I am trying to deserialize, but it is possible that dart could assign the correct type? Thank you beforehand! |
unfortunately, that's an issue with variance here. I don't have a good solution at the moment, but I think a workaround would be changing your cachehandlers to accept OperationResponse<Object?, Object?> and cast the results |
Okay thank you. For the moment defining a cache handler type as final responseUpsertObservation = response as OperationResponse<GUpsertObservationData, GUpsertObservationVars>; Moves the error from the OfflineMutationTypedLink to the cache handler, but still cannot cast the subtype giving the same typecasting error: Unhandled Exception: type 'OperationResponse<Object, Object>' is not a subtype of type 'OperationResponse<GUpsertObservationData, GUpsertObservationVars>' in type cast I will try to investigate if it is possible to cast it somehow. Do you know if there's probably an internal issue (or using built_value underhood library?) |
You can work around it by casting not the response, but the data: void myCacheHandler(
CacheProxy proxy,
OperationResponse<dynamic, dynamic> response,
) {
//ignore: avoid_dynamic_calls
final myData = response.data as MyData?
} Using |
This is not an issue in built_value. Actually, built_value is one of the only serialization libraries that support something like serializers.deserialize(json); which would deserialize to the correct type without you having to specify it. (Most would require you to write The issue is that after deserialization, we don't know the exact type. We just know it an So, even if all the runtime types match perfectly, Dart still checks the types before calling the function and because the generics type parameters are This is unfortunately more of an ugly corner of the Dart type system than an issue in built_value. However, there would be ways around this. We could add a new method to OperationRequest: abstract class OperationRequest<TData, TVars> {
Stream<OperationResponse<TData, TVars> execute(TypedLink link) => link.request(this);
} With this hack, we could get Dart to infer the correct type parameters. This would require changes to the code generation though since OperationRequest is implemented, not extended at the moment. |
Hmmm interesting... 🤔 Thank you for the clarification! I finally could get rid of the type error as you suggested casting the |
@francescreig please feel free to carry on with that PR, I've since moved project and role so am no longer able to push that PR forward. It's been quite a while since I looked at any of that code so not sure I can remember enough to provide any useful input |
Yeah, I was also running into this issue myself and did not really understand it, until the same thing happened when it added the feature to run the client in an Isolate (also there the requests are serialized and the receiver does not know the. generic types), and so I had to do something similar ->
If you manage to improve the OfflineMutationTypedLink, I'd be grateful. As far as I understand, the currently shipped OfflineMutationLink does not work at all, right? (I was not the maintainer of ferry at the time this was written and did not use it myself yet so I may not fully understand what is happening there) |
@francescreig can you share your solution? Or if possible add this to the open PR? Either would be very helpful. |
Hello,
I am been working on creating an OfflineMutationTypedLink same as this PR #147. I've nearly implemented but I have a problem that actually it was pointed here: when you want to reexecute offline mutations, calling
requestController.add(req)
would not execute the request since it does not see any response.Trying to instead call
client.request(req)
(see below)leads me to call again the request method that I override on the same OfflineMutationTypedLink, so I end up in a recursive call that freezes the app.
Here it is pointed to pass the client as a dependency, but I think I am failing on this, since using the same client (OfflineClient) that internally has this OfflineMutationTypedLink gives a recursive calls.
This is my request method that I override:
So when I set the setter
connected
to true it runs a method to execute the requests, thus entering in a recursive call (callingrequest
overrided method from above):The text was updated successfully, but these errors were encountered: