Skip to content

Improve Execution input nullability #4046

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

Merged
merged 3 commits into from
Jul 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/main/java/graphql/EngineRunningState.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public class EngineRunningState {
private final EngineRunningObserver engineRunningObserver;
private volatile ExecutionInput executionInput;
private final GraphQLContext graphQLContext;

// will be null after updateExecutionInput is called
@Nullable
private volatile ExecutionId executionId;

// if true the last decrementRunning() call will be ignored
Expand Down Expand Up @@ -164,7 +167,7 @@ private void incrementRunning() {

public void updateExecutionInput(ExecutionInput executionInput) {
this.executionInput = executionInput;
this.executionId = executionInput.getExecutionId();
this.executionId = executionInput.getExecutionIdNonNull();
}

private void changeOfState(EngineRunningObserver.RunningState runningState) {
Expand Down
27 changes: 26 additions & 1 deletion src/main/java/graphql/ExecutionInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import graphql.execution.ExecutionId;
import graphql.execution.RawVariables;
import org.dataloader.DataLoaderRegistry;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;

import java.util.Locale;
import java.util.Map;
Expand All @@ -17,6 +20,7 @@
* This represents the series of values that can be input on a graphql query execution
*/
@PublicApi
@NullMarked
public class ExecutionInput {
private final String query;
private final String operationName;
Expand Down Expand Up @@ -58,6 +62,7 @@ public String getQuery() {
/**
* @return the name of the query operation
*/
@Nullable
public String getOperationName() {
return operationName;
}
Expand All @@ -71,6 +76,7 @@ public String getOperationName() {
* @deprecated - use {@link #getGraphQLContext()}
*/
@Deprecated(since = "2021-07-05")
@Nullable
public Object getContext() {
return context;
}
Expand All @@ -85,13 +91,15 @@ public GraphQLContext getGraphQLContext() {
/**
* @return the local context object to pass to all top level (i.e. query, mutation, subscription) data fetchers
*/
@Nullable
public Object getLocalContext() {
return localContext;
}

/**
* @return the root object to start the query execution on
*/
@Nullable
public Object getRoot() {
return root;
}
Expand Down Expand Up @@ -119,12 +127,27 @@ public DataLoaderRegistry getDataLoaderRegistry() {


/**
* This value can be null before the execution starts, but once the execution starts, it will be set to a non-null value.
* See #getExecutionIdNonNull() for a non-null version of this.
*
* @return Id that will be/was used to execute this operation.
*/
@Nullable
public ExecutionId getExecutionId() {
return executionId;
}


/**
* Once the execution starts, GraphQL Java will make sure that this execution id is non-null.
* Therefore use this method if you are sue that the execution has started to get a guaranteed non-null execution id.
*
* @return the non null execution id of this operation.
*/
public ExecutionId getExecutionIdNonNull() {
return Assert.assertNotNull(this.executionId);
}

/**
* This returns the locale of this operation.
*
Expand Down Expand Up @@ -224,6 +247,7 @@ public static Builder newExecutionInput(String query) {
return new Builder().query(query);
}

@NullUnmarked
public static class Builder {

private String query;
Expand All @@ -245,6 +269,7 @@ public static class Builder {

/**
* Package level access to the graphql context
*
* @return shhh but it's the graphql context
*/
GraphQLContext graphQLContext() {
Expand Down Expand Up @@ -312,7 +337,7 @@ public Builder context(Object context) {
return this;
}

/**
/**
* This will give you a builder of {@link GraphQLContext} and any values you set will be copied
* into the underlying {@link GraphQLContext} of this execution input
*
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/graphql/execution/EngineRunningObserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import graphql.ExperimentalApi;
import graphql.GraphQLContext;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* This class lets you observe the running state of the graphql-java engine. As it processes and dispatches graphql fields,
Expand Down Expand Up @@ -46,8 +47,9 @@ enum RunningState {
/**
* This will be called when the running state of the graphql-java engine changes.
*
* @param executionId the id of the current execution
* @param executionId the id of the current execution. This could be null when the engine starts,
* if there is no execution id provided in the execution input
* @param graphQLContext the graphql context
*/
void runningStateChanged(ExecutionId executionId, GraphQLContext graphQLContext, RunningState runningState);
void runningStateChanged(@Nullable ExecutionId executionId, GraphQLContext graphQLContext, RunningState runningState);
}
5 changes: 4 additions & 1 deletion src/main/java/graphql/execution/RawVariables.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import graphql.PublicApi;
import graphql.collect.ImmutableKit;
import graphql.collect.ImmutableMapWithNullValues;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.Map;

/**
* Holds raw variables, which have not been coerced yet into {@link CoercedVariables}
*/
@PublicApi
@NullMarked
public class RawVariables {
private static final RawVariables EMPTY = RawVariables.of(ImmutableKit.emptyMap());
private final ImmutableMapWithNullValues<String, Object> rawVariables;
Expand All @@ -26,7 +29,7 @@ public boolean containsKey(String key) {
return rawVariables.containsKey(key);
}

public Object get(String key) {
public @Nullable Object get(String key) {
return rawVariables.get(key);
}

Expand Down