diff --git a/src/main/java/graphql/ExecutionResult.java b/src/main/java/graphql/ExecutionResult.java index e3715ee55..c05251ecc 100644 --- a/src/main/java/graphql/ExecutionResult.java +++ b/src/main/java/graphql/ExecutionResult.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Map; +import java.util.function.Consumer; /** * This simple value class represents the result of performing a graphql query. @@ -34,7 +35,7 @@ public interface ExecutionResult { * See : https://graphql.github.io/graphql-spec/June2018/#sec-Data * * @return true if the entry "data" should be present in the result - * false otherwise + * false otherwise */ boolean isDataPresent(); @@ -54,4 +55,98 @@ public interface ExecutionResult { * @return a map of the result that strictly follows the spec */ Map toSpecification(); + + + /** + * This helps you transform the current {@link ExecutionResult} object into another one by starting a builder with all + * the current values and allows you to transform it how you want. + * + * @param builderConsumer the consumer code that will be given a builder to transform + * + * @return a new {@link ExecutionResult} object based on calling build on that builder + */ + default ExecutionResult transform(Consumer> builderConsumer) { + Builder builder = newExecutionResult().from(this); + builderConsumer.accept(builder); + return builder.build(); + } + + /** + * @return a builder that allows you to build a new execution result + */ + static Builder newExecutionResult() { + return ExecutionResultImpl.newExecutionResult(); + } + + interface Builder> { + + /** + * Sets values into the builder based on a previous {@link ExecutionResult} + * + * @param executionResult the previous {@link ExecutionResult} + * + * @return the builder + */ + B from(ExecutionResult executionResult); + + /** + * Sets new data into the builder + * + * @param data the data to use + * + * @return the builder + */ + B data(Object data); + + /** + * Sets error list as the errors for this builder + * + * @param errors the errors to use + * + * @return the builder + */ + B errors(List errors); + + /** + * Adds the error list to any existing the errors for this builder + * + * @param errors the errors to add + * + * @return the builder + */ + B addErrors(List errors); + + /** + * Adds the error to any existing the errors for this builder + * + * @param error the error to add + * + * @return the builder + */ + B addError(GraphQLError error); + + /** + * Sets the extension map for this builder + * + * @param extensions the extensions to use + * + * @return the builder + */ + B extensions(Map extensions); + + /** + * Adds a new entry into the extensions map for this builder + * + * @param key the key of the extension entry + * @param value the value of the extension entry + * + * @return the builder + */ + B addExtension(String key, Object value); + + /** + * @return a newly built {@link ExecutionResult} + */ + ExecutionResult build(); + } } diff --git a/src/main/java/graphql/ExecutionResultImpl.java b/src/main/java/graphql/ExecutionResultImpl.java index 1bb675bf0..bec1b5ac4 100644 --- a/src/main/java/graphql/ExecutionResultImpl.java +++ b/src/main/java/graphql/ExecutionResultImpl.java @@ -104,22 +104,17 @@ public String toString() { '}'; } - public ExecutionResultImpl transform(Consumer builderConsumer) { - Builder builder = newExecutionResult().from(this); - builderConsumer.accept(builder); - return builder.build(); - } - public static Builder newExecutionResult() { return new Builder(); } - public static class Builder { + public static class Builder implements ExecutionResult.Builder { private boolean dataPresent; private Object data; private List errors = new ArrayList<>(); private Map extensions; + @Override public Builder from(ExecutionResult executionResult) { dataPresent = executionResult.isDataPresent(); data = executionResult.getData(); @@ -128,39 +123,46 @@ public Builder from(ExecutionResult executionResult) { return this; } + @Override public Builder data(Object data) { dataPresent = true; this.data = data; return this; } + @Override public Builder errors(List errors) { this.errors = errors; return this; } + @Override public Builder addErrors(List errors) { this.errors.addAll(errors); return this; } + @Override public Builder addError(GraphQLError error) { this.errors.add(error); return this; } + @Override public Builder extensions(Map extensions) { this.extensions = extensions; return this; } + @Override public Builder addExtension(String key, Object value) { this.extensions = (this.extensions == null ? new LinkedHashMap<>() : this.extensions); this.extensions.put(key, value); return this; } - public ExecutionResultImpl build() { + @Override + public ExecutionResult build() { return new ExecutionResultImpl(dataPresent, data, errors, extensions); } } diff --git a/src/main/java/graphql/cachecontrol/CacheControl.java b/src/main/java/graphql/cachecontrol/CacheControl.java index 2941c8a46..7dc662ba5 100644 --- a/src/main/java/graphql/cachecontrol/CacheControl.java +++ b/src/main/java/graphql/cachecontrol/CacheControl.java @@ -206,7 +206,7 @@ public static CacheControl newCacheControl() { @Deprecated @DeprecatedAt("2022-07-26") public ExecutionResult addTo(ExecutionResult executionResult) { - return ExecutionResultImpl.newExecutionResult() + return ExecutionResult.newExecutionResult() .from(executionResult) .addExtension(CACHE_CONTROL_EXTENSION_KEY, hintsToCacheControlProperties()) .build(); diff --git a/src/test/groovy/graphql/ExecutionResultTest.groovy b/src/test/groovy/graphql/ExecutionResultTest.groovy new file mode 100644 index 000000000..67e411941 --- /dev/null +++ b/src/test/groovy/graphql/ExecutionResultTest.groovy @@ -0,0 +1,31 @@ +package graphql + +import graphql.language.SourceLocation +import spock.lang.Specification + +/** + * Most of the tests are actually in ExecutionResultImplTest since this is the actual impl + */ +class ExecutionResultTest extends Specification { + + def error1 = new InvalidSyntaxError(new SourceLocation(966, 964), "Yowza") + + def "can use builder to build it"() { + when: + ExecutionResult er = ExecutionResult.newExecutionResult().data([a: "b"]).addError(error1).addExtension("x", "y").build() + then: + er.data == [a: "b"] + er.errors == [error1] + er.extensions == [x: "y"] + } + + def "can transform"() { + when: + ExecutionResult er = ExecutionResult.newExecutionResult().data([a: "b"]).addError(error1).addExtension("x", "y").build() + er = er.transform({ bld -> bld.addExtension("foo", "bar") }) + then: + er.data == [a: "b"] + er.errors == [error1] + er.extensions == [x: "y", foo: "bar"] + } +} diff --git a/src/test/groovy/graphql/cachecontrol/CacheControlTest.groovy b/src/test/groovy/graphql/cachecontrol/CacheControlTest.groovy index 636528900..c05482e3c 100644 --- a/src/test/groovy/graphql/cachecontrol/CacheControlTest.groovy +++ b/src/test/groovy/graphql/cachecontrol/CacheControlTest.groovy @@ -1,7 +1,7 @@ package graphql.cachecontrol import graphql.ExecutionInput -import graphql.ExecutionResultImpl +import graphql.ExecutionResult import graphql.GraphQLContext import graphql.TestUtil import graphql.execution.CoercedVariables @@ -29,7 +29,7 @@ class CacheControlTest extends Specification { cc.hint(ResultPath.parse("/hint/33/private"), 33, CacheControl.Scope.PRIVATE) cc.hint(ResultPath.parse("/hint/private"), CacheControl.Scope.PRIVATE) - def er = ExecutionResultImpl.newExecutionResult().data("data").build() + def er = ExecutionResult.newExecutionResult().data("data").build() when: def newER = cc.addTo(er) @@ -56,7 +56,7 @@ class CacheControlTest extends Specification { def startingExtensions = ["someExistingExt": "data"] - def er = ExecutionResultImpl.newExecutionResult().data("data").extensions(startingExtensions).build() + def er = ExecutionResult.newExecutionResult().data("data").extensions(startingExtensions).build() when: def newER = cc.addTo(er) diff --git a/src/test/groovy/graphql/introspection/IntrospectionResultToSchemaTest.groovy b/src/test/groovy/graphql/introspection/IntrospectionResultToSchemaTest.groovy index f140b65a1..9f2346df4 100644 --- a/src/test/groovy/graphql/introspection/IntrospectionResultToSchemaTest.groovy +++ b/src/test/groovy/graphql/introspection/IntrospectionResultToSchemaTest.groovy @@ -3,7 +3,7 @@ package graphql.introspection import com.fasterxml.jackson.databind.ObjectMapper import graphql.Assert import graphql.ExecutionInput -import graphql.ExecutionResultImpl +import graphql.ExecutionResult import graphql.GraphQL import graphql.TestUtil import graphql.language.Document @@ -700,7 +700,7 @@ input InputType { def "create schema fail"() { given: - def failResult = ExecutionResultImpl.newExecutionResult().build() + def failResult = ExecutionResult.newExecutionResult().build() when: Document document = introspectionResultToSchema.createSchemaDefinition(failResult)