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

Adding GraphQlContext in preference to the legacy context #2368

Merged
merged 7 commits into from
Jul 5, 2021

Conversation

bbakerman
Copy link
Member

The original graphql-java code copied the graphql-js pattern of having a opaque context object

Since everything in JS is untyped and mutable this works because one can just "wack" another property into the context

However we have learnt over time that since the context is sa shared place (for both frameworks and user code to use) we need to have a more formal mechanism that just a Object getContext() pattern.

So we have made the GraphqlContext getGraphqlContext() the preferred context mechanism and deprecated the older Object getContext methods.

At some point the legacy getContext will disappear but since this is a WIDELY used mechanism that will not be any time soon.

@andimarek andimarek added this to the 17.0 milestone Jun 3, 2021
@andimarek
Copy link
Member

cc @bclozel @rstoyanchev @rwinch
This is the more specific context we talked about. Would love to get your input. Thanks

@bbakerman
Copy link
Member Author

Related to #2357

Copy link

@rstoyanchev rstoyanchev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall. A few comments below.

@@ -187,6 +206,7 @@ public static Builder newExecutionInput(String query) {
private String query;
private String operationName;
private Object context = GraphQLContext.newContext().build();
private GraphQLContext graphQLContext = GraphQLContext.newContext().build();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that context is also an instance of GraphQLContext by default, perhaps the new graphQLContext can be initialized to the same instance, so that the new getGraphQLContext simply exposes the existing default context instance but for its actual type.

This can also be helpful for migration in cases where the existing context is not replaced since code can be switched to simply access it through the new graphQLContext methods. And it shouldn't change anything for cases where the existing context is replaced.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea - thanks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

public Builder graphQLContext(GraphQLContext.Builder contextBuilder) {
return graphQLContext(contextBuilder.build());
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if there is even a need to replace the GraphQLContext instance? A single instance that's not exposed for replacement seems fine for a shared context, or at least it could start out that way until there is evidence to allow it. I cannot think of reasons to replace it but if such reasons do surface, it could also lead to enhancingGraphQLContext itself rather than allowing the instance to be replaced.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is about trying to seed it up with information from outside the graphql query run

Eg imagine some how you now the principal of the user making the request at the time - you can set this in via this.

Of course you could do

ExecutionInput ei - ExecutionInput.newExecutionInput().query(s).variables(..).build()

ei.getGraphqlContext().put(Principal.class, requestPrincipal)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do see how key-value pairs can be inserted into the context through an:

  • ExecutionInput instance via getGraphQLContext
  • ExecutionInput.Builder instance via graphQLContext(UnaryOperator<GraphQLContext.Builder>) although this one currently replaces the underlying context instance rather than transforming it (as per my next comment) so doesn't quite work for just seeding (without replacing) the context.

What I don't see is why even allow replacing the GraphQLContext instance? If all you want to seed it, that should be possible with a single, shared instance only that isn't exposed for replacing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gerat idea - done - goes into the one and only context

GraphQLContext.Builder builder = GraphQLContext.newContext();
builder = contextBuilderFunction.apply(builder);
return graphQLContext(builder.build());
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Irrespective of my previous comment about whether graphQLContext is a fixed instance that can or cannot be replaced, I think this method should operate on the default instance. I can imagine cases where some code only has access to GraphQLContext.Builder and needs to update the context without actually replacing the instance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment does not match any more

}

/**
* This will put all the valuyes from the map into the underlying {@link GraphQLContext} of this execution input

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* This will put all the valuyes from the map into the underlying {@link GraphQLContext} of this execution input
* This will put all the values from the map into the underlying {@link GraphQLContext} of this execution input

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I think this is doing what you want - its a new context here but that is "copied / putAll" into the one context object

* @return this builder
*/
public Builder graphQLContext(Consumer<GraphQLContext.Builder> builderFunction) {
GraphQLContext.Builder builder = GraphQLContext.newContext();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should operate on the underlying GraphQLContext instance rather than creating a new one, right? It would be consistent with the changes we've been discussing and I think it's meant to be that way judging from the Javadoc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - this is putAll into the one context object

@paulbakker
Copy link

Would it be possible to make this class extendable?

In the DGS framework we have a DgsContext, which is very similar in concept to the GraphQLContext. We have a mechanism in the framework to create custom context, which is accessible through the DgsContext.

If DgsContext can extend GraphQLContext, we can get DGS users onto GraphQLContext without breaking existing code. This would also make the integration between DGS and spring-graphql better - this is one of the issues I ran into because spring-graphql assumes GraphQLContext.

@bbakerman
Copy link
Member Author

@paulbakker

Yeah this makes sense to me - GraphQLContext probably should have been an interface in retrospect (said everyone in Java) but allowing it to extended is the next best thing.

you won't get the fancy builders however because UNLESS you know of a tricky way there is no Java way to inherit static builder methods which is a PITA

@bbakerman
Copy link
Member Author

@paulbakker I have made it protected extendable

@rstoyanchev
Copy link

I think it's important to emphasize the changes in this PR provide a migration path for existing APIs that currently rely on a custom object.

Existing usage of the context methods will continue to work next to the new graphQlContext methods which will return a separate instance. That way existing applications and frameworks can support both existing and new ways of accessing the context at the same time.

The idea is the new graphQlContext method becomes a fixed, top-level container that multiple frameworks or components can share. The existing context methods will just be depreciated so existing users can migrate gradually.

If this is all used in this way, I imagine there shouldn't be a need to extend GraphQLContext which is something that only one party or framework can do.

@bbakerman
Copy link
Member Author

@rstoyanchev - you right - there is no need for extension because long term we dont allow you to actually set a GraphqlContext in.

@paulbakker - today there is two context objects (transitonary) - one is the old context that was an opaque type with no contract

The second is the new graphqlContext which is a glorified map but has a fixed contract as a map like store. So frameworks and code can graphqlcontext.get("com.netflix.dgs.whatever.key) to get a value you know should be there because your framework put it there.

Since longer term you don't SET the context object but rather set values into a fixed context value (we create a graphql context per Executionnput) then there is no need for extension

I have reverted that change back - it is still fixed.

@bbakerman bbakerman merged commit 97855c6 into master Jul 5, 2021
tsegismont added a commit to tsegismont/vertx-web that referenced this pull request Sep 15, 2021
Closes vert-x3#2023

With GraphQL-Java 17, te GraphQLContext object becomes the standard to share contextual data between components of a GraphQL Java application (see https://github.com/graphql-java/graphql-java/releases/tag/v17.0 and graphql-java/graphql-java#2368).

This commit:

- deprecates the current context hooks in Vert.x Web GraphQL handlers
- adds a new hook that allows to manipulate the ExecutionInputBuilder
- updates the docs

Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
tsegismont added a commit to vert-x3/vertx-web that referenced this pull request Sep 22, 2021
Closes #2023

With GraphQL-Java 17, te GraphQLContext object becomes the standard to share contextual data between components of a GraphQL Java application (see https://github.com/graphql-java/graphql-java/releases/tag/v17.0 and graphql-java/graphql-java#2368).

This commit:

- deprecates the current context hooks in Vert.x Web GraphQL handlers
- adds a new hook that allows to manipulate the ExecutionInputBuilder
- updates the docs

Signed-off-by: Thomas Segismont <tsegismont@gmail.com>
trustin added a commit to line/armeria that referenced this pull request Apr 19, 2022
…ts` utility (#4201)

Motivation:

The context field of ExecutionInput/DataFetchingEnvironment is a deprecated method. This should be replaced.
- See https://github.com/graphql-java/graphql-java/releases/tag/v17.0
- See graphql-java/graphql-java#2368

Modifications:

- Added `ServiceRequestContex`t to `GraphQLContext`
- Added `GraphqlServiceContexts` utility class for retrieving `ServiceRequestContext` from a `GraphQLContext`.

Result:

- `armeria-graphql` doesn't use deprecated API anymore.
- A user can retrieve the current `ServiceRequestContext` using `GraphServiceContexts`.

Co-authored-by: Trustin Lee <t@motd.kr>
Co-authored-by: Ikhun Um <ih.pert@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants