hive_router_plan_executor/response/
graphql_error.rs1use core::fmt;
2use graphql_parser::Pos;
3use graphql_tools::validation::utils::ValidationError;
4use serde::{de, Deserialize, Deserializer, Serialize};
5use sonic_rs::Value;
6use std::collections::HashMap;
7
8#[derive(Clone, Debug, Deserialize, Serialize)]
9#[serde(rename_all = "camelCase")]
10pub struct GraphQLError {
11 pub message: String,
12 #[serde(default, skip_serializing_if = "is_none_or_empty")]
13 pub locations: Option<Vec<GraphQLErrorLocation>>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub path: Option<GraphQLErrorPath>,
16 #[serde(default, skip_serializing_if = "GraphQLErrorExtensions::is_empty")]
17 pub extensions: GraphQLErrorExtensions,
18}
19
20fn is_none_or_empty<T>(opt: &Option<Vec<T>>) -> bool {
21 opt.as_ref().is_none_or(|v| v.is_empty())
22}
23
24impl From<String> for GraphQLError {
25 fn from(message: String) -> Self {
26 GraphQLError {
27 message,
28 locations: None,
29 path: None,
30 extensions: GraphQLErrorExtensions::default(),
31 }
32 }
33}
34
35impl From<&str> for GraphQLError {
36 fn from(message: &str) -> Self {
37 GraphQLError {
38 message: message.to_string(),
39 locations: None,
40 path: None,
41 extensions: GraphQLErrorExtensions::default(),
42 }
43 }
44}
45
46impl From<&ValidationError> for GraphQLError {
47 fn from(val: &ValidationError) -> Self {
48 GraphQLError {
49 message: val.message.to_string(),
50 locations: Some(val.locations.iter().map(|pos| pos.into()).collect()),
51 path: None,
52 extensions: GraphQLErrorExtensions::new_from_code("GRAPHQL_VALIDATION_FAILED"),
53 }
54 }
55}
56
57impl From<&Pos> for GraphQLErrorLocation {
58 fn from(val: &Pos) -> Self {
59 GraphQLErrorLocation {
60 line: val.line,
61 column: val.column,
62 }
63 }
64}
65
66impl GraphQLError {
67 pub fn entity_index_and_path<'a>(&'a self) -> Option<EntityIndexAndPath<'a>> {
68 self.path.as_ref().and_then(|p| p.entity_index_and_path())
69 }
70
71 pub fn normalize_entity_error(
72 self,
73 entity_index_error_map: &HashMap<&usize, Vec<GraphQLErrorPath>>,
74 ) -> Vec<GraphQLError> {
75 if let Some(entity_index_and_path) = &self.entity_index_and_path() {
76 if let Some(entity_error_paths) =
77 entity_index_error_map.get(&entity_index_and_path.entity_index)
78 {
79 return entity_error_paths
80 .iter()
81 .map(|error_path| {
82 let mut new_error_path = error_path.clone();
83 new_error_path.extend_from_slice(entity_index_and_path.rest_of_path);
84 GraphQLError {
85 path: Some(new_error_path),
86 ..self.clone()
87 }
88 })
89 .collect();
90 }
91 }
92 vec![self]
93 }
94
95 pub fn from_message_and_extensions(
96 message: String,
97 extensions: GraphQLErrorExtensions,
98 ) -> Self {
99 GraphQLError {
100 message,
101 locations: None,
102 path: None,
103 extensions,
104 }
105 }
106
107 pub fn add_subgraph_name(mut self, subgraph_name: &str) -> Self {
108 self.extensions
109 .service_name
110 .get_or_insert(subgraph_name.to_string());
111 self.extensions
112 .code
113 .get_or_insert("DOWNSTREAM_SERVICE_ERROR".to_string());
114 self
115 }
116
117 pub fn add_affected_path(mut self, affected_path: String) -> Self {
118 self.extensions.affected_path = Some(affected_path);
119 self
120 }
121}
122
123#[derive(Clone, Debug, Deserialize, Serialize)]
124pub struct GraphQLErrorLocation {
125 pub line: usize,
126 pub column: usize,
127}
128
129#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
130#[serde(untagged)]
131pub enum GraphQLErrorPathSegment {
132 String(String),
133 Index(usize),
134}
135
136#[derive(Clone, Debug, Default, Deserialize, Serialize)]
137#[serde(transparent)]
138pub struct GraphQLErrorPath {
139 pub segments: Vec<GraphQLErrorPathSegment>,
140}
141
142pub struct EntityIndexAndPath<'a> {
143 pub entity_index: usize,
144 pub rest_of_path: &'a [GraphQLErrorPathSegment],
145}
146
147impl GraphQLErrorPath {
148 pub fn with_capacity(capacity: usize) -> Self {
149 GraphQLErrorPath {
150 segments: Vec::with_capacity(capacity),
151 }
152 }
153 pub fn concat(&self, segment: GraphQLErrorPathSegment) -> Self {
154 let mut new_path = self.segments.clone();
155 new_path.push(segment);
156 GraphQLErrorPath { segments: new_path }
157 }
158
159 pub fn concat_index(&self, index: usize) -> Self {
160 self.concat(GraphQLErrorPathSegment::Index(index))
161 }
162
163 pub fn concat_str(&self, field: String) -> Self {
164 self.concat(GraphQLErrorPathSegment::String(field))
165 }
166
167 pub fn extend_from_slice(&mut self, other: &[GraphQLErrorPathSegment]) {
168 self.segments.extend_from_slice(other);
169 }
170
171 pub fn entity_index_and_path<'a>(&'a self) -> Option<EntityIndexAndPath<'a>> {
172 match &self.segments.as_slice() {
173 [GraphQLErrorPathSegment::String(maybe_entities), GraphQLErrorPathSegment::Index(entity_index), rest_of_path @ ..]
174 if maybe_entities == "_entities" =>
175 {
176 Some(EntityIndexAndPath {
177 entity_index: *entity_index,
178 rest_of_path,
179 })
180 }
181 _ => None,
182 }
183 }
184}
185
186#[derive(Clone, Debug, Serialize, Default)]
187#[serde(rename_all = "camelCase")]
188pub struct GraphQLErrorExtensions {
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub code: Option<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub service_name: Option<String>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub affected_path: Option<String>,
196 #[serde(flatten)]
197 pub extensions: HashMap<String, Value>,
198}
199
200impl<'de> Deserialize<'de> for GraphQLErrorExtensions {
203 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
204 where
205 D: Deserializer<'de>,
206 {
207 struct GraphQLErrorExtensionsVisitor;
208
209 impl<'de> de::Visitor<'de> for GraphQLErrorExtensionsVisitor {
210 type Value = GraphQLErrorExtensions;
211
212 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
213 formatter.write_str("a map for GraphQLErrorExtensions")
214 }
215
216 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
217 where
218 A: de::MapAccess<'de>,
219 {
220 let mut code = None;
221 let mut service_name = None;
222 let mut affected_path = None;
223 let mut extensions = HashMap::new();
224
225 while let Some(key) = map.next_key::<String>()? {
226 match key.as_str() {
227 "code" => {
228 if code.is_some() {
229 return Err(de::Error::duplicate_field("code"));
230 }
231 code = Some(map.next_value()?);
232 }
233 "serviceName" => {
234 if service_name.is_some() {
235 return Err(de::Error::duplicate_field("serviceName"));
236 }
237 service_name = Some(map.next_value()?);
238 }
239 "affectedPath" => {
240 if affected_path.is_some() {
241 return Err(de::Error::duplicate_field("affectedPath"));
242 }
243 affected_path = map.next_value()?;
244 }
245 other_key => {
246 let value: Value = map.next_value()?;
247 extensions.insert(other_key.to_string(), value);
248 }
249 }
250 }
251
252 Ok(GraphQLErrorExtensions {
253 code,
254 service_name,
255 affected_path,
256 extensions,
257 })
258 }
259 }
260
261 deserializer.deserialize_map(GraphQLErrorExtensionsVisitor)
262 }
263}
264
265impl GraphQLErrorExtensions {
266 pub fn new_from_code(code: &str) -> Self {
267 GraphQLErrorExtensions {
268 code: Some(code.to_string()),
269 service_name: None,
270 affected_path: None,
271 extensions: HashMap::new(),
272 }
273 }
274
275 pub fn new_from_code_and_service_name(code: &str, service_name: &str) -> Self {
276 GraphQLErrorExtensions {
277 code: Some(code.to_string()),
278 service_name: Some(service_name.to_string()),
279 affected_path: None,
280 extensions: HashMap::new(),
281 }
282 }
283
284 pub fn get(&self, key: &str) -> Option<&Value> {
285 self.extensions.get(key)
286 }
287
288 pub fn set(&mut self, key: String, value: Value) {
289 self.extensions.insert(key, value);
290 }
291
292 pub fn is_empty(&self) -> bool {
293 self.code.is_none()
294 && self.service_name.is_none()
295 && self.affected_path.is_none()
296 && self.extensions.is_empty()
297 }
298}