Skip to content

Commit 723b840

Browse files
Implement toString/hashCode/equals for DataFetcherResult
This facilitates usage in tests for quick checking of equality (and useful error messages in assertion messages). References #3963.
1 parent dbdf37b commit 723b840

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

src/main/java/graphql/execution/DataFetcherResult.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.ArrayList;
1212
import java.util.List;
1313
import java.util.Map;
14+
import java.util.Objects;
1415
import java.util.function.Consumer;
1516
import java.util.function.Function;
1617

@@ -25,10 +26,19 @@
2526
* This also allows you to pass down new local context objects between parent and child fields. If you return a
2627
* {@link #getLocalContext()} value then it will be passed down into any child fields via
2728
* {@link graphql.schema.DataFetchingEnvironment#getLocalContext()}
28-
*
29+
* <p>
2930
* You can also have {@link DataFetcher}s contribute to the {@link ExecutionResult#getExtensions()} by returning
3031
* extensions maps that will be merged together via the {@link graphql.extensions.ExtensionsBuilder} and its {@link graphql.extensions.ExtensionsMerger}
3132
* in place.
33+
* <p>
34+
* This provides {@link #hashCode()} and {@link #equals(Object)} methods that afford comparison with other {@link DataFetcherResult} object.s
35+
* However, to function correctly, this relies on the values provided in the following fields in turn also implementing {@link #hashCode()}} and {@link #equals(Object)} as appropriate:
36+
* <ul>
37+
* <li>The data returned in {@link #getData()}.
38+
* <li>The individual errors returned in {@link #getErrors()}.
39+
* <li>The context returned in {@link #getLocalContext()}.
40+
* <li>The keys/values in the {@link #getExtensions()} {@link Map}.
41+
* </ul>
3242
*
3343
* @param <T> The type of the data fetched
3444
*/
@@ -125,6 +135,35 @@ public <R> DataFetcherResult<R> map(Function<@Nullable T, @Nullable R> transform
125135
.build();
126136
}
127137

138+
139+
@Override
140+
public boolean equals(Object o) {
141+
if (o == null || getClass() != o.getClass()) {
142+
return false;
143+
}
144+
145+
DataFetcherResult<?> that = (DataFetcherResult<?>) o;
146+
return Objects.equals(data, that.data)
147+
&& errors.equals(that.errors)
148+
&& Objects.equals(localContext, that.localContext)
149+
&& Objects.equals(extensions, that.extensions);
150+
}
151+
152+
@Override
153+
public int hashCode() {
154+
return Objects.hash(data, errors, localContext, extensions);
155+
}
156+
157+
@Override
158+
public String toString() {
159+
return "DataFetcherResult{" +
160+
"data=" + data +
161+
", errors=" + errors +
162+
", localContext=" + localContext +
163+
", extensions=" + extensions +
164+
'}';
165+
}
166+
128167
/**
129168
* Creates a new data fetcher result builder
130169
*

src/test/groovy/graphql/execution/DataFetcherResultTest.groovy

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package graphql.execution
22

3+
import graphql.GraphQLError
34
import graphql.InvalidSyntaxError
45
import graphql.validation.ValidationError
56
import graphql.validation.ValidationErrorType
@@ -107,4 +108,77 @@ class DataFetcherResultTest extends Specification {
107108
result.getExtensions() == [a : "b"]
108109
result.getErrors() == [error1, error2]
109110
}
111+
112+
def "implements equals/hashCode for matching results"() {
113+
when:
114+
def firstResult = toDataFetcherResult(first)
115+
def secondResult = toDataFetcherResult(second)
116+
117+
then:
118+
firstResult == secondResult
119+
firstResult.hashCode() == secondResult.hashCode()
120+
121+
where:
122+
first | second
123+
[data: "A string"] | [data: "A string"]
124+
[data: 5] | [data: 5]
125+
[data: ["a", "b"]] | [data: ["a", "b"]]
126+
[errors: [error("An error")]] | [errors: [error("An error")]]
127+
[data: "A value", errors: [error("An error")]] | [data: "A value", errors: [error("An error")]]
128+
[data: "A value", localContext: 5] | [data: "A value", localContext: 5]
129+
[data: "A value", errors: [error("An error")], localContext: 5] | [data: "A value", errors: [error("An error")], localContext: 5]
130+
[data: "A value", extensions: ["key": "value"]] | [data: "A value", extensions: ["key": "value"]]
131+
[data: "A value", errors: [error("An error")], localContext: 5, extensions: ["key": "value"]] | [data: "A value", errors: [error("An error")], localContext: 5, extensions: ["key": "value"]]
132+
}
133+
134+
def "implements equals/hashCode for different results"() {
135+
when:
136+
def firstResult = toDataFetcherResult(first)
137+
def secondResult = toDataFetcherResult(second)
138+
139+
then:
140+
firstResult != secondResult
141+
firstResult.hashCode() != secondResult.hashCode()
142+
143+
where:
144+
first | second
145+
[data: "A string"] | [data: "A different string"]
146+
[data: 5] | [data: "not 5"]
147+
[data: ["a", "b"]] | [data: ["a", "c"]]
148+
[errors: [error("An error")]] | [errors: [error("A different error")]]
149+
[data: "A value", errors: [error("An error")]] | [data: "A different value", errors: [error("An error")]]
150+
[data: "A value", localContext: 5] | [data: "A value", localContext: 1]
151+
[data: "A value", errors: [error("An error")], localContext: 5] | [data: "A value", errors: [error("A different error")], localContext: 5]
152+
[data: "A value", extensions: ["key": "value"]] | [data: "A value", extensions: ["key", "different value"]]
153+
[data: "A value", errors: [error("An error")], localContext: 5, extensions: ["key": "value"]] | [data: "A value", errors: [error("An error")], localContext: 5, extensions: ["key": "different value"]]
154+
}
155+
156+
private static DataFetcherResult toDataFetcherResult(Map<String, Object> resultFields) {
157+
def resultBuilder = DataFetcherResult.newResult();
158+
resultFields.forEach { key, value ->
159+
if (value != null) {
160+
switch (key) {
161+
case "data":
162+
resultBuilder.data(value)
163+
break;
164+
case "errors":
165+
resultBuilder.errors(value as List<GraphQLError>);
166+
break;
167+
case "localContext":
168+
resultBuilder.localContext(value);
169+
break;
170+
case "extensions":
171+
resultBuilder.extensions(value as Map<Object, Object>);
172+
break;
173+
}
174+
}
175+
}
176+
return resultBuilder.build();
177+
}
178+
179+
private static GraphQLError error(String message) {
180+
return GraphQLError.newError()
181+
.message(message)
182+
.build();
183+
}
110184
}

0 commit comments

Comments
 (0)