subgraph/utils/document/get_from_document/
mod.rs

1use bson::{oid::ObjectId, Bson};
2use log::{debug, error, trace};
3
4use super::DocumentUtils;
5
6#[derive(Debug)]
7pub enum DocumentValue {
8    String(String),
9    StringArray(Vec<String>),
10    Int(i32),
11    IntArray(Vec<i32>),
12    Boolean(bool),
13    BooleanArray(Vec<bool>),
14    ObjectID(bson::oid::ObjectId),
15    ObjectIDArray(Vec<bson::oid::ObjectId>),
16    Document(bson::Document),
17    DocumentArray(Vec<bson::Document>),
18    UUID(uuid::Uuid),
19    UUIDArray(Vec<uuid::Uuid>),
20    DateTime(chrono::DateTime<chrono::Utc>),
21    DateTimeArray(Vec<chrono::DateTime<chrono::Utc>>),
22    Null,
23    None,
24}
25
26impl DocumentUtils {
27    pub fn get_document_string_scalar(
28        document: &bson::Document,
29        field_name: &str,
30        is_list: bool,
31    ) -> Result<DocumentValue, async_graphql::Error> {
32        debug!("Getting Document String Scalar: {}", field_name);
33
34        if document.get(field_name).is_none() {
35            return Ok(DocumentValue::None);
36        }
37
38        if document.get(field_name).unwrap().as_null().is_some() {
39            return Ok(DocumentValue::Null);
40        }
41
42        if is_list {
43            if let Some(Bson::Array(documents)) = document.get(field_name) {
44                let valid_strings = documents.iter().all(|value| value.as_str().is_some());
45
46                if !valid_strings {
47                    error!("Not all values are strings for field {}", field_name);
48                    return Err(async_graphql::Error::new(format!(
49                        "Not all values are strings for field {}",
50                        field_name
51                    )));
52                }
53
54                let values = documents
55                    .into_iter()
56                    .map(|value| value.as_str().unwrap().to_string())
57                    .collect::<Vec<String>>();
58                trace!("Document Value String Array: {:?}", values);
59                return Ok(DocumentValue::StringArray(values));
60            } else {
61                trace!("Document Value String Array: Empty Vec");
62                return Ok(DocumentValue::StringArray(vec![]));
63            }
64        }
65
66        let value = document.get_str(field_name).map_err(|err| {
67            error!("Value is not a string: {}", err);
68            async_graphql::Error::new(format!("Value is not a string: {}", err))
69        })?;
70
71        trace!("Found String Value: {:?}", value);
72        Ok(DocumentValue::String(value.to_string()))
73    }
74
75    pub fn get_document_int_scalar(
76        document: &bson::Document,
77        field_name: &str,
78        is_list: bool,
79    ) -> Result<DocumentValue, async_graphql::Error> {
80        debug!("Getting Document Int Scalar: {}", field_name);
81
82        if document.get(field_name).is_none() {
83            return Ok(DocumentValue::None);
84        }
85
86        if document.get(field_name).unwrap().as_null().is_some() {
87            return Ok(DocumentValue::Null);
88        }
89
90        if is_list {
91            if let Some(Bson::Array(documents)) = document.get(field_name) {
92                // Check that all values are i32 or i64
93                let valid = documents.iter().all(|value| {
94                    let i32_value = value.as_i32();
95                    if i32_value.is_none() {
96                        let i64_value = value.as_f64();
97                        if i64_value.is_some() {
98                            return true;
99                        } else {
100                            return false;
101                        }
102                    }
103                    return true;
104                });
105
106                if !valid {
107                    error!("Not all values are ints for field {}", field_name);
108                    return Err(async_graphql::Error::new(format!(
109                        "Not all values are ints for field {}",
110                        field_name
111                    )));
112                }
113
114                let values = documents
115                    .into_iter()
116                    .map(|value| {
117                        let i32_value = value.as_i32();
118                        if i32_value.is_none() {
119                            let i64_value = value.as_f64();
120                            if i64_value.is_some() {
121                                return i64_value.unwrap() as i32;
122                            } else {
123                                // Alrady checked above.
124                                error!("Could not parse int value: {:?}", value);
125                                return -1;
126                            }
127                        }
128                        return i32_value.unwrap();
129                    })
130                    .collect::<Vec<i32>>();
131                trace!("Document Value Int Array: {:?}", values);
132                return Ok(DocumentValue::IntArray(values));
133            } else {
134                trace!("Document Value Int Array: Empty Vec");
135                return Ok(DocumentValue::IntArray(vec![]));
136            }
137        }
138
139        let value = document.get(field_name).unwrap();
140        let i32_value = value.as_i32();
141        if i32_value.is_none() {
142            let i64_value = value.as_i64();
143            if i64_value.is_some() {
144                return Ok(DocumentValue::Int(i64_value.unwrap() as i32));
145            } else {
146                error!("Could not parse int value: {:?}", value);
147                return Err(async_graphql::Error::new(format!(
148                    "Could not parse int value: {:?}",
149                    value
150                )));
151            }
152        }
153        trace!("Found Int Value: {:?}", value);
154        Ok(DocumentValue::Int(i32_value.unwrap()))
155    }
156
157    pub fn get_document_boolean_scalar(
158        document: &bson::Document,
159        field_name: &str,
160        is_list: bool,
161    ) -> Result<DocumentValue, async_graphql::Error> {
162        debug!("Getting Document Boolean Scalar: {}", field_name);
163
164        if document.get(field_name).is_none() {
165            return Ok(DocumentValue::None);
166        }
167
168        if document.get(field_name).unwrap().as_null().is_some() {
169            return Ok(DocumentValue::Null);
170        }
171
172        if is_list {
173            let valid_bools = document
174                .get_array(field_name)?
175                .into_iter()
176                .all(|value| value.as_bool().is_some());
177
178            if !valid_bools {
179                error!("Not all values are booleans for field {}", field_name);
180                return Err(async_graphql::Error::new(format!(
181                    "Not all values are booleans for field {}",
182                    field_name
183                )));
184            }
185
186            let values = document
187                .get_array(field_name)?
188                .into_iter()
189                .map(|value| value.as_bool().unwrap())
190                .collect::<Vec<bool>>();
191            trace!("Document Value Boolean Array: {:?}", values);
192            return Ok(DocumentValue::BooleanArray(values));
193        }
194
195        let value = document.get_bool(field_name).map_err(|err| {
196            error!("Value is not a boolean: {}", err);
197            async_graphql::Error::new(format!("Value is not a boolean: {}", err))
198        })?;
199        trace!("Found Boolean Value: {:?}", value);
200        Ok(DocumentValue::Boolean(value))
201    }
202
203    pub fn get_document_uuid_scalar(
204        document: &bson::Document,
205        field_name: &str,
206        is_list: bool,
207    ) -> Result<DocumentValue, async_graphql::Error> {
208        debug!("Getting Document UUID Scalar: {}", field_name);
209
210        if document.get(field_name).is_none() {
211            return Ok(DocumentValue::None);
212        }
213
214        if document.get(field_name).unwrap().as_null().is_some() {
215            return Ok(DocumentValue::Null);
216        }
217
218        if is_list {
219            if let Some(Bson::Array(documents)) = document.get(field_name) {
220                let valid_uuids = documents.iter().all(|value| {
221                    let value = value.as_str().unwrap_or("");
222                    let uuid = uuid::Uuid::parse_str(value);
223                    if uuid.is_err() {
224                        return false;
225                    } else {
226                        return true;
227                    }
228                });
229
230                if !valid_uuids {
231                    error!("Not all values are uuids for field {}", field_name);
232                    return Err(async_graphql::Error::new(format!(
233                        "Not all values are uuids for field {}",
234                        field_name
235                    )));
236                }
237
238                let values = documents
239                    .into_iter()
240                    .map(|value| {
241                        let value = value.as_str().unwrap_or("");
242                        let uuid = uuid::Uuid::parse_str(value);
243                        if uuid.is_err() {
244                            uuid::Uuid::nil()
245                        } else {
246                            uuid.unwrap()
247                        }
248                    })
249                    .collect();
250                trace!("Document Value UUID Array: {:?}", values);
251                return Ok(DocumentValue::UUIDArray(values));
252            } else {
253                trace!("Document Value UUID Array: Empty Vec");
254                return Ok(DocumentValue::UUIDArray(vec![]));
255            }
256        }
257
258        let value = document.get_str(field_name).map_err(|err| {
259            error!("Value is not a uuid: {}", err);
260            async_graphql::Error::new(format!("Value is not a uuid: {}", err))
261        })?;
262
263        trace!("Document Value UUID: {:?}", value);
264        Ok(DocumentValue::UUID(uuid::Uuid::parse_str(value).unwrap()))
265    }
266
267    pub fn get_document_datetime_scalar(
268        document: &bson::Document,
269        field_name: &str,
270        is_list: bool,
271    ) -> Result<DocumentValue, async_graphql::Error> {
272        debug!("Getting Document DateTime Scalar: {}", field_name);
273
274        if document.get(field_name).is_none() {
275            return Ok(DocumentValue::None);
276        }
277
278        if document.get(field_name).unwrap().as_null().is_some() {
279            return Ok(DocumentValue::Null);
280        }
281
282        if is_list {
283            if let Some(Bson::Array(documents)) = document.get(field_name) {
284                // Check all values are valid dates
285                let is_valid = documents.iter().all(|value| {
286                    let value = value.as_datetime();
287                    if value.is_none() {
288                        return false;
289                    }
290                    true
291                });
292                if !is_valid {
293                    error!("Not all values are valid dates for field {}", field_name);
294                    return Err(async_graphql::Error::new("Invalid DateTime"));
295                }
296                let values = documents
297                    .into_iter()
298                    .map(|value| {
299                        let value = value.as_datetime().unwrap();
300                        value.to_chrono()
301                    })
302                    .collect();
303                trace!("Document Value DateTime Array: {:?}", values);
304                return Ok(DocumentValue::DateTimeArray(values));
305            } else {
306                trace!("Document Value DateTime Array: Empty Vec");
307                return Ok(DocumentValue::DateTimeArray(vec![]));
308            }
309        }
310
311        let value = document.get_datetime(field_name).map_err(|err| {
312            error!("Value is not a datetime: {}", err);
313            async_graphql::Error::new(format!("Value is not a datetime: {}", err))
314        })?;
315        // convert bson datetime to chrono datetime
316        trace!("Document Value DateTime: {:?}", value);
317        Ok(DocumentValue::DateTime(value.to_chrono()))
318    }
319
320    pub fn get_document_object_id_scalar(
321        document: &bson::Document,
322        field_name: &str,
323        is_list: bool,
324    ) -> Result<DocumentValue, async_graphql::Error> {
325        debug!("Getting Document ObjectID Scalar: {}", field_name);
326
327        if document.get(field_name).is_none() {
328            return Ok(DocumentValue::None);
329        }
330
331        if document.get(field_name).unwrap().as_null().is_some() {
332            return Ok(DocumentValue::Null);
333        }
334
335        if is_list {
336            if let Some(Bson::Array(documents)) = document.get(field_name) {
337                let valid_object_ids = documents.iter().all(|value| {
338                    let value = value.as_object_id();
339                    if value.is_none() {
340                        return false;
341                    }
342                    true
343                });
344
345                if !valid_object_ids {
346                    error!("Not all values are object ids for field {}", field_name);
347                    return Err(async_graphql::Error::new(format!(
348                        "Not all values are object ids for field {}",
349                        field_name
350                    )));
351                }
352
353                let value = documents
354                    .into_iter()
355                    .map(|value| value.as_object_id().unwrap())
356                    .collect::<Vec<ObjectId>>();
357                trace!("Document Value ObjectID Array: {:?}", value);
358                return Ok(DocumentValue::ObjectIDArray(value));
359            } else {
360                trace!("Document Value ObjectID Array: Empty Vec");
361                return Ok(DocumentValue::ObjectIDArray(vec![]));
362            }
363        }
364        let value = document.get_object_id(field_name).map_err(|err| {
365            error!("Value is not an object id: {}", err);
366            async_graphql::Error::new(format!("Value is not an object id: {}", err))
367        })?;
368        trace!("Document Value ObjectID: {:?}", value);
369        Ok(DocumentValue::ObjectID(value))
370    }
371
372    pub fn get_document_object_scalar(
373        document: &bson::Document,
374        field_name: &str,
375        is_list: bool,
376    ) -> Result<DocumentValue, async_graphql::Error> {
377        debug!("Get Document Object Scalar");
378        trace!("Field Name: {}", field_name);
379
380        if document.get(field_name).is_none() {
381            return Ok(DocumentValue::None);
382        }
383
384        if document.get(field_name).unwrap().as_null().is_some() {
385            return Ok(DocumentValue::Null);
386        }
387
388        let value = document.get(field_name).unwrap();
389
390        if is_list {
391            if let Some(bson_array) = value.as_array() {
392                let valid_docs = bson_array.iter().all(|value| {
393                    let value = value.as_document();
394                    if value.is_none() {
395                        return false;
396                    }
397                    true
398                });
399
400                if !valid_docs {
401                    error!("Not all values are documents for field {}", field_name);
402                    return Err(async_graphql::Error::new(format!(
403                        "Not all values are documents for field {}",
404                        field_name
405                    )));
406                }
407
408                let values = bson_array
409                    .into_iter()
410                    .map(|value| value.as_document().unwrap().clone())
411                    .collect::<Vec<bson::Document>>();
412                trace!("Document Value Object Array: {:?}", values);
413                return Ok(DocumentValue::DocumentArray(values));
414            } else {
415                trace!("Document Value Object Array: Empty Vec");
416                return Ok(DocumentValue::DocumentArray(vec![]));
417            }
418        } else {
419            trace!("Document Value Object: {:?}", value);
420            Ok(DocumentValue::Document(
421                value.as_document().unwrap().clone(),
422            ))
423        }
424    }
425
426    pub fn get_document_enum_scalar(
427        document: &bson::Document,
428        field_name: &str,
429        is_list: bool,
430    ) -> Result<DocumentValue, async_graphql::Error> {
431        debug!("Resolving Enum Scalar");
432
433        if document.get(field_name).is_none() {
434            trace!(
435                "Field `{}` not found, returning DocumentValue::None",
436                field_name
437            );
438            return Ok(DocumentValue::None);
439        }
440
441        if document.get(field_name).unwrap().as_null().is_some() {
442            trace!(
443                "Field `{}` is null, returning DocumentValue::Null",
444                field_name
445            );
446            return Ok(DocumentValue::Null);
447        }
448
449        if is_list {
450            if let Some(Bson::Array(documents)) = document.get(field_name) {
451                let valid_strings = documents.iter().all(|value| value.as_str().is_some());
452
453                if !valid_strings {
454                    error!("Not all values are strings for field {}", field_name);
455                    return Err(async_graphql::Error::new(format!(
456                        "Not all values are strings for field {}",
457                        field_name
458                    )));
459                }
460
461                let values = documents
462                    .into_iter()
463                    .map(|value| value.as_str().unwrap().to_string())
464                    .collect::<Vec<String>>();
465                trace!("Document Value String Array: {:?}", values);
466                return Ok(DocumentValue::StringArray(values));
467            } else {
468                trace!("Document Value String Array: Empty Vec");
469                return Ok(DocumentValue::StringArray(vec![]));
470            }
471        }
472
473        let value = document.get_str(field_name).map_err(|err| {
474            error!("Value is not a string: {}", err);
475            async_graphql::Error::new(format!("Value is not a string: {}", err))
476        })?;
477
478        trace!("Found String Value: {:?}", value);
479        Ok(DocumentValue::String(value.to_string()))
480    }
481}