Skip to content

Commit 37ce4c9

Browse files
committed
Added serilization support to graphql language nodes
1 parent 75dc4b3 commit 37ce4c9

7 files changed

Lines changed: 164 additions & 6 deletions

File tree

‎src/main/java/graphql/GraphQLError.java‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,22 @@
33

44
import graphql.language.SourceLocation;
55

6+
import java.io.Serializable;
67
import java.util.List;
78
import java.util.Map;
89

910
/**
11+
* The interface describing graphql errors
12+
*
13+
* NOTE: This class implements {@link java.io.Serializable} and hence it can be serialised and placed into a distributed cache. However we
14+
* are not aiming to provide long term compatibility and do not intend for you to place this serialised data into permanent storage,
15+
* with times frames that cross graphql-java versions. While we don't change things unnecessarily, we may inadvertently break
16+
* the serialised compatibility across versions.
17+
*
1018
* @see <a href="https://facebook.github.io/graphql/#sec-Errors">GraphQL Spec - 7.2.2 Errors</a>
1119
*/
1220
@PublicApi
13-
public interface GraphQLError {
21+
public interface GraphQLError extends Serializable {
1422

1523
/**
1624
* @return a description of the error intended for the developer as a guide to understand and correct the error

‎src/main/java/graphql/execution/preparsed/PreparsedDocumentEntry.java‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import graphql.GraphQLError;
44
import graphql.language.Document;
55

6+
import java.io.Serializable;
67
import java.util.List;
78

89
import static graphql.Assert.assertNotNull;
@@ -11,8 +12,13 @@
1112
/**
1213
* An instance of a preparsed document entry represents the result of a query parse and validation, like
1314
* an either implementation it contains either the correct result in th document property or the errors.
15+
*
16+
* NOTE: This class implements {@link java.io.Serializable} and hence it can be serialised and placed into a distributed cache. However we
17+
* are not aiming to provide long term compatibility and do not intend for you to place this serialised data into permanent storage,
18+
* with times frames that cross graphql-java versions. While we don't change things unnecessarily, we may inadvertently break
19+
* the serialised compatibility across versions.
1420
*/
15-
public class PreparsedDocumentEntry {
21+
public class PreparsedDocumentEntry implements Serializable {
1622
private final Document document;
1723
private final List<? extends GraphQLError> errors;
1824

‎src/main/java/graphql/language/Comment.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package graphql.language;
22

3-
public class Comment {
3+
import java.io.Serializable;
4+
5+
public class Comment implements Serializable {
46
public final String content;
57
public final SourceLocation sourceLocation;
68

‎src/main/java/graphql/language/Description.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package graphql.language;
22

3-
public class Description {
3+
import java.io.Serializable;
4+
5+
public class Description implements Serializable {
46
public final String content;
57
public final SourceLocation sourceLocation;
68
public final boolean multiLine;

‎src/main/java/graphql/language/Node.java‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@
44
import graphql.util.TraversalControl;
55
import graphql.util.TraverserContext;
66

7+
import java.io.Serializable;
78
import java.util.List;
89

9-
public interface Node<T extends Node> {
10+
/**
11+
* The base interface for virtually all graphql language elements
12+
*
13+
* NOTE: This class implements {@link java.io.Serializable} and hence it can be serialised and placed into a distributed cache. However we
14+
* are not aiming to provide long term compatibility and do not intend for you to place this serialised data into permanent storage,
15+
* with times frames that cross graphql-java versions. While we don't change things unnecessarily, we may inadvertently break
16+
* the serialised compatibility across versions.
17+
*/
18+
public interface Node<T extends Node> extends Serializable {
1019

1120
/**
1221
* @return a list of the children of this node

‎src/main/java/graphql/language/SourceLocation.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package graphql.language;
22

33

4-
public class SourceLocation {
4+
import java.io.Serializable;
5+
6+
public class SourceLocation implements Serializable {
57

68
private final int line;
79
private final int column;
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package graphql.language
2+
3+
import graphql.GraphQLError
4+
import graphql.InvalidSyntaxError
5+
import graphql.TestUtil
6+
import graphql.execution.preparsed.PreparsedDocumentEntry
7+
import graphql.validation.ValidationError
8+
import graphql.validation.ValidationErrorType
9+
import spock.lang.Specification
10+
11+
class SerialisationTest extends Specification {
12+
13+
static <T> T serialisedDownAndBack(T inputObj) {
14+
def file = File.createTempFile("serialised", ".bin")
15+
16+
FileOutputStream f = new FileOutputStream(file)
17+
ObjectOutputStream o = new ObjectOutputStream(f)
18+
19+
o.writeObject(inputObj)
20+
o.close()
21+
22+
FileInputStream fi = new FileInputStream(file)
23+
ObjectInputStream oi = new ObjectInputStream(fi)
24+
25+
T object = oi.readObject() as T
26+
oi.close()
27+
28+
return object
29+
}
30+
31+
static SourceLocation srcLoc(int line, int col) {
32+
new SourceLocation(line, col)
33+
}
34+
35+
def query = '''
36+
#comment
37+
query HeroForEpisode($ep: Episode!) {
38+
hero(episode: $ep) {
39+
name
40+
... on Droid {
41+
primaryFunction
42+
}
43+
... on Human {
44+
height
45+
}
46+
}
47+
48+
rightComparison: hero(episode: JEDI) {
49+
...comparisonFields
50+
}
51+
52+
differentArgObjects( s : "s", int : 1, b : true, list : [{a : "s"}, 1, "s"], obj : {x : "s", y : true} )
53+
54+
directiveOnField @skip(if:true)
55+
}
56+
57+
fragment comparisonFields on Character {
58+
name
59+
appearsIn
60+
friends {
61+
name
62+
}
63+
}
64+
'''
65+
66+
def "#1071 basic document serialisation"() {
67+
68+
// this helps prove that we can serialise a query document
69+
// and hence allow caching of that parsed document via
70+
// systems that require serializable objects
71+
72+
when:
73+
Document originalDoc = TestUtil.parseQuery(query)
74+
def originalAst = AstPrinter.printAst(originalDoc)
75+
76+
Document newDocument = serialisedDownAndBack(originalDoc)
77+
def newAst = AstPrinter.printAst(newDocument)
78+
79+
then:
80+
81+
originalAst == newAst
82+
}
83+
84+
def "basic error serialisation"() {
85+
when:
86+
GraphQLError syntaxError = new InvalidSyntaxError(srcLoc(1, 2), "Bad Syntax")
87+
88+
GraphQLError serialisedError = serialisedDownAndBack(syntaxError)
89+
90+
then:
91+
92+
syntaxError.getMessage() == serialisedError.getMessage()
93+
syntaxError.getLocations() == serialisedError.getLocations()
94+
}
95+
96+
def "PreparsedDocumentEntry with document is serializable"() {
97+
98+
when:
99+
Document originalDoc = TestUtil.parseQuery(query)
100+
def originalEntry = new PreparsedDocumentEntry(originalDoc)
101+
def originalAst = AstPrinter.printAst(originalEntry.getDocument())
102+
103+
PreparsedDocumentEntry newEntry = serialisedDownAndBack(originalEntry)
104+
def newAst = AstPrinter.printAst(newEntry.getDocument())
105+
106+
then:
107+
108+
originalAst == newAst
109+
}
110+
111+
def "PreparsedDocumentEntry with errors is serializable"() {
112+
113+
when:
114+
GraphQLError syntaxError1 = new InvalidSyntaxError(srcLoc(1, 1), "Bad Syntax 1")
115+
GraphQLError validationError2 = new ValidationError(ValidationErrorType.FieldUndefined, srcLoc(2, 2), "Bad Query 2")
116+
def originalEntry = new PreparsedDocumentEntry([syntaxError1, validationError2])
117+
118+
PreparsedDocumentEntry newEntry = serialisedDownAndBack(originalEntry)
119+
120+
then:
121+
122+
newEntry.getErrors().size() == 2
123+
newEntry.getErrors().get(0).getMessage() == syntaxError1.getMessage()
124+
newEntry.getErrors().get(0).getLocations() == syntaxError1.getLocations()
125+
126+
newEntry.getErrors().get(1).getMessage() == validationError2.getMessage()
127+
newEntry.getErrors().get(1).getLocations() == validationError2.getLocations()
128+
}
129+
}

0 commit comments

Comments
 (0)