From 1dbb9c6fe620e2e79a902d2521d6e04034f09020 Mon Sep 17 00:00:00 2001 From: Rob Richard <rob@1stdibs.com> Date: Thu, 17 Apr 2025 15:57:44 -0400 Subject: [PATCH 1/5] Define "execution result" and "request error result" Signed-off-by: Rob Richard <rob@1stdibs.com> --- spec/Section 6 -- Execution.md | 23 +++---- spec/Section 7 -- Response.md | 110 +++++++++++++++++++-------------- 2 files changed, 76 insertions(+), 57 deletions(-) diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index f3e080705..efb283d72 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -310,12 +310,12 @@ MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues): - Let {responseStream} be a new _event stream_. - When {sourceStream} emits {sourceValue}: - - Let {response} be the result of running + - Let {executionResult} be the result of running {ExecuteSubscriptionEvent(subscription, schema, variableValues, sourceValue)}. - If internal {error} was raised: - Cancel {sourceStream}. - - Complete {responseStream} with {error}. + - Complete {executionResult} with {error}. - Otherwise emit {response} on {responseStream}. - When {sourceStream} completes normally: - Complete {responseStream} normally. @@ -827,23 +827,24 @@ or coercion, at a specific _response position_. While these errors must be reported in the response, they are "handled" by producing partial {"data"} in the _response_. -Note: This is distinct from a _request error_ which results in a response with -no data. +Note: This is distinct from a _request error_ which results in a _request error +result_ with no data. If an execution error is raised while resolving a field (either directly or nested inside any lists), it is handled as though the _response position_ at which the error occurred resolved to {null}, and the error must be added to the -{"errors"} list in the response. +{"errors"} list in the _execution result_. If the result of resolving a _response position_ is {null} (either due to the result of {ResolveFieldValue()} or because an execution error was raised), and that position is of a `Non-Null` type, then an execution error is raised at that -position. The error must be added to the {"errors"} list in the response. +position. The error must be added to the {"errors"} list in the _execution +result_. If a _response position_ resolves to {null} because of an execution error which -has already been added to the {"errors"} list in the response, the {"errors"} -list must not be further affected. That is, only one error should be added to -the errors list per _response position_. +has already been added to the {"errors"} list in the _execution result_, the +{"errors"} list must not be further affected. That is, only one error should be +added to the errors list per _response position_. Since `Non-Null` response positions cannot be {null}, execution errors are propagated to be handled by the parent _response position_. If the parent @@ -857,5 +858,5 @@ position_ must resolve to {null}. If the `List` type is also wrapped in a `Non-Null`, the execution error continues to propagate upwards. If every _response position_ from the root of the request to the source of the -execution error has a `Non-Null` type, then the {"data"} entry in the response -should be {null}. +execution error has a `Non-Null` type, then the {"data"} entry in the _execution +result_ should be {null}. diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 037b2a167..190eb0cc0 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -9,40 +9,59 @@ the case that any _execution error_ was raised and replaced with {null}. ## Response Format -A GraphQL request returns either a _response_ or a _response stream_. +:: A GraphQL request returns a _response_. A _response_ is either an _execution +result_, a _request error result_, or a _response stream_. -### Response +### Execution Result -:: A GraphQL request returns a _response_ when the GraphQL operation is a query -or mutation. A _response_ must be a map. +:: A GraphQL request returns an _execution result_ when the GraphQL operation is +a query or mutation and the request included execution. Additionally, an +execution result is the event emitted by a _response stream_. -If the request raised any errors, the response map must contain an entry with -key {"errors"}. The value of this entry is described in the "Errors" section. If -the request completed without raising any errors, this entry must not be +An _execution result_ must be map. + +The _execution result_ must contain an entry with key {"data"}. The value of +this entry is described in the "Data" section. + +If execution raised any errors, the _execution result_ must contain an entry +with key {"errors"}. The value of this entry must be a non-empty list of +_execution error_ raised during execution. Each error must be a map as described +in the "Errors" section below. If the request completed without raising any +errors, this entry must not be present. + +Note: When {"errors"} is present in a _execution result_, it may be helpful for +it to appear first when serialized to make it more apparent that errors are present. -If the request included execution, the response map must contain an entry with -key {"data"}. The value of this entry is described in the "Data" section. If the -request failed before execution, due to a syntax error, missing information, or -validation error, this entry must not be present. +The _execution result_ may also contain an entry with key `extensions`. The +value of this entry is described in the "Extensions" section. -The response map may also contain an entry with key `extensions`. This entry, if -set, must have a map as its value. This entry is reserved for implementers to -extend the protocol however they see fit, and hence there are no additional -restrictions on its contents. +### Request Error Result -To ensure future changes to the protocol do not break existing services and -clients, the top level response map must not contain any entries other than the -three described above. +:: A GraphQL request returns a _request error result_ when the request fails +before execution. A request may fail before execution due to a syntax error, +missing information, or validation error, resulting in one or more _request +error_ being raised. This request will result in no response data. + +A _request error result_ must be a map. + +The _request error result_ map must contain an entry with key {"errors"}. The +value of this entry must be a non-empty list of _request error_ raised during +the _request_. It must contain at least one indicating why no data was able to +be returned. Each error must be a map as described in the "Errors" section +below. + +Note: It may be helpful for the {"errors"} key to appear first when serialized +to make it more apparent that errors are present. -Note: When {"errors"} is present in the response, it may be helpful for it to -appear first when serialized to make it more clear when errors are present in a -response during debugging. +The _request error result_ map may also contain an entry with key `extensions`. +The value of this entry is described in the "Extensions" section. ### Response Stream :: A GraphQL request returns a _response stream_ when the GraphQL operation is a -subscription. A _response stream_ must be a stream of _response_. +subscription and the request included execution. A response stream must be a +stream of _execution result_. ### Response Position @@ -89,37 +108,25 @@ found at `["hero", "friends"]`, the hero's first friend at ### Data -The {"data"} entry in the response will be the result of the execution of the -requested operation. If the operation was a query, this output will be an object -of the query root operation type; if the operation was a mutation, this output -will be an object of the mutation root operation type. +The {"data"} entry in the _execution result_ will be the result of the execution +of the requested operation. If the operation was a query, this output will be an +object of the query root operation type; if the operation was a mutation, this +output will be an object of the mutation root operation type. The response data is the result of accumulating the resolved result of all response positions during execution. -If an error was raised before execution begins, the {"data"} entry should not be -present in the response. +If an error was raised before execution begins, the _response_ must be a +_request error result_. The {"data"} entry must not be present in this map. If an error was raised during the execution that prevented a valid response, the {"data"} entry in the response should be `null`. ### Errors -The {"errors"} entry in the response is a non-empty list of errors raised during -the _request_, where each error is a map of data described by the error result -format below. - -If present, the {"errors"} entry in the response must contain at least one -error. If no errors were raised during the request, the {"errors"} entry must -not be present in the response. - -If the {"data"} entry in the response is not present, the {"errors"} entry must -be present. It must contain at least one _request error_ indicating why no data -was able to be returned. - -If the {"data"} entry in the response is present (including if it is the value -{null}), the {"errors"} entry must be present if and only if one or more -_execution error_ was raised during execution. +The {"errors"} entry in the _execution result_ or _request error result_ is a +non-empty list of errors raised during the _request_, where each error is a map +of data described by the error result format below. **Request Errors** @@ -130,9 +137,9 @@ to determine which operation to execute, or invalid input values for variables. A request error is typically the fault of the requesting client. -If a request error is raised, the {"data"} entry in the response must not be -present, the {"errors"} entry must include the error, and request execution -should be halted. +If a request error is raised, the _response_ must be a _request error result_. +The {"data"} entry in this map must not be present, the {"errors"} entry must +include the error, and request execution should be halted. **Execution Errors** @@ -307,6 +314,17 @@ discouraged. } ``` +### Extensions + +The {"extensions"} entry in an _execution result_ or _request error result_, if +set, must have a map as its value. This entry is reserved for implementers to +extend the protocol however they see fit, and hence there are no additional +restrictions on its contents. + +To ensure future changes to the protocol do not break existing services and +clients, the _execution result_ and _request error result_ maps must not contain +any entries other than those described above. + ## Serialization Format GraphQL does not require a specific serialization format. However, clients From fa4358e283c4df76975e69e3a14ee60dc3060f66 Mon Sep 17 00:00:00 2001 From: Rob Richard <rob@1stdibs.com> Date: Thu, 24 Apr 2025 12:11:54 -0400 Subject: [PATCH 2/5] address PR comments --- spec/Section 7 -- Response.md | 40 ++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 190eb0cc0..450e9d45c 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -10,13 +10,14 @@ the case that any _execution error_ was raised and replaced with {null}. ## Response Format :: A GraphQL request returns a _response_. A _response_ is either an _execution -result_, a _request error result_, or a _response stream_. +result_, a _response stream_, or a _request error result_. ### Execution Result :: A GraphQL request returns an _execution result_ when the GraphQL operation is -a query or mutation and the request included execution. Additionally, an -execution result is the event emitted by a _response stream_. +a query or mutation and the request included execution. Additionally, for each +event in a subscription's _source stream_, the _response stream_ will emit an +_execution result_. An _execution result_ must be map. @@ -36,12 +37,22 @@ present. The _execution result_ may also contain an entry with key `extensions`. The value of this entry is described in the "Extensions" section. +### Response Stream + +:: A GraphQL request returns a _response stream_ when the GraphQL operation is a +subscription and the request included execution. A response stream must be a +stream of _execution result_. + ### Request Error Result -:: A GraphQL request returns a _request error result_ when the request fails -before execution. A request may fail before execution due to a syntax error, -missing information, or validation error, resulting in one or more _request -error_ being raised. This request will result in no response data. +:: A GraphQL request returns a _request error result_ when one or more _request +error_ are raised, causing the request to fail before execution. This request +will result in no response data. + +Note: A _request error_ may be raised before execution due to missing +information, syntax errors, validation failure, coercion failure, or any other +reason the implementation may determine should prevent the request from +proceeding. A _request error result_ must be a map. @@ -54,15 +65,11 @@ below. Note: It may be helpful for the {"errors"} key to appear first when serialized to make it more apparent that errors are present. +The _request error result_ map must not contain an entry with key {"data"}. + The _request error result_ map may also contain an entry with key `extensions`. The value of this entry is described in the "Extensions" section. -### Response Stream - -:: A GraphQL request returns a _response stream_ when the GraphQL operation is a -subscription and the request included execution. A response stream must be a -stream of _execution result_. - ### Response Position <a name="sec-Path"> @@ -117,7 +124,7 @@ The response data is the result of accumulating the resolved result of all response positions during execution. If an error was raised before execution begins, the _response_ must be a -_request error result_. The {"data"} entry must not be present in this map. +_request error result_ which will result in no response data. If an error was raised during the execution that prevented a valid response, the {"data"} entry in the response should be `null`. @@ -321,9 +328,12 @@ set, must have a map as its value. This entry is reserved for implementers to extend the protocol however they see fit, and hence there are no additional restrictions on its contents. +### Additional Entries + To ensure future changes to the protocol do not break existing services and clients, the _execution result_ and _request error result_ maps must not contain -any entries other than those described above. +any entries other than those described above. Clients must ignore any entries +other than those described above. ## Serialization Format From 3d9285832d15d6869b87f240d3ee79638be1f490 Mon Sep 17 00:00:00 2001 From: Rob Richard <rob@1stdibs.com> Date: Thu, 24 Apr 2025 12:27:09 -0400 Subject: [PATCH 3/5] fix errors --- spec/Section 7 -- Response.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 450e9d45c..c656c12d3 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -58,9 +58,8 @@ A _request error result_ must be a map. The _request error result_ map must contain an entry with key {"errors"}. The value of this entry must be a non-empty list of _request error_ raised during -the _request_. It must contain at least one indicating why no data was able to -be returned. Each error must be a map as described in the "Errors" section -below. +the _request_. The errors it contains should indicate why no data was able to be +returned. Each error must be a map as described in the "Errors" section below. Note: It may be helpful for the {"errors"} key to appear first when serialized to make it more apparent that errors are present. From a93f61a7473ff9b327a5ab29df1b94f9bc3aed19 Mon Sep 17 00:00:00 2001 From: Rob Richard <robrichard87@gmail.com> Date: Thu, 24 Apr 2025 12:29:45 -0400 Subject: [PATCH 4/5] Apply suggestions from code review fix execution Co-authored-by: Benjie <benjie@jemjie.com> --- spec/Section 6 -- Execution.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index efb283d72..f9b16107c 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -315,8 +315,8 @@ MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues): sourceValue)}. - If internal {error} was raised: - Cancel {sourceStream}. - - Complete {executionResult} with {error}. - - Otherwise emit {response} on {responseStream}. + - Complete {responseStream} with {error}. + - Otherwise emit {executionResult} on {responseStream}. - When {sourceStream} completes normally: - Complete {responseStream} normally. - When {sourceStream} completes with {error}: From a13ec3668d557af0a0396f80a6432f22e53c98b5 Mon Sep 17 00:00:00 2001 From: Rob Richard <rob@1stdibs.com> Date: Thu, 24 Apr 2025 12:34:36 -0400 Subject: [PATCH 5/5] Use updated wording for request error --- spec/Section 7 -- Response.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index c656c12d3..f79100cd2 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -58,8 +58,9 @@ A _request error result_ must be a map. The _request error result_ map must contain an entry with key {"errors"}. The value of this entry must be a non-empty list of _request error_ raised during -the _request_. The errors it contains should indicate why no data was able to be -returned. Each error must be a map as described in the "Errors" section below. +the _request_. It must contain at least one _request error_ indicating why no +data was able to be returned. Each error must be a map as described in the +"Errors" section below. Note: It may be helpful for the {"errors"} key to appear first when serialized to make it more apparent that errors are present.