Skip to content

Ability to pass custom context within the same GraphQLServletContext #226

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

Closed
howardhaozhang opened this issue Dec 19, 2019 · 11 comments
Closed

Comments

@howardhaozhang
Copy link

howardhaozhang commented Dec 19, 2019

I need to pass custom context within the same GraphQLServletContext for some information within the same Http Servlet request. getContext() of DataFetchingEnvironment only allows to return GraphQLContext or GraphQLServletContext. Is there any better way to do this? Thanks.

@ketiko
Copy link

ketiko commented Feb 3, 2020

I also want to provide a custom context.

@oliemansm
Copy link
Member

You'd have to provide a custom implementation of GraphQLServletContextBuilder and inject that in the GraphQLInvocationInputFactory. It depends on how you're creating an instance of the GraphQLHttpServlet. If you're using the graphql-spring-boot-starter you only have to define the custom implementation as a bean and it'll be picked up automatically, e.g.

@Component
class CustomGraphQLServletContextBuilder implements GraphQLServletContextBuilder {
  public GraphQLContext build(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
   return new MyGraphQLServletContext(httpServletRequest, httpServletResponse);
  }

  public GraphQLContext build(Session session, HandshakeRequest handshakeRequest) {
    return new MyGraphQLWebSocketContext(session);
  }
}

@ketiko
Copy link

ketiko commented Apr 1, 2020

I forgot to reply after I solved this, but I did exactly as @oliemansm suggested.

@howardhaozhang
Copy link
Author

howardhaozhang commented Apr 8, 2020

@oliemansm Thanks for you reply. I have a custom implementation of GraphQLServletContextBuilder as you suggested. But getContext() of DataFetchingEnvironment only allows to return GraphQLContext or GraphQLServletContext. I hope to get MyGraphQLContext instead of GraphQLContext from DataFetchingEnvironment with the following CustomGraphQLServletContextBuilder.

@component
class CustomGraphQLServletContextBuilder implements GraphQLServletContextBuilder {
public MyGraphQLContext build(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
return new MyGraphQLServletContext(httpServletRequest, httpServletResponse);
}

public MyGraphQLContext build(Session session, HandshakeRequest handshakeRequest) {
return new MyGraphQLWebSocketContext(session);
}
}

I want to pass around some information in MyGraphQLContext. Please let me know how to do it. Thanks.

@oliemansm
Copy link
Member

You just have to cast it to your own type, i.e.:

String someQueryMethod(DataFetchingEnvironment env) {
  MyGraphQLContext context = (MyGraphQLContext) env.getContext();
  return "bar";
}

@howardhaozhang
Copy link
Author

@oliemansm Please refer to the following pseudocode. I need to use DefaultGraphQLServletContext and also need to keep custom context. My issue is how to create MyGraphQLServletContext.

@component
public class MyGraphQLServletContextBuilder implements GraphQLServletContextBuilder {

private MyRepository myRepository;

public MyGraphQLServletContextBuilder(MyRepository myRepository) {
    this.myRepository = myRepository;
}

@Override
public MyGraphQLServletContext build(HttpServletRequest req, HttpServletResponse response) {
    var customContext = new CustomContext();
    var myGraphQLServletContext = DefaultMyGraphQLServletContext.createServletContext(null, null).with(req).with(response).build();

    return myGraphQLServletContext;
}

@Override
public MyGraphQLServletContext build() {
    ...
}

@Override
public MyGraphQLServletContext build(Session session, HandshakeRequest request) {
    ...
}

}

public class DefaultMyGraphQLServletContext extends DefaultGraphQLServletContext implements MyGraphQLServletContext {

private CustomContext customContext;

public static Builder createServletContext(DataLoaderRegistry registry, Subject subject, CustomContext customContext) {
    this.customContext = customContext;

    // how to create MyGraphQLServletContext here using DefaultGraphQLServletContext
}

public static Builder createServletContext() {
    ...
}

@Override
public ServiceClient getCustomContext() {
    return customContext;
}

}

public interface MyGraphQLServletContext extends GraphQLServletContext {
ServiceClient getCustomContext();
}

@oliemansm
Copy link
Member

In the end you have a method named getCustomContext() returning an object of type ServiceClient. So that makes me think you've given a thing which is actually a ServiceClient a (for me) confusing name of CustomContext. Because in that case you're question isn't about the concept of inheritance which I originally thought, but it's actually a custom thing you want to put in your own context and get a hold of later. I wouldn't have called it CustomContext then, but just a ServiceClient.

The MyGraphQLServletContext is your own class, so you can come up with various methods of adding it. You seem to have copied the Builder pattern as used here too, so you could add it there so you can call something like this:

DefaultMyGraphQLServletContext.createServletContext(null, null)
  .with(req)
  .with(response)
  .with(customContext) // or with(serviceClient)
  .build();

You don't have to use a builder pattern if you don't want to. It seems to be a source of confusion at this time. Perhaps get rid of that builder in your case for now and create your context as you would any simple bean with a constructor and setters.

At the bottom you're trying to set an instance variable from a static method btw. That's nog going to work.

This is getting into the realm of more generic "how do I do stuff in Java" then it's really related to this library though.

@howardhaozhang
Copy link
Author

@oliemansm Thanks for you quick reply. ServiceClient should be CustomContext in my previous post. class DefaultMyGraphQLServletContext extends class DefaultGraphQLServletContext. Unfortunately, DefaultGraphQLServletContext's constructor is private. It prevents me to reuse the code of DefaultGraphQLServletContext for DefaultMyGraphQLServletContext. One way may be to duplicate the code of DefaultGraphQLServletContext in DefaultMyGraphQLServletContext. But it is out side of graphql-java-servlet library, I don't think it is good even it works. Any suggestions for this? Thanks.

@oliemansm
Copy link
Member

You can’t subclass it if the constructor is private. You can apply the decorator pattern though. So keep the default context as a field inside your custom context. And forward any calls you need to to that context and handle any others calls you want to yourself.

@oliemansm
Copy link
Member

@howardhaozhang Can you confirm this solved it so we can close this issue?

@howardhaozhang
Copy link
Author

@oliemansm It works. Thanks.

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

No branches or pull requests

3 participants