@@ -106,9 +106,14 @@ on an existing `WebSocketGraphQlClient` to create another with different configu
106106
107107----
108108
109+
110+
111+ [[client-websocketgraphqlclient-connection]]
112+ ==== Connection
113+
109114A connection is established transparently when requests are made. There is only one
110- shared, active connection at a time. If the connection is lost, it is automatically
111- re-established on the next request.
115+ shared, active connection at a time. If the connection is lost, it is re-established on
116+ the next request.
112117
113118`WebSocketGraphQlClient` also exposes lifecycle methods:
114119
@@ -120,12 +125,48 @@ allow requests again.
120125
121126
122127
128+ [[client-websocketgraphqlclient-interceptor]]
129+ ==== Interceptor
130+
131+ The https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md[GraphQL over WebSocket]
132+ protocol defines a number of connection oriented messages in addition to executing
133+ requests. For example, a client sends `"connection_init"` and the server responds with
134+ `"connection_ack"` at the start of a connection.
135+
136+ For WebSocket transport specific interception, you can create a
137+ `WebSocketGraphQlClientInterceptor`:
138+
139+ [source,java,indent=0,subs="verbatim,quotes"]
140+ ----
141+ static class MyInterceptor implements WebSocketGraphQlClientInterceptor {
142+
143+ @Override
144+ public Mono<Object> connectionInitPayload() {
145+ // ... the "connection_init" payload to send
146+ }
147+
148+ @Override
149+ public Mono<Void> handleConnectionAck(Map<String, Object> ackPayload) {
150+ // ... the "connection_ack" payload received
151+ }
152+
153+ }
154+ ----
155+
156+ <<client-interception,Register>> the above interceptor as any other
157+ `GraphQlClientInterceptor` and use it also to intercept GraphQL requests, but note there
158+ can be at most one interceptor of type `WebSocketGraphQlClientInterceptor`.
159+
160+
161+
123162[[client-graphqlclient-builder]]
124163=== Builder
125164
126165`GraphQlClient` defines a parent `Builder` with common configuration options for the
127- builders of all extensions. Currently, it has lets you configure a `DocumentSource`,
128- which is a strategy for loading the document for a request by file name.
166+ builders of all extensions. Currently, it has lets you configure:
167+
168+ - `DocumentSource` strategy to load the document for a request from a file
169+ - <<client-interception>> of executed requests
129170
130171
131172
@@ -193,8 +234,8 @@ response and the field:
193234[[client-requests-execute]]
194235=== Execute
195236
196- ` retrieve` is only a shortcut to decode from a single path in the response map. For more
197- control, use the `execute` method and handle the response:
237+ <<client-requests- retrieve>> is only a shortcut to decode from a single path in the
238+ response map. For more control, use the `execute` method and handle the response:
198239
199240For example:
200241
@@ -270,11 +311,22 @@ You can use the `GraphQlClient` <<client-graphqlclient-builder>> to customize th
270311
271312
272313
314+
273315[[client-subscriptions]]
274- == Subscriptions
316+ == Subscription Requests
317+
318+ `GraphQlClient` can execute subscriptions over transports that support it. Currently, only
319+ the WebSocket transport supports GraphQL streams, so you'll need to create a
320+ <<client-websocketgraphqlclient,WebSocketGraphQlClient>>.
321+
322+
323+
324+ [[client-subscriptions-retrieve]]
325+ === Retrieve
275326
276- For a subscription operation, call `retrieveSubscription` instead of `retrieve` to
277- obtain a stream of responses, each decoded to a target object:
327+ To start a subscription stream, use `retrieveSubscription` which is similar to
328+ <<client-requests-retrieve,retrieve>> for a single response but returning a stream of
329+ responses, each decoded to some data:
278330
279331[source,java,indent=0,subs="verbatim,quotes"]
280332----
@@ -283,9 +335,26 @@ obtain a stream of responses, each decoded to a target object:
283335 .toEntity(String.class);
284336----
285337
286- Similar to the <<client-requests-retrieve, retrieve>> vs <<client-requests-execute, execute>>
287- alternatives for single response requests, the same is also available for subscriptions.
288- For more control over each response, use `executeSubscription`:
338+ A subscription stream may end with:
339+
340+ - `SubscriptionErrorException` if the server ends the
341+ subscription with an explicit "error" message that contains one or more GraphQL errors.
342+ The exception provides access to the GraphQL errors decoded from that message.
343+ - `GraphQlTransportException` such as `WebSocketDisconnectedException` if the underlying
344+ connection is closed or lost in which case you can use the `retry` operator to reestablish
345+ the connection and start the subscription again.
346+
347+
348+
349+
350+
351+
352+ [[client-subscriptions-execute]]
353+ === Execute
354+
355+ <<client-subscriptions-retrieve>> is only a shortcut to decode from a single path in each
356+ response map. For more control, use the `executeSubscription` method and handle each
357+ response directly:
289358
290359[source,java,indent=0,subs="verbatim,quotes"]
291360----
@@ -310,12 +379,42 @@ For more control over each response, use `executeSubscription`:
310379 });
311380----
312381
313- NOTE: Subscriptions are supported only over <<client-websocketgraphqlclient, WebSocket>>.
314382
315383
316384
385+ [[client-interception]]
386+ == Interception
317387
388+ You create a `GraphQlClientInterceptor` to intercept all requests through a client:
318389
390+ [source,java,indent=0,subs="verbatim,quotes"]
391+ ----
392+ static class MyInterceptor implements GraphQlClientInterceptor {
319393
394+ @Override
395+ public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
396+ // ...
397+ return chain.next(request);
398+ }
320399
400+ @Override
401+ public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
402+ // ...
403+ return chain.next(request);
404+ }
405+
406+ }
407+ ----
408+
409+ Once the interceptor is created, register it through the client builder:
410+
411+ [source,java,indent=0,subs="verbatim,quotes"]
412+ ----
413+ URI url = ... ;
414+ WebSocketClient client = ... ;
415+
416+ WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
417+ .interceptor(new MyInterceptor())
418+ .build();
419+ ----
321420
0 commit comments