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

[doc] Add some documentation on testing custom datafetchers with subscriptions #3872

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions doc/how-to/test-sirius-web-based-applications.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
= How to test Sirius Web based applications

== How to perform GraphQL requests?

There are three main APIs to perform GraphQL requests.

The first way to perform a GraphQL request is by using one of the implementations of `IQueryRunner` to execute queries used to retrieves data from the backend.
It is also possible to use one of the implementations of `IMutationRunner` to perform changes in the backend.
Finally, we can use an implementation of `ISubscriptionRunner` to subscribe to flux of data to be warned when something has been changed.

Those runners exist in order to give contributors of the Sirius Web project and downstream projects of Sirius Web a set of APIs encapsulating the body of the requests and the way to execute it.


== How to test custom datafetchers in a subscription

In the project `sirius-web`, we have countless tests using subscriptions.
In order to subscribe to the flux of data of those subscription, we are most of the time relying on `IGraphQLRequestor#subscribe` directly or not by using a `XxxSubscriptionRunner` which encapsulates the body of the request too.
By using `IGraphQLRequestor#subscribe`, we will return the raw `Flux<Object>` created by the event processor.
The subscription to this flux will not trigger the execution of child data fetchers.

For example, let's consider the following fake subscription:

```
subscription someRepresentationEvent($input: SomeRepresentationEventInput!) {
someRepresentationEvent(input: $input) {
__typename
}
}
```

Using such a subscription with `IGraphQLRequestor#subscribe` would not trigger the execution of child datafetchers.
But contrary to what one may believe, executing this subscription instead would not change this fact:

```
subscription someRepresentationEvent($input: SomeRepresentationEventInput!) {
someRepresentationEvent(input: $input) {
__typename
... on SomeRepresentationRefreshedEventPayload {
someValue {
customDataFetcherWithSomeParameters(arg1: 'foo', arg2: 'bar')
}
}
}
}
```

By using `IGraphQLRequestor#subscribe`, the custom datafetcher will not be executed and only the raw payloads will be returned.

In order to trigger the execution of the custom datafetcher in a subscription, one has to use `IGraphQLRequestor#subscribeToSpecification` which will instead return a `Flux<String>` just like the one used by the frontend.
This flux will emit the JSON serialization of the data created accordingly to the official GraphQL specification.
This will trigger the execution of all the datafetchers used in the body of the request.

In order to perform evaluation on such result, it is best to use `JsonPath` in this manner:

```
var input = new SomeRepresentationEventInput(...);
var flux = this.graphQLRequestor.subscribeToSpecification(SUBSCRIPTION, input);

Consumer<String> consumer = payload -> Optional.of(payload)
.ifPresentOrElse(body -> {
String typename = JsonPath.read(body, "$.data.someRepresentationEvent.__typename");
assertThat(typename).isEqualTo("SomeRepresentationRefreshedEventPayload");

String value = JsonPath.read(body, "$.data.someRepresentationEvent.someValue.customDataFetcherWithSomeParameters");
assertThat(value).isEqualTo("foobar");
}, () -> fail("Missing data"));

StepVerifier.create(flux)
.consumeNextWith(consumer)
.thenCancel()
.verify(Duration.ofSeconds(10));
```