Description
My company has various of services that provides customer data. I'm trying to build POC to showcase what is possible to maybe solve our own internal issues.
There is no single "view" of the data and instead we must retrieve the data we need from the various services. Using Reactor, I thought I would be able to save the response from a request, and reuse the response if a different field is queried from that same response.
For example, given a todo, let's say the description
and completed
come from the same API. I want to be able to save the response in the Reactor context and reuse if more fields are required.
@SchemaMapping(typeName = "Todo")
@Controller
public class TodoController {
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE = new ParameterizedTypeReference<>() {};
private static final String RESPONSE_ATTR = TodoController.class.getName() + ".todoResponse";
private final WebClient todosWebClient;
public TodoController(WebClient todosWebClient) {
this.todosWebClient = todosWebClient;
}
@SchemaMapping(field = "title")
public Mono<String> resolveTitle(Todo todo) {
return Mono.deferContextual(contextView -> {
Map<String, Object> cached = contextView.getOrDefault(RESPONSE_ATTR, null);
if (cached != null) {
return Mono.just(cached.get("title").toString());
}
return this.todosWebClient.get().uri("/todos/{id}", todo.getId())
.retrieve()
.bodyToMono(MAP_TYPE)
.flatMap(res -> Mono.just(res).contextWrite(context -> context.put(RESPONSE_ATTR, res)))
.flatMap(res -> Mono.just(res.get("title").toString()));
});
}
@SchemaMapping(field = "completed")
public Mono<Boolean> resolveCompleted(Todo todo) {
return Mono.deferContextual(contextView -> {
Map<String, Object> cached = contextView.getOrDefault(RESPONSE_ATTR, null);
if (cached != null) {
return Mono.just(Boolean.parseBoolean(cached.get("completed").toString()));
}
return this.todosWebClient.get().uri("/todos/{id}", todo.getId())
.retrieve()
.bodyToMono(MAP_TYPE)
.flatMap(res -> Mono.just(res).contextWrite(context -> context.put(RESPONSE_ATTR, res)))
.flatMap(res -> Mono.just(Boolean.parseBoolean(res.get("completed").toString())));
});
}
}
However, seems this approach does not work since the logs show (2) requests being sent:
2022-03-14 14:02:36.473 DEBUG 28960 --- [ttp@13e9f2e2-76] o.s.w.r.f.client.ExchangeFunctions : [7f28f96f] HTTP GET https://jsonplaceholder.typicode.com/todos/2
2022-03-14 14:02:36.627 DEBUG 28960 --- [ttp@13e9f2e2-76] o.s.w.r.f.client.ExchangeFunctions : [4301696d] HTTP GET https://jsonplaceholder.typicode.com/todos/2
2022-03-14 14:02:36.948 DEBUG 28960 --- [or-http-epoll-3] o.s.w.r.f.client.ExchangeFunctions : [4301696d] [500c0d95-1] Response 200 OK
2022-03-14 14:02:36.948 DEBUG 28960 --- [or-http-epoll-2] o.s.w.r.f.client.ExchangeFunctions : [7f28f96f] [d12daeed-1] Response 200 OK
I have uploaded a sample project of the above approach: https://github.com/ciscoo/cache-graphql
Side note, I'm trying to build a single view into our data. For example, the todoDetails
comes from a service which I want to "unwrap" if that makes sense.
{
"id": 1,
"todoDetails": {
"id": 1,
"completed": false
}
}
to
{
"id": 1,
"completed": false
}