I can't find out how I can load a referenced resource, identified by a deeply nestet property in combination with a property higher up in a nested data structure.
Given these classes (getters and setters etc. omitted, but you get the idea):
class Resource {
int tenantId;
List<SubResource> subResources;
}
class SubResource {
String name;
int userId;
}
class Tenant {
int id;
String name;
}
class User {
int id;
String name;
}
And this GraphQL schema:
type Resource {
tenantName: String
subResources: [SubResource]
}
type SubResource {
userName
}
I can resolve the Resource.tenantName property in my ResourceController with a method like this:
@SchemaMapping
public CompletableFuture<String> tenantName(Resource resource, DataLoader<Integer, Tenant> loader) {
return loader.load(resource.getTenantId()).thenApply(User::getName);
}
But SubResource.userName... how do I do that, when I need both tenantId and userId to look up userName? This is as far as I got so far :-/
@SchemaMapping
public CompletableFuture<String> userName(SubResource subResource, DataLoader<TenantAndUserId, User> loader) {
// Look up tenant id - but how?
int tenantId = ???;
// TenantAndUserId just holds tenantId and userId properties
var key = new TenantAndUserId(tenantId, subResource.getUserId());
return loader.load(key).thenApply(User::getName);
}
I finally found out how to use localContext to pass tenantId down to the userName method, so just in case others have the same issue, here is what I added:
@SchemaMapping
DataFetcherResult<List<SubResource>> subResources(Resource resource) {
return DataFetcherResult.<List<SubResource>>newResult()
.data(resource.getSubResources())
.localContext(GraphQLContext.newContext()
.of("tenantId", resource.getTenantId())
.build())
.build();
}
and then the userName method can have tenantId injected like this:
@SchemaMapping
public CompletableFuture<String> userName(SubResource subResource, @LocalContextValue("tenantId") UUID tenantId, DataLoader<TenantAndUserId, User> loader) {
// TenantAndUserId just holds tenantId and userId properties
var key = new TenantAndUserId(tenantId, subResource.getUserId());
return loader.load(key).thenApply(User::getName);
}