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

Multiple references in @GraphQLContext #99

Closed
paulkore opened this Issue Apr 17, 2018 · 4 comments

Comments

Projects
None yet
2 participants
@paulkore

paulkore commented Apr 17, 2018

Hello,

Here's a simple use case of the @GraphQLContext annotation to add a description property to Widget type:

query q1 {
    findAllWidgets() {
        description
    }
}
@GraphQLQuery
public String description(@GraphQLContext Widget widget) {
     return "Widget " + widget.getId();
}

Suppose that another query looks like this:

query q2 {
    findAllBoxes() {
        widgets {
            description
        }
    }
}

We'd like description to return a different value, depending on the context of the entire query. Is it possible to use @GraphQLContext with multiple type references, like so?

@GraphQLQuery
public String description(@GraphQLContext Widget widget, @GraphQLContext Box box) {
     return "Widget " + widget.getId() + " inside box number " + box.getNumber();
}
@kaqqao

This comment has been minimized.

Contributor

kaqqao commented Apr 17, 2018

This is not possible out of the box, as the grand parent (and earlier) contexts are not retained by graphql-java (DataFetchingEnvironment only provides access to the immediate enclosing context).
Still, because this is an often requested feature I'll try to add support for it eventually.

In the meantime, you can simply store each context object you want to keep for later inside the global context.

E.g. use a map or a typed object of some kind as the global context when executing any operation:

ExecutionInput input = ExecutionInput.newExecutionInput()
                .query(query)
                .context(new HashMap<String, Object>()) //the relevant line
                .build());

graphQL.execute(input);

Then, in each (relevant) resolver, store the current context for later:

@GraphQLQuery
public List<Widget> widgets(@GraphQLContext Box box, @GraphQLRootContext Map<String, Object> global) {
    global.put("box", box); // keep Box for later
    return box.getWidgets(); //do the usual
}

Then:

@GraphQLQuery
public String description(@GraphQLContext Widget widget, @GraphQLRootContext("box") Box box) {
     return "Widget " + widget.getId() + " inside box number " + box.getNumber();
}

This is a hacky ad-hoc way to do it, but illustrates an approach. It would be better to use a dedicated class instead of a map for the global context.

Even better, for a generic solution (that I'll try to bring to SPQR out-of-the-box), have a stack in the shared context, and an instrumentation (graphql-java's Instrumentation, which is basically an interceptor) that triggers before or after each field resolution and pushes the current context to the stack. This way none of your resolvers need to change, nor be aware of the custom logic.

If you wanted, you could even provide a custom ArgumentInjector to SPQR to inject an object from the stack based on some custom annotation for example...

@paulkore

This comment has been minimized.

paulkore commented Apr 18, 2018

This is a great response, thanks! We will try to implement the generic context stack as you suggest.

@kaqqao

This comment has been minimized.

Contributor

kaqqao commented Apr 18, 2018

Cheers 😁
Take note that all the fields on the same level will have the same context object. So you'll either have to use a deduplicating collection (e.g. LinkedHashSet) or find a way to use instrumentation to trigger once per level only (which may or may not be easy, I'm not all too familiar with that API since the change).

@kaqqao

This comment has been minimized.

Contributor

kaqqao commented Apr 26, 2018

Closing this as it's answered. Please reopen if you feel something is left unaddressed.

@kaqqao kaqqao closed this Apr 26, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment