graphql: Create result from options
Changelog-Add: Create result from options
MODIFY
packages/graphql/lib/src/core/_base_options.dart
packages/graphql/lib/src/core/_base_options.dart
@@ -115,4 +115,18 @@ abstract class BaseOptions<TParsed extends Object?> {
int get hashCode => const ListEquality<Object?>(
DeepCollectionEquality(),
).hash(properties);
+
+ QueryResult<TParsed> createResult({
+ Map<String, dynamic>? data,
+ OperationException? exception,
+ Context context = const Context(),
+ required QueryResultSource source,
+ }) =>
+ QueryResult.internal(
+ parserFn: parserFn,
+ data: data,
+ exception: exception,
+ context: context,
+ source: source,
+ );
}

MODIFY
packages/graphql/lib/src/core/observable_query.dart
packages/graphql/lib/src/core/observable_query.dart
import 'dart:async';
import 'package:graphql/client.dart';
-import 'package:graphql/src/utilities/response.dart';
import 'package:meta/meta.dart';
import 'package:graphql/src/core/fetch_more.dart';
@@ -133,7 +132,7 @@ class ObservableQuery<TParsed> {
if (isRefetchSafe) {
addResult(QueryResult.loading(
data: latestResult?.data,
- parserFn: options.parserFn,
+ options: options,
));
return await queryManager.refetchQuery<TParsed>(queryId);
}
@@ -220,7 +219,7 @@ class ObservableQuery<TParsed> {
FetchMoreOptions fetchMoreOptions) async {
addResult(QueryResult.loading(
data: latestResult?.data,
- parserFn: options.parserFn,
+ options: options,
));
return fetchMoreImplementation(
@@ -232,17 +231,6 @@ class ObservableQuery<TParsed> {
);
}
- void addFetchResult(
- Response response,
- QueryResultSource source, {
- bool fromRebroadcast = false,
- }) {
- addResult(
- mapFetchResultToQueryResult<TParsed>(response, options, source: source),
- fromRebroadcast: fromRebroadcast,
- );
- }
-
/// Add a [result] to the [stream] unless it was created
/// before [lasestResult].
///

MODIFY
packages/graphql/lib/src/core/query_manager.dart
packages/graphql/lib/src/core/query_manager.dart
import 'dart:async';
-import 'package:graphql/src/core/result_parser.dart';
import 'package:graphql/src/utilities/response.dart';
import 'package:meta/meta.dart';
import 'package:collection/collection.dart';
@@ -76,7 +75,7 @@ class QueryManager {
// TODO optimisticResults for streams just skip the cache for now
yield QueryResult.optimistic(
data: options.optimisticResult as Map<String, dynamic>?,
- parserFn: options.parserFn,
+ options: options,
);
} else if (shouldRespondEagerlyFromCache(options.fetchPolicy)) {
final cacheResult = cache.readQuery(
@@ -85,9 +84,9 @@ class QueryManager {
);
if (cacheResult != null) {
yield QueryResult(
+ options: options,
source: QueryResultSource.cache,
data: cacheResult,
- parserFn: options.parserFn,
);
}
}
@@ -114,8 +113,8 @@ class QueryManager {
} catch (failure, trace) {
// we set the source to indicate where the source of failure
queryResult ??= QueryResult(
+ options: options,
source: QueryResultSource.network,
- parserFn: options.parserFn,
);
queryResult.exception = coalesceErrors(
@@ -133,9 +132,9 @@ class QueryManager {
})
.transform<QueryResult<TParsed>>(StreamTransformer.fromHandlers(
handleError: (err, trace, sink) => sink.add(_wrapFailure(
+ options,
err,
trace,
- options.parserFn,
)),
))
.map((QueryResult<TParsed> queryResult) {
@@ -145,9 +144,9 @@ class QueryManager {
} catch (ex, trace) {
yield* Stream.fromIterable([
_wrapFailure(
+ options,
ex,
trace,
- options.parserFn,
)
]);
}
@@ -222,7 +221,7 @@ class QueryManager {
// _resolveQueryEagerly handles cacheOnly,
// so if we're loading + cacheFirst we continue to network
return MultiSourceResult(
- parserFn: options.parserFn,
+ options: options,
eagerResult: eagerResult,
networkResult:
(shouldStopAtCache(options.fetchPolicy) && !eagerResult.isLoading)
@@ -262,8 +261,8 @@ class QueryManager {
} catch (failure, trace) {
// we set the source to indicate where the source of failure
queryResult ??= QueryResult(
+ options: options,
source: QueryResultSource.network,
- parserFn: options.parserFn,
);
queryResult.exception = coalesceErrors(
@@ -295,9 +294,7 @@ class QueryManager {
String queryId,
BaseOptions<TParsed> options,
) {
- QueryResult<TParsed> queryResult = QueryResult.loading(
- parserFn: options.parserFn,
- );
+ QueryResult<TParsed> queryResult = QueryResult.loading(options: options);
try {
if (options.optimisticResult != null) {
@@ -317,17 +314,17 @@ class QueryManager {
// we only push an eager query with data
if (data != null) {
queryResult = QueryResult(
+ options: options,
data: data,
source: QueryResultSource.cache,
- parserFn: options.parserFn,
);
}
if (options.fetchPolicy == FetchPolicy.cacheOnly &&
queryResult.isLoading) {
queryResult = QueryResult(
+ options: options,
source: QueryResultSource.cache,
- parserFn: options.parserFn,
exception: OperationException(
linkException: CacheMissException(
'Could not resolve the given request against the cache. (FetchPolicy.cacheOnly)',
@@ -363,8 +360,7 @@ class QueryManager {
/// Refetch the [ObservableQuery] referenced by [queryId],
/// overriding any present non-network-only [FetchPolicy].
Future<QueryResult<TParsed>?> refetchQuery<TParsed>(String queryId) {
- WatchQueryOptions<TParsed> options =
- queries[queryId]!.options as WatchQueryOptions<TParsed>;
+ var options = getQuery<TParsed>(queryId)!.options;
if (!willAlwaysExecuteOnNetwork(options.fetchPolicy)) {
options = options.copyWithFetchPolicy(FetchPolicy.networkOnly);
}
@@ -387,10 +383,13 @@ class QueryManager {
}
ObservableQuery<Object?>? getQuery(String? queryId) {
- if (queries.containsKey(queryId)) {
- return queries[queryId!];
+ if (!queries.containsKey(queryId)) {
+ return null;
+ }
+ final query = queries[queryId!];
+ if (query is ObservableQuery<TParsed>) {
+ return query;
}
-
return null;
}
@@ -404,8 +403,7 @@ class QueryManager {
String? queryId,
QueryResult<TParsed> queryResult,
) {
- final ObservableQuery<TParsed>? observableQuery =
- getQuery(queryId) as ObservableQuery<TParsed>?;
+ final observableQuery = getQuery<TParsed>(queryId);
if (observableQuery != null && !observableQuery.controller.isClosed) {
observableQuery.addResult(queryResult);
@@ -420,8 +418,8 @@ class QueryManager {
required BaseOptions<TParsed> options,
}) {
QueryResult<TParsed> queryResult = QueryResult(
+ options: options,
source: QueryResultSource.optimisticResult,
- parserFn: options.parserFn,
);
attemptCacheWriteFromClient(
@@ -477,9 +475,12 @@ class QueryManager {
optimistic: query.options.policies.mergeOptimisticData,
);
if (_cachedDataHasChangedFor(query, cachedData)) {
- query.addFetchResult(
+ query.addResult(
+ mapFetchResultToQueryResult(
Response(data: cachedData, response: {}),
- QueryResultSource.cache,
+ query.options,
+ source: QueryResultSource.cache,
+ ),
fromRebroadcast: true,
);
}
@@ -517,13 +518,13 @@ class QueryManager {
}
QueryResult<TParsed> _wrapFailure<TParsed>(
+ BaseOptions<TParsed> options,
dynamic ex,
StackTrace trace,
- ResultParserFn<TParsed> parserFn,
) =>
QueryResult(
+ options: options,
// we set the source to indicate where the source of failure
source: QueryResultSource.network,
exception: coalesceErrors(linkException: translateFailure(ex, trace)),
- parserFn: parserFn,
);

MODIFY
packages/graphql/lib/src/core/query_result.dart
packages/graphql/lib/src/core/query_result.dart
import 'dart:async' show FutureOr;
import 'package:graphql/client.dart';
+import 'package:graphql/src/core/_base_options.dart';
import 'package:graphql/src/core/result_parser.dart';
+import 'package:meta/meta.dart';
/// The source of the result data contained
///
@@ -39,7 +41,8 @@ final _eagerSources = {
/// A single operation result
class QueryResult<TParsed extends Object?> {
- QueryResult({
+ @protected
+ QueryResult.internal({
this.data,
this.exception,
this.context = const Context(),
@@ -47,32 +50,44 @@ class QueryResult<TParsed extends Object?> {
required this.source,
}) : timestamp = DateTime.now();
+ factory QueryResult({
+ required BaseOptions<TParsed> options,
+ Map<String, dynamic>? data,
+ OperationException? exception,
+ Context context = const Context(),
+ required QueryResultSource source,
+ }) =>
+ options.createResult(
+ source: source,
+ data: data,
+ exception: exception,
+ context: context,
+ );
+
/// Unexecuted singleton, used as a placeholder for mutations,
/// etc.
- static final unexecuted = QueryResult(
+ static final unexecuted = QueryResult.internal(
source: null,
parserFn: (d) =>
throw UnimplementedError("Unexecuted query data can not be parsed."),
)..timestamp = DateTime.fromMillisecondsSinceEpoch(0);
factory QueryResult.loading({
+ required BaseOptions<TParsed> options,
Map<String, dynamic>? data,
- required ResultParserFn<TParsed> parserFn,
}) =>
- QueryResult(
+ options.createResult(
data: data,
source: QueryResultSource.loading,
- parserFn: parserFn,
);
factory QueryResult.optimistic({
+ required BaseOptions<TParsed> options,
Map<String, dynamic>? data,
- required ResultParserFn<TParsed> parserFn,
}) =>
- QueryResult(
+ options.createResult(
data: data,
source: QueryResultSource.optimisticResult,
- parserFn: parserFn,
);
DateTime timestamp;
@@ -141,10 +156,10 @@ class QueryResult<TParsed extends Object?> {
class MultiSourceResult<TParsed> {
MultiSourceResult({
+ required BaseOptions<TParsed> options,
QueryResult<TParsed>? eagerResult,
this.networkResult,
- required ResultParserFn<TParsed> parserFn,
- }) : eagerResult = eagerResult ?? QueryResult.loading(parserFn: parserFn),
+ }) : eagerResult = eagerResult ?? QueryResult.loading(options: options),
assert(
eagerResult!.source != QueryResultSource.network,
'An eager result cannot be gotten from the network',

MODIFY
packages/graphql/lib/src/utilities/response.dart
packages/graphql/lib/src/utilities/response.dart
@@ -35,10 +35,10 @@ QueryResult<TParsed> mapFetchResultToQueryResult<TParsed>(
}
return QueryResult(
+ options: options,
data: data,
context: response.context,
source: source,
exception: coalesceErrors(graphqlErrors: errors),
- parserFn: options.parserFn,
);
}

ADD
packages/graphql/test/query_manager_test.dart
+import 'package:gql/language.dart';
+import 'package:graphql/client.dart';
+import 'package:mockito/mockito.dart';
+import 'package:test/test.dart';
+
+import 'helpers.dart';
+
+void main() {
+ late MockLink link;
+ late GraphQLClient client;
+
+ setUp(() {
+ link = MockLink();
+
+ client = GraphQLClient(
+ cache: getTestCache(),
+ link: link,
+ );
+ });
+
+ group('QueryManager', () {
+ test("Can refetch", () {
+ final response = Response(
+ data: <String, dynamic>{
+ 'fetchPerson': null,
+ },
+ response: {},
+ );
+ when(
+ link.request(any),
+ ).thenAnswer(
+ (_) => Stream.fromIterable(
+ [response],
+ ),
+ );
+
+ final observable = client.watchQuery(
+ WatchQueryOptions<String?>(
+ document: parseString("""{ fetchPerson { name } }"""),
+ parserFn: (data) => data['fetchPerson']?['name'] as String?,
+ ),
+ );
+ client.queryManager.refetchQuery(observable.queryId);
+ });
+ });
+}

MODIFY
packages/graphql_flutter/lib/src/widgets/hooks/subscription.dart
packages/graphql_flutter/lib/src/widgets/hooks/subscription.dart
@@ -30,9 +30,9 @@ QueryResult<TParsed> useSubscription<TParsed>(
initialData: options.optimisticResult != null
? QueryResult.optimistic(
data: options.optimisticResult as Map<String, dynamic>?,
- parserFn: options.parserFn,
+ options: options,
)
- : QueryResult.loading(parserFn: options.parserFn),
+ : QueryResult.loading(options: options),
);
return snapshot.data!;
}

graphql: Create result from options

Changelog-Add: Create result from options