apollo_compiler/response.rs
1//! GraphQL [responses](https://spec.graphql.org/draft/#sec-Response)
2//!
3//! This exists primarily to support [`introspection::partial_execute`].
4
5#[cfg(doc)]
6use crate::introspection;
7use crate::parser::LineColumn;
8use crate::parser::SourceMap;
9use crate::parser::SourceSpan;
10use serde::Deserialize;
11use serde::Serialize;
12/// Re-export of the version of the `serde_json_bytes` crate used for [`JsonValue`] and [`JsonMap`]
13pub use serde_json_bytes;
14
15/// A JSON-compatible dynamically-typed value.
16///
17/// Note: [`serde_json_bytes::Value`] is similar
18/// to [`serde_json::Value`][serde_json_bytes::serde_json::Value]
19/// but uses its reference-counted [`ByteString`][serde_json_bytes::ByteString]
20/// for string values and map keys.
21pub type JsonValue = serde_json_bytes::Value;
22
23/// A JSON-compatible object/map with string keys and dynamically-typed values.
24pub type JsonMap = serde_json_bytes::Map<serde_json_bytes::ByteString, JsonValue>;
25
26/// A [response](https://spec.graphql.org/October2021/#sec-Response-Format)
27/// to a GraphQL request that did not cause any [request error][crate::request::RequestError]
28/// and started [execution](https://spec.graphql.org/draft/#sec-Execution)
29/// of selection sets and fields.
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31#[serde(deny_unknown_fields)]
32pub struct ExecutionResponse {
33 // <https://spec.graphql.org/October2021/#note-6f005> suggests serializing this first
34 #[serde(skip_serializing_if = "Vec::is_empty")]
35 #[serde(default)]
36 pub errors: Vec<GraphQLError>,
37
38 pub data: Option<JsonMap>,
39}
40
41/// A serializable [error](https://spec.graphql.org/October2021/#sec-Errors.Error-result-format),
42/// as found in a GraphQL response.
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(deny_unknown_fields)]
45pub struct GraphQLError {
46 /// The error message.
47 pub message: String,
48
49 /// Locations in relevant to the error, if any.
50 #[serde(skip_serializing_if = "Vec::is_empty")]
51 #[serde(default)]
52 pub locations: Vec<LineColumn>,
53
54 /// If non-empty, the error is a [field error]
55 /// for the particular field found at this path in [`ExecutionResponse::data`].
56 ///
57 /// [field error]: https://spec.graphql.org/October2021/#sec-Errors.Field-errors
58 #[serde(skip_serializing_if = "Vec::is_empty")]
59 #[serde(default)]
60 pub path: Vec<ResponseDataPathSegment>,
61
62 /// Reserved for any additional information
63 #[serde(skip_serializing_if = "JsonMap::is_empty")]
64 #[serde(default)]
65 pub extensions: JsonMap,
66}
67
68/// A `Vec<ResponseDataPathSegment>` like in [`GraphQLError::path`]
69/// represents a [path](https://spec.graphql.org/draft/#sec-Errors.Error-Result-Format)
70/// into [`ExecutionResponse::data`],
71/// starting at the root and indexing into increasingly nested JSON objects or arrays.
72///
73/// # Example
74///
75/// In a GraphQL response like this:
76///
77/// ```json
78/// {
79/// "data": {
80/// "players": [
81/// {"name": "Alice"},
82/// {"name": "Bob"}
83/// ]
84/// },
85/// "errors": [
86/// {
87/// "message": "Something went wrong",
88/// "path": ["players", 1, "name"]
89/// }
90/// ]
91/// }
92/// ```
93///
94/// The error path would have a Rust representation like
95/// `vec![Field("players"), ListIndex(1), Field("name")]`
96/// and designate the value `"name": "Bob"`.
97#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
98#[serde(untagged)]
99pub enum ResponseDataPathSegment {
100 /// The relevant key in an object value
101 Field(crate::Name),
102
103 /// The index of the relevant item in a list value
104 ListIndex(usize),
105}
106
107impl GraphQLError {
108 pub fn new(
109 message: impl Into<String>,
110 location: Option<SourceSpan>,
111 sources: &SourceMap,
112 ) -> Self {
113 Self {
114 message: message.into(),
115 locations: location
116 .into_iter()
117 .filter_map(|location| location.line_column(sources))
118 .collect(),
119 path: Default::default(),
120 extensions: Default::default(),
121 }
122 }
123}