Skip to content

Commit 535f742

Browse files
author
Håkan Rosenhorn
committed
Began work on Executor flow
1 parent 39fdb7a commit 535f742

File tree

5 files changed

+207
-16
lines changed

5 files changed

+207
-16
lines changed

src/main/scala/se/uprise/graphql/GraphQL.scala

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,40 @@ import java.io.ByteArrayInputStream
44

55
import org.antlr.v4.runtime.{CommonTokenStream, ANTLRInputStream}
66
import se.uprise.graphql.error.GraphQLFormattedError
7-
import se.uprise.parser.GraphQlParser.FieldContext
7+
import se.uprise.graphql.execution.Executor
8+
import se.uprise.parser.GraphQlParser.{DocumentContext, FieldContext}
89
import se.uprise.parser.{GraphQlBaseVisitor, GraphQlParser, GraphQlLexer}
910

1011

1112
import se.uprise.graphql.types.GraphQLSchema
1213

1314

14-
case class GraphQLResult(data: String, errors: List[GraphQLFormattedError])
15+
/**
16+
* The result of a GraphQL parse, validation and execution.
17+
*
18+
* `data` is the result of a successful execution of the query.
19+
* `errors` is included when any errors occurred as a non-empty array.
20+
*/
21+
// FIXME: Should not be type Any
22+
case class GraphQLResult(data: Any, errors: List[GraphQLFormattedError])
1523

1624

17-
class GraphQlVisitor extends GraphQlBaseVisitor[Unit] {
18-
override def visitField(ctx: FieldContext): Unit = {
19-
println("HEJ HEJ", ctx.fieldName().NAME())
20-
}
21-
}
25+
/**
26+
* This is the primary entry point function for fulfilling GraphQL operations
27+
* by parsing, validating, and executing a GraphQL document along side a
28+
* GraphQL schema.
29+
*
30+
* More sophisticated GraphQL servers, such as those which persist queries,
31+
* may wish to separate the validation and execution phases to a static time
32+
* tooling step, and a server runtime step.
33+
*/
34+
2235

36+
// The rootObject seems to be some object/context you can pass along that will be available in all your resolve methods
2337
object GraphQL {
2438
def apply(schema: GraphQLSchema,
25-
requestString: String,
26-
rootObject: Option[String],
39+
requestString: String,
40+
rootObject: Any,
2741
variableValues: Map[String, String],
2842
operationName: String
2943
): GraphQLResult = {
@@ -32,10 +46,15 @@ object GraphQL {
3246
val lexer = new GraphQlLexer(input)
3347
val tokens = new CommonTokenStream(lexer)
3448
val parser = new GraphQlParser(tokens)
35-
36-
val visitor = new GraphQlVisitor()
37-
visitor.visit(parser.document())
38-
49+
50+
val document: DocumentContext = parser.document()
51+
52+
Executor(schema,
53+
rootObject,
54+
document,
55+
operationName,
56+
variableValues)
57+
3958

4059
new GraphQLResult("svejs", List.empty)
4160
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package se.uprise.graphql.error
2+
3+
// FIXME: Extend with proper values from js ref
4+
case class GraphQLError(message: String) extends Exception(message)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package se.uprise.graphql.execution
2+
3+
import se.uprise.graphql.types.GraphQLSchema
4+
import se.uprise.parser.GraphQlParser.{OperationDefinitionContext, FragmentDefinitionContext}
5+
6+
7+
/**
8+
* Data that must be available at all points during query execution.
9+
*
10+
* Namely, schema of the type system that is currently executing,
11+
* and the fragments defined in the query document
12+
*/
13+
// FIXME: root should not have type Any
14+
// FIXME: Variables should not be map of Any (?)
15+
case class ExecutionContext(
16+
schema: GraphQLSchema,
17+
fragments: Map[String, FragmentDefinitionContext],
18+
root: Any,
19+
operation: OperationDefinitionContext,
20+
variables: Map[String, Any],
21+
errors: List[Exception]) {
22+
23+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package se.uprise.graphql.execution
2+
3+
import se.uprise.graphql.error.{GraphQLError, GraphQLFormattedError}
4+
import se.uprise.graphql.types.GraphQLSchema
5+
import se.uprise.parser.GraphQlParser
6+
import se.uprise.parser.GraphQlParser._
7+
import scala.collection.JavaConversions._
8+
9+
/**
10+
* Terminology
11+
*
12+
* "Definitions" are the generic name for top-level statements in the document.
13+
* Examples of this include:
14+
* 1) Operations (such as a query)
15+
* 2) Fragments
16+
*
17+
* "Operations" are a generic name for requests in the document.
18+
* Examples of this include:
19+
* 1) query,
20+
* 2) mutation
21+
*
22+
* "Selections" are the statements that can appear legally and at
23+
* single level of the query. These include:
24+
* 1) field references e.g "a"
25+
* 2) fragment "spreads" e.g. "...c"
26+
* 3) inline fragment "spreads" e.g. "...on Type { a }"
27+
*/
28+
29+
30+
/**
31+
* The result of execution. `data` is the result of executing the
32+
* query, `errors` is null if no errors occurred, and is a
33+
* non-empty array if an error occurred.
34+
*/
35+
// FIXME: root should not be type Any
36+
case class ExecutionResult(data: Any, errors: List[GraphQLFormattedError])
37+
38+
39+
object Executor {
40+
def apply(schema: GraphQLSchema,
41+
root: Any,
42+
ast: DocumentContext,
43+
operationName: String,
44+
variableValues: Map[String, String] = Map.empty): ExecutionResult = {
45+
46+
// FIXME: We cannot pass a immutable List of errors in the execution context
47+
val exeContext = buildExecutionContext(schema, root, ast, operationName, variableValues, List.empty)
48+
49+
// FIXME: Result is currently Any
50+
val result = executeOperation(exeContext, root, exeContext.operation)
51+
52+
new ExecutionResult("FIXME", List.empty)
53+
}
54+
55+
/**
56+
* Implements the "Evaluating operations" section of the spec.
57+
*/
58+
def executeOperation(exeContext: ExecutionContext,
59+
root: Any,
60+
operation: OperationDefinitionContext): Any = {
61+
62+
}
63+
64+
65+
// FIXME: root should perhaps not be type Any
66+
def buildExecutionContext(schema: GraphQLSchema,
67+
root: Any,
68+
ast: DocumentContext,
69+
operationName: String,
70+
variableValues: Map[String, String] = Map.empty,
71+
errors: List[Exception]): ExecutionContext = {
72+
73+
// Convert from Java to Scala
74+
val definitionSet: List[DefinitionContext] = ast.definition().toList
75+
76+
// FIXME: Might be improved...
77+
val fragments: Map[String, FragmentDefinitionContext] = definitionSet flatMap { entry =>
78+
entry.getRuleIndex match {
79+
case GraphQlParser.RULE_fragmentDefinition =>
80+
val fragment = entry.asInstanceOf[FragmentDefinitionContext]
81+
Some(fragment.fragmentName().NAME().getText -> fragment)
82+
case _ => None
83+
}
84+
} toMap
85+
86+
// FIXME: Might be improved...
87+
val operations: Map[String, OperationDefinitionContext] = definitionSet flatMap { entry =>
88+
entry.getRuleIndex match {
89+
case GraphQlParser.RULE_operationDefinition =>
90+
val operation = entry.asInstanceOf[OperationDefinitionContext]
91+
Some(operation.NAME().getText -> operation)
92+
case _ => None
93+
}
94+
} toMap
95+
96+
if (operationName.length > 0 && operations.size != 1) {
97+
throw new GraphQLError("Must provide operation name if query contains multiple operations")
98+
}
99+
100+
val opName = operationName match {
101+
case name if name.length > 0 => name
102+
case _ => operations.head._1
103+
}
104+
105+
val operation = operations.get(opName) match {
106+
case Some(entry) => entry
107+
case None => throw new GraphQLError("Unknown operation name: " + opName)
108+
}
109+
110+
val variables = getVariableValues(schema,
111+
operation.variableDefinitions().variableDefinition().toList,
112+
variableValues
113+
)
114+
115+
ExecutionContext(schema, fragments, root, operation, variables, errors)
116+
}
117+
118+
119+
/**
120+
* Prepares an object map of variables of the correct type based on the provided
121+
* variable definitions and arbitrary input. If the input cannot be coerced
122+
* to match the variable definitions, a GraphQLError will be thrown.
123+
*/
124+
/*
125+
FIXME: Replace Any types with proper Types resolved from the variableValues
126+
Ex: query Example($size: Int) {
127+
variableValues = { size: 100 }
128+
*/
129+
130+
def getVariableValues(schema: GraphQLSchema,
131+
definitionASTs: List[VariableDefinitionContext] = List.empty,
132+
variableValues: Map[String, String] = Map.empty): Map[String, Any] = {
133+
134+
135+
// FIXME: Implement
136+
// definitionASTs map { definition =>
137+
// val varName = definition.variable().NAME().getText
138+
// }
139+
140+
Map.empty
141+
}
142+
143+
def getVariableValue(schema: GraphQLSchema,
144+
definitionAST: VariableDefinitionContext,
145+
input: String) = {
146+
147+
}
148+
}
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
package se.uprise.graphql.types
22

3-
/**
4-
* Created by Håkan Rosenhorn on 2015-07-09.
5-
*/
63
trait GraphQLInputType

0 commit comments

Comments
 (0)