Skip to content
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

Subscribe is not listening to remote changes even when policy is set to OfflineFirstGetPolicy.awaitRemote #364

Closed
rutaba1 opened this issue Jul 24, 2023 · 6 comments

Comments

@rutaba1
Copy link

rutaba1 commented Jul 24, 2023

Hi, I have a configured graphql repository for and working fine for all the operations except subscribe. I had set the policy to OfflineFirstGetPolicy.awaitRemote and when I change anything from the backend the listener is not getting triggered. It triggers only If I do any CRUD operation from the frontend.

Here is the code for my Repository set up

class Repository extends OfflineFirstWithGraphqlRepository {
  Repository._(String endpoint)
      : super(
          migrations: migrations,
          graphqlProvider: GraphqlProvider(
            link: HttpLink(endpoint),
            modelDictionary: graphqlModelDictionary,
          ),
          sqliteProvider: SqliteProvider(
            "BrickGraphql.sqlite",
            databaseFactory: databaseFactory,
            modelDictionary: sqliteModelDictionary,
          ),
          offlineRequestManager: GraphqlRequestSqliteCacheManager(
            'brick_graphql_offline_queue.sqlite',
            databaseFactory: databaseFactory,
          ),
          autoHydrate: true,
        );

  factory Repository() => _singleton!;

  static Repository? _singleton;

  static void configure(String endpoint) {
    _singleton = Repository._(endpoint);
  }
}

And here is the UI where I'm trying to listen to the stream

StreamBuilder<List<Todo>>(
              stream: repository?.subscribe<Todo>(
                  policy: OfflineFirstGetPolicy.awaitRemote),
              builder: (context, ss) {
                return ss.data?.isNotEmpty == true
                    ? Center(
                        child: ListView.builder(
                          itemCount: ss.data!.length,
                          itemBuilder: (ctx, i) {
                            return Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: ListTile(
                                leading: Text(ss.data![i].id),
                                title: Text(ss.data![i].title),
                                tileColor: Colors.pink.shade50,
                              ),
                            );
                          },
                        ),
                      )
                    : const Center(
                        child: Text("No data available"),
                      );
              },
            )

I have also noticed that in initState function I have to call get function to hydrate the local database the stream subscription is not doing it on it's own.

@tshedor
Copy link
Collaborator

tshedor commented Jul 25, 2023

Hey @rutaba1 is your backend GraphQL operation a subscribe operation or a get operation?

If you're calling a get operation, then subscribe will only add a new event to the stream when local data satisfying the subscribe query is changed in the SQLite db.

@rutaba1
Copy link
Author

rutaba1 commented Jul 25, 2023

@tshedor It is a subscribe operation and I'm even overriding the subscribe function in my GraphqlQueryOperationTransformer but still no luck.

 @override
  GraphqlOperation? get subscribe => const GraphqlOperation(
        document: r'''
      subscription Subscription{
        todo_mutated {
         key
	 event 
         data {
            title
            id
          }
        }
      }''',
      );

I'm getting an error in console now with no realtime behaviour

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'String' in type cast
E/flutter (19394): #0      _$TodoFromGraphql (package:brick_graphql_test/brick/adapters/todo_adapter.g.dart:8:30)
E/flutter (19394): #1      TodoAdapter.fromGraphql (package:brick_graphql_test/brick/adapters/todo_adapter.g.dart:95:13)
E/flutter (19394): #2      GraphqlProvider.subscribe (package:brick_graphql/src/graphql_provider.dart:131:27) 

I'm printing the values from $TodoFromGraphql function in generated adapter and I'm getting this:
MAP
______{id: 86, title: Test}
MAP_______{id: 87, title: Fielder}
MAP_______{todo_mutated: null}

Here 1st and 2nd are valid but 3rd is not

@tshedor
Copy link
Collaborator

tshedor commented Jul 25, 2023

@rutaba1 can you please provide your Todo model? When you say "printing the values" does that mean you're doing print(data)?

@rutaba1
Copy link
Author

rutaba1 commented Jul 26, 2023

@tshedor Yeah,here is my model

@ConnectOfflineFirstWithGraphql(
    graphqlConfig: GraphqlSerializable(
  queryOperationTransformer: TodoQueryOperationTransformer.new,
))
class Todo extends OfflineFirstWithGraphqlModel {
  @Sqlite(unique: true)
  final String id;

  final String title;

  Todo({
    required this.id,
    required this.title,
  });
}

and in todo_adapter.g.dart I am printing the value here.

Future<Todo> _$TodoFromGraphql(Map<String, dynamic> data,
    {required GraphqlProvider provider,
    OfflineFirstWithGraphqlRepository? repository}) async {
  print('MAP_______${data}');
  return Todo(id: data['id'] as String, title: data['title'] as String);
}

@tshedor
Copy link
Collaborator

tshedor commented Jul 26, 2023

@rutaba1 ah, I see. Brick expects the fields you list within the operation to map as top-level properties for the Dart instance fields. For example, the expected Dart model would be

@ConnectOfflineFirstWithGraphql(
    graphqlConfig: GraphqlSerializable(
  queryOperationTransformer: TodoQueryOperationTransformer.new,
))
class Todo extends OfflineFirstWithGraphqlModel {
  final String key;

  final String event;

  final Map<String, dynamic> data;

  Todo({
    required this.key,
    required this.event,
    required this.data,
  });
}

If you want to use the nested properties of data as instance fields in Dart, you'll need to do some custom remapping:

@ConnectOfflineFirstWithGraphql(
    graphqlConfig: GraphqlSerializable(
  queryOperationTransformer: TodoQueryOperationTransformer.new,
))
class Todo extends OfflineFirstWithGraphqlModel {
  @Sqlite(unique: true)
  @Graphql(fromGenerator: "(data['data'] as Map)['id'] as String")
  final String id;

  @Graphql(fromGenerator: "(data['data'] as Map)['title'] as String")
  final String title;

  Todo({
    required this.id,
    required this.title,
  });
}

@tshedor
Copy link
Collaborator

tshedor commented Aug 24, 2023

@rutaba1 going to close this due to inactivity

@tshedor tshedor closed this as completed Aug 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants