platform/packages/graphql/angel_graphql/lib/src/graphql_ws.dart

81 lines
2.8 KiB
Dart
Raw Normal View History

2019-04-18 00:52:26 +00:00
import 'dart:async';
import 'dart:io';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:graphql_schema/graphql_schema.dart';
import 'package:graphql_server/graphql_server.dart';
2019-04-18 02:02:27 +00:00
import 'package:graphql_server/subscriptions_transport_ws.dart' as stw;
import 'package:web_socket_channel/io.dart';
2019-04-18 00:52:26 +00:00
/// A [RequestHandler] that serves a spec-compliant GraphQL backend, over WebSockets.
/// This endpoint only supports WebSockets, and can be used to deliver subscription events.
///
/// `graphQLWS` uses the Apollo WebSocket protocol, for the sake of compatibility with
/// existing tooling.
///
/// See:
/// * https://github.com/apollographql/subscriptions-transport-ws
2019-04-18 04:12:52 +00:00
RequestHandler graphQLWS(GraphQL graphQL, {Duration keepAliveInterval}) {
2019-04-18 00:52:26 +00:00
return (req, res) async {
if (req is HttpRequestContext) {
if (WebSocketTransformer.isUpgradeRequest(req.rawRequest)) {
await res.detach();
2019-04-18 04:12:52 +00:00
var socket = await WebSocketTransformer.upgrade(req.rawRequest,
protocolSelector: (protocols) {
2019-08-14 16:02:51 +00:00
if (protocols.contains('graphql-ws')) {
2019-04-19 02:02:42 +00:00
return 'graphql-ws';
2019-08-14 16:02:51 +00:00
} else {
2019-04-18 04:12:52 +00:00
throw AngelHttpException.badRequest(
2019-04-19 02:05:10 +00:00
message: 'Only the "graphql-ws" protocol is allowed.');
2019-08-14 16:02:51 +00:00
}
2019-04-18 04:12:52 +00:00
});
2019-04-18 02:02:27 +00:00
var channel = IOWebSocketChannel(socket);
var client = stw.RemoteClient(channel.cast<String>());
2019-04-18 04:12:52 +00:00
var server =
_GraphQLWSServer(client, graphQL, req, res, keepAliveInterval);
2019-04-18 02:02:27 +00:00
await server.done;
2019-04-18 00:52:26 +00:00
} else {
throw AngelHttpException.badRequest(
message: 'The `graphQLWS` endpoint only accepts WebSockets.');
}
} else {
throw AngelHttpException.badRequest(
message: 'The `graphQLWS` endpoint only accepts HTTP/1.1 requests.');
}
};
}
2019-04-18 02:02:27 +00:00
class _GraphQLWSServer extends stw.Server {
final GraphQL graphQL;
final RequestContext req;
final ResponseContext res;
2019-04-18 04:12:52 +00:00
_GraphQLWSServer(stw.RemoteClient client, this.graphQL, this.req, this.res,
Duration keepAliveInterval)
: super(client, keepAliveInterval: keepAliveInterval);
2019-04-18 02:02:27 +00:00
@override
bool onConnect(stw.RemoteClient client, [Map connectionParams]) => true;
@override
Future<stw.GraphQLResult> onOperation(String id, String query,
[Map<String, dynamic> variables, String operationName]) async {
try {
var globalVariables = <String, dynamic>{
'__requestctx': req,
'__responsectx': res,
};
var data = await graphQL.parseAndExecute(
query,
operationName: operationName,
sourceUrl: 'input',
globalVariables: globalVariables,
2019-08-14 16:04:57 +00:00
variableValues: variables,
2019-04-18 02:02:27 +00:00
);
return stw.GraphQLResult(data);
} on GraphQLException catch (e) {
return stw.GraphQLResult(null, errors: e.errors);
}
}
}