Skip to content

Commit

Permalink
fix(client): fetchMore partial handling
Browse files Browse the repository at this point in the history
strict data structure exception handling for fetchMore, FetchMoreOptions.partial for dealing with __typename/structural inconsistencies, better docs.
Also tightened the fetchMore types, as operation data can only be Maps.
  • Loading branch information
micimize committed Nov 6, 2020
1 parent 3d0b64e commit 10ec576
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 12 deletions.
8 changes: 4 additions & 4 deletions examples/starwars/lib/episode/hero_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ class HeroForEpisode extends StatelessWidget {
// NOTE: a loading message is always sent, but if you're developing locally,
// the network result might be returned so fast that
// flutter rebuilds again too quickly for you don't see the loading result on the stream
print([
result.source,
if (result.data != null) result.data['hero']['name']
]);
// print([
// result.source,
// if (result.data != null) result.data['hero']['name']
// ]);
if (result.hasException) {
return Text(result.exception.toString());
}
Expand Down
4 changes: 3 additions & 1 deletion examples/starwars/lib/reviews/review_page_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class PagingReviews extends StatelessWidget {
Widget build(BuildContext context) {
return Query(
options: QueryOptions(
// if we have cached results, don't clobber them
fetchPolicy: FetchPolicy.cacheFirst,
document: gql(r'''
query Reviews($page: Int!) {
reviews(page: $page) {
Expand Down Expand Up @@ -59,7 +61,7 @@ class PagingReviews extends StatelessWidget {
: RaisedButton(
onPressed: () {
fetchMore(
FetchMoreOptions(
FetchMoreOptions.partial(
variables: {'page': nextPage},
updateQuery: (existing, newReviews) => ({
'reviews': {
Expand Down
19 changes: 16 additions & 3 deletions packages/graphql/lib/src/core/fetch_more.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import 'package:graphql/src/core/query_options.dart';
import 'package:graphql/src/core/query_result.dart';
import 'package:graphql/src/core/policies.dart';

import 'package:graphql/src/core/_query_write_handling.dart';

/// Fetch more results and then merge them with [previousResult]
/// according to [FetchMoreOptions.updateQuery]
///
Expand Down Expand Up @@ -53,13 +55,24 @@ Future<QueryResult> fetchMoreImplementation(
previousResult.data,
fetchMoreResult.data,
);
assert(data != null, 'updateQuery result cannot be null');

assert(
data != null,
'updateQuery result cannot be null:\n'
' previousResultData: ${previousResult.data},\n'
' fetchMoreResultData: ${fetchMoreResult.data}',
);
fetchMoreResult.data = data;

if (originalOptions.fetchPolicy != FetchPolicy.noCache) {
queryManager.cache.writeQuery(
queryManager.attemptCacheWriteFromClient(
request,
data: data,
data,
fetchMoreResult,
writeQuery: (req, data) => queryManager.cache.writeQuery(
req,
data: data,
),
);
}

Expand Down
5 changes: 5 additions & 0 deletions packages/graphql/lib/src/core/observable_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ class ObservableQuery {
///
/// The results will then be added to to stream for listeners to react to,
/// such as for triggering `grahphql_flutter` widget rebuilds
///
/// **NOTE**: with the addition of strict data structure checking in v4,
/// it is easy to make mistakes in writing [updateQuery].
///
/// To mitigate this, [FetchMoreOptions.partial] has been provided.
Future<QueryResult> fetchMore(FetchMoreOptions fetchMoreOptions) async {
assert(fetchMoreOptions.updateQuery != null);

Expand Down
34 changes: 31 additions & 3 deletions packages/graphql/lib/src/core/query_options.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use_from_same_package
import 'package:graphql/src/core/_base_options.dart';
import 'package:graphql/src/utilities/helpers.dart';
import 'package:meta/meta.dart';

import 'package:gql/ast.dart';
Expand Down Expand Up @@ -133,6 +134,11 @@ class WatchQueryOptions extends QueryOptions {
}

/// options for fetchMore operations
///
/// **NOTE**: with the addition of strict data structure checking in v4,
/// it is easy to make mistakes in writing [updateQuery].
///
/// To mitigate this, [FetchMoreOptions.partial] has been provided.
class FetchMoreOptions {
FetchMoreOptions({
DocumentNode document,
Expand All @@ -143,19 +149,41 @@ class FetchMoreOptions {
}) : assert(updateQuery != null),
this.document = document ?? documentNode;

/// Automatically merge the results of [updateQuery] into `previousResultData`.
///
/// This is useful if you only want to, say, extract some list data
/// from the newly fetched result, and don't want to worry about
/// structural inconsistencies while merging.
static FetchMoreOptions partial({
DocumentNode document,
Map<String, dynamic> variables = const {},
@required UpdateQuery updateQuery,
}) =>
FetchMoreOptions(
document: document,
variables: variables,
updateQuery: partialUpdater(updateQuery),
);

DocumentNode document;

Map<String, dynamic> variables;

/// Strategy for merging the fetchMore result data
/// with the result data already in the cache
UpdateQuery updateQuery;

/// Wrap an [UpdateQuery] in a [deeplyMergeLeft] of the `previousResultData`.
static UpdateQuery partialUpdater(UpdateQuery update) =>
(previous, fetched) => deeplyMergeLeft(
[previous, update(previous, fetched)],
);
}

/// merge fetchMore result data with earlier result data
typedef dynamic UpdateQuery(
dynamic previousResultData,
dynamic fetchMoreResultData,
typedef Map<String, dynamic> UpdateQuery(
Map<String, dynamic> previousResultData,
Map<String, dynamic> fetchMoreResultData,
);

extension WithType on Request {
Expand Down
5 changes: 5 additions & 0 deletions packages/graphql/lib/src/graphql_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ class GraphQLClient implements GraphQLDataProxy {

/// Fetch more results and then merge them with the given [previousResult]
/// according to [FetchMoreOptions.updateQuery].
///
/// **NOTE**: with the addition of strict data structure checking in v4,
/// it is easy to make mistakes in writing [updateQuery].
///
/// To mitigate this, [FetchMoreOptions.partial] has been provided.
@experimental
Future<QueryResult> fetchMore(
FetchMoreOptions fetchMoreOptions, {
Expand Down
4 changes: 4 additions & 0 deletions packages/graphql_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ In order to use the `FetchMore()` function, you will need to first define `Fetch
final Map pageInfo = result.data['search']['pageInfo'];
final String fetchMoreCursor = pageInfo['endCursor'];
/// **NOTE**: with the addition of strict data structure checking in v4,
/// it is easy to make mistakes in writing [updateQuery].
///
/// To mitigate this, [FetchMoreOptions.partial] has been provided.
FetchMoreOptions opts = FetchMoreOptions(
variables: {'cursor': fetchMoreCursor},
updateQuery: (previousResultData, fetchMoreResultData) {
Expand Down
2 changes: 1 addition & 1 deletion packages/graphql_flutter/lib/src/widgets/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:graphql/client.dart';
import 'package:graphql_flutter/src/widgets/graphql_provider.dart';

// method to call from widget to fetchmore queries
typedef FetchMore = dynamic Function(FetchMoreOptions options);
typedef FetchMore = Future<QueryResult> Function(FetchMoreOptions options);

typedef Refetch = Future<QueryResult> Function();

Expand Down

0 comments on commit 10ec576

Please sign in to comment.