Skip to main content

apollo_federation/connectors/runtime/
mapping.rs

1//! Mapping from a Connectors request or response to GraphQL
2
3use std::collections::HashMap;
4
5use itertools::Itertools;
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::connectors::ApplyToError;
10use crate::connectors::ProblemLocation;
11
12/// A mapping problem
13#[derive(Clone, Debug, Serialize, Deserialize)]
14pub struct Problem {
15    pub message: String,
16    pub path: String,
17    pub count: usize,
18    pub location: ProblemLocation,
19}
20
21/// Aggregate a list of [`ApplyToError`] into [mapping problems](Problem)
22pub fn aggregate_apply_to_errors(
23    errors: Vec<ApplyToError>,
24    location: ProblemLocation,
25) -> impl Iterator<Item = Problem> {
26    errors
27        .into_iter()
28        .fold(
29            HashMap::default(),
30            |mut acc: HashMap<(String, String), usize>, err| {
31                let path = err
32                    .path()
33                    .iter()
34                    .map(|p| match p.as_u64() {
35                        Some(_) => "@", // ignore array indices for grouping
36                        None => p.as_str().unwrap_or_default(),
37                    })
38                    .join(".");
39
40                acc.entry((err.message().to_string(), path))
41                    .and_modify(|c| *c += 1)
42                    .or_insert(1);
43                acc
44            },
45        )
46        .into_iter()
47        .map(move |((message, path), count)| Problem {
48            message,
49            path,
50            count,
51            location,
52        })
53}
54
55/// Aggregate a list of [`ApplyToError`] into [mapping problems](Problem) while preserving [`ProblemLocation`]
56pub fn aggregate_apply_to_errors_with_problem_locations(
57    errors: Vec<(ProblemLocation, ApplyToError)>,
58) -> impl Iterator<Item = Problem> {
59    errors
60        .into_iter()
61        .fold(
62            HashMap::new(),
63            |mut acc: HashMap<ProblemLocation, Vec<ApplyToError>>, (loc, err)| {
64                acc.entry(loc).or_default().push(err);
65                acc
66            },
67        )
68        .into_iter()
69        .flat_map(|(location, errors)| aggregate_apply_to_errors(errors, location))
70}