@@ -5,24 +5,32 @@ extension GraphQLHandler {
55 /// https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#get
66 func handleGet( request: Request ) async throws -> Response {
77 let graphQLRequest = try request. query. decode ( GraphQLRequest . self)
8+
9+ let documentAST : Document
810 let operationType : OperationType
911 do {
10- operationType = try graphQLRequest. operationType ( )
12+ documentAST = try parse (
13+ source: Source ( body: graphQLRequest. query, name: graphQLRequest. operationName ?? " " )
14+ )
15+ operationType = try Self . operationTypeOf ( document: documentAST, named: graphQLRequest. operationName)
1116 } catch {
1217 // Indicates a request parsing error
1318 throw Abort ( . badRequest, reason: error. localizedDescription)
1419 }
1520 guard operationType != . mutation else {
1621 throw Abort ( . methodNotAllowed, reason: " Mutations using GET are disallowed " )
1722 }
23+
1824 let graphQLContextComputationInputs = GraphQLContextComputationInputs < WebSocketInitResult > (
1925 vaporRequest: request,
2026 graphQLRequest: graphQLRequest,
2127 websocketInitResult: nil
2228 )
2329 let context = try await computeContext ( graphQLContextComputationInputs)
2430 let result = await execute (
25- graphQLRequest: graphQLRequest,
31+ documentAST: documentAST,
32+ operationName: graphQLRequest. operationName,
33+ variables: graphQLRequest. variables,
2634 context: context,
2735 additionalValidationRules: config. additionalValidationRules
2836 )
@@ -35,43 +43,75 @@ extension GraphQLHandler {
3543 throw Abort ( . unsupportedMediaType, reason: " Missing `Content-Type` header " )
3644 }
3745 let graphQLRequest = try request. content. decode ( GraphQLRequest . self)
46+
47+ let documentAST : Document
48+ do {
49+ documentAST = try parse (
50+ source: Source ( body: graphQLRequest. query, name: graphQLRequest. operationName ?? " " )
51+ )
52+ } catch let error as GraphQLError {
53+ // Indicates a request parsing error
54+ let result = GraphQLResult ( data: nil , errors: [ error] )
55+ return try encodeResponse ( result: result, headers: request. headers)
56+ }
57+
3858 let graphQLContextComputationInputs = GraphQLContextComputationInputs < WebSocketInitResult > (
3959 vaporRequest: request,
4060 graphQLRequest: graphQLRequest,
4161 websocketInitResult: nil
4262 )
4363 let context = try await computeContext ( graphQLContextComputationInputs)
4464 let result = await execute (
45- graphQLRequest: graphQLRequest,
65+ documentAST: documentAST,
66+ operationName: graphQLRequest. operationName,
67+ variables: graphQLRequest. variables,
4668 context: context,
4769 additionalValidationRules: config. additionalValidationRules
4870 )
4971 return try encodeResponse ( result: result, headers: request. headers)
5072 }
5173
5274 private func execute(
53- graphQLRequest: GraphQLRequest ,
75+ documentAST: Document ,
76+ operationName: String ? ,
77+ variables: [ String : Map ] ,
5478 context: Context ,
5579 additionalValidationRules: [ @Sendable ( ValidationContext ) -> Visitor ]
5680 ) async -> GraphQLResult {
5781 // https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#validation
82+
83+ // Validate schema
84+ do {
85+ let schemaValidationErrors = try validateSchema ( schema: schema)
86+ guard schemaValidationErrors. isEmpty else {
87+ return GraphQLResult ( errors: schemaValidationErrors)
88+ }
89+ } catch {
90+ return GraphQLResult ( errors: [ GraphQLError ( error) ] )
91+ }
92+
5893 let validationRules = GraphQL . specifiedRules + additionalValidationRules
94+ // Validate request
95+ let validationErrors = validate (
96+ schema: schema,
97+ ast: documentAST,
98+ rules: validationRules
99+ )
100+ guard validationErrors. isEmpty else {
101+ return GraphQLResult ( errors: validationErrors)
102+ }
59103
60104 // https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md#execution
61105 let result : GraphQLResult
62106 do {
63- result = try await graphql (
107+ result = try await GraphQL . execute (
64108 schema: schema,
65- request : graphQLRequest . query ,
109+ documentAST : documentAST ,
66110 rootValue: rootValue,
67111 context: context,
68- variableValues: graphQLRequest. variables,
69- operationName: graphQLRequest. operationName,
70- validationRules: validationRules
112+ variableValues: variables,
113+ operationName: operationName
71114 )
72- } catch let error as GraphQLError {
73- // This indicates a request parsing error
74- return GraphQLResult ( data: nil , errors: [ error] )
75115 } catch {
76116 return GraphQLResult (
77117 data: nil ,
@@ -122,4 +162,23 @@ extension GraphQLHandler {
122162 try response. content. encode ( result, as: selectedMediaType)
123163 return response
124164 }
165+
166+ private static func operationTypeOf( document: Document , named operationName: String ? = nil ) throws -> OperationType {
167+ let operationDefinitions = document. definitions. filter { operation in
168+ operation. kind == . operationDefinition
169+ } . compactMap { operation in
170+ operation as? OperationDefinition
171+ }
172+
173+ // We don't have to validate the document and check for multiple/missing operations here
174+ // That will be done upon execution.
175+ let operationMatch = operationDefinitions. first { operation in
176+ if let operationName {
177+ return operationName == operation. name? . value
178+ } else {
179+ return true
180+ }
181+ }
182+ return operationMatch? . operation ?? . query
183+ }
125184}
0 commit comments