1use crate::{
2 handle::graphql::ResponseGenerationConfig,
3 latency::{LatencyConfig, LatencyGenerator},
4};
5use anyhow::Error;
6use hyper::{
7 HeaderMap,
8 header::{HeaderName, HeaderValue},
9};
10use serde::{Deserialize, Serialize};
11use serde_json_bytes::serde_json;
12use serde_yaml::Value;
13use std::collections::HashMap;
14use tracing::{info, warn};
15
16const SUBGRAPH_OVERRIDES_KEY: &str = "subgraph_overrides";
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21struct BaseConfig {
22 #[serde(default = "default_port")]
23 pub port: u16,
24 #[serde(default)]
25 pub headers: HashMap<String, String>,
26 #[serde(default)]
27 pub latency: LatencyConfig,
28 #[serde(default)]
29 pub response_generation: ResponseGenerationConfig,
30 #[serde(default = "default_cache_responses")]
31 pub cache_responses: bool,
32}
33
34pub fn default_port() -> u16 {
35 8080
36}
37
38fn default_cache_responses() -> bool {
39 true
40}
41
42impl Default for BaseConfig {
43 fn default() -> Self {
44 Self {
45 port: default_port(),
46 headers: Default::default(),
47 latency: Default::default(),
48 response_generation: Default::default(),
49 cache_responses: default_cache_responses(),
50 }
51 }
52}
53
54impl BaseConfig {
55 pub fn into_parts(
56 self,
57 ) -> anyhow::Result<(
58 u16,
59 bool,
60 LatencyGenerator,
61 HeaderMap<HeaderValue>,
62 ResponseGenerationConfig,
63 )> {
64 info!(config=%serde_json::to_string(&self.latency).unwrap(), "latency generation");
65 let latency_generator = LatencyGenerator::new(self.latency);
66
67 info!(headers=%serde_json::to_string(&self.headers).unwrap(), "additional headers");
68 let additional_headers: anyhow::Result<HeaderMap<HeaderValue>> = self
69 .headers
70 .into_iter()
71 .map(|(k, v)| Ok((HeaderName::try_from(&k)?, HeaderValue::try_from(&v)?)))
72 .collect();
73
74 let mut response_generation = self.response_generation;
75 response_generation.merge_default_scalars();
76
77 info!(config=%serde_json::to_string(&response_generation).unwrap(), "response generation");
78
79 Ok((
80 self.port,
81 self.cache_responses,
82 latency_generator,
83 additional_headers?,
84 response_generation,
85 ))
86 }
87}
88
89#[derive(Debug, Clone)]
90pub struct Config {
91 pub headers: HeaderMap<HeaderValue>,
92 pub latency_generator: LatencyGenerator,
93 pub response_generation: ResponseGenerationConfig,
94 pub cache_responses: bool,
95 pub subgraph_overrides: SubgraphOverrides,
96}
97
98#[derive(Debug, Clone, Default)]
99pub struct SubgraphOverrides {
100 pub headers: HashMap<String, HeaderMap<HeaderValue>>,
101 pub latency_generator: HashMap<String, LatencyGenerator>,
102 pub response_generation: HashMap<String, ResponseGenerationConfig>,
103 pub cache_responses: HashMap<String, bool>,
104}
105
106impl Default for Config {
107 fn default() -> Self {
108 Self {
109 headers: Default::default(),
110 latency_generator: LatencyGenerator::new(LatencyConfig::default()),
111 response_generation: Default::default(),
112 cache_responses: default_cache_responses(),
113 subgraph_overrides: Default::default(),
114 }
115 }
116}
117
118impl Config {
119 pub fn parse_yaml(mut base: Value) -> anyhow::Result<(u16, Config)> {
121 let mapping = base
122 .as_mapping_mut()
123 .ok_or_else(|| Error::msg("config file must be a mapping"))?;
124
125 let mut subgraph_cache_responses = HashMap::new();
126 let mut subgraph_headers = HashMap::new();
127 let mut subgraph_latency_generators = HashMap::new();
128 let mut subgraph_response_generation_configs = HashMap::new();
129
130 if let Some(overrides) = mapping.remove(SUBGRAPH_OVERRIDES_KEY) {
131 match overrides {
132 Value::Mapping(mapping) => {
133 for (subgraph_name, subgraph_override) in mapping {
134 let mut subgraph_config = base.clone();
135
136 let override_mapping = subgraph_override
137 .as_mapping()
138 .ok_or_else(|| Error::msg("subgraph override must be a mapping"))?;
139
140 if override_mapping.contains_key("port") {
141 warn!("port overrides for subgraphs will be ignored")
142 }
143
144 merge_yaml(subgraph_override, &mut subgraph_config);
145 let parsed_config: BaseConfig = serde_yaml::from_value(subgraph_config)?;
146 let subgraph_name: String = serde_yaml::from_value(subgraph_name)?;
147
148 info!("generating customized config for {}", subgraph_name);
149 let (
150 _port,
151 cache_responses,
152 latency_generator,
153 headers,
154 response_generation,
155 ) = parsed_config.into_parts()?;
156
157 subgraph_cache_responses.insert(subgraph_name.clone(), cache_responses);
158 subgraph_latency_generators
159 .insert(subgraph_name.clone(), latency_generator);
160 subgraph_headers.insert(subgraph_name.clone(), headers);
161 subgraph_response_generation_configs
162 .insert(subgraph_name, response_generation);
163 }
164 }
165 _ => return Err(Error::msg("config file must be a mapping")),
166 }
167 }
168
169 let (port, cache_responses, latency, headers, response_generation) =
170 serde_yaml::from_value::<BaseConfig>(base)?.into_parts()?;
171
172 Ok((
173 port,
174 Config {
175 headers,
176 latency_generator: latency,
177 response_generation,
178 cache_responses,
179 subgraph_overrides: SubgraphOverrides {
180 headers: subgraph_headers,
181 latency_generator: subgraph_latency_generators,
182 response_generation: subgraph_response_generation_configs,
183 cache_responses: subgraph_cache_responses,
184 },
185 },
186 ))
187 }
188}
189
190fn merge_yaml(overrides: serde_yaml::Value, base: &mut serde_yaml::Value) {
195 use serde_yaml::Value;
196
197 match (overrides, base) {
198 (Value::Mapping(override_map), Value::Mapping(base_map)) => {
200 for (key, override_val) in override_map.into_iter() {
201 match base_map.get_mut(&key) {
204 Some(base_val) => merge_yaml(override_val, base_val),
205 None => _ = base_map.insert(key, override_val),
206 };
207 }
208 }
209
210 (overrides, base) => *base = overrides,
212 }
213}