use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[derive(Debug, Clone, Deserialize)]
pub struct SubgraphResponse {
pub data: Option<Value>,
#[serde(default)]
pub errors: Vec<GraphQLError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphQLError {
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<Vec<Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub locations: Option<Vec<Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extensions: Option<Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MergedResponse {
pub data: Value,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub errors: Vec<GraphQLError>,
}
pub fn merge_responses(responses: &[(String, SubgraphResponse)]) -> MergedResponse {
let mut merged_data = Map::new();
let mut merged_errors = Vec::new();
for (subgraph_name, response) in responses {
if let Some(Value::Object(data)) = &response.data {
for (key, value) in data {
merged_data.insert(key.clone(), value.clone());
}
}
for error in &response.errors {
let mut attributed = error.clone();
let ext = attributed.extensions.get_or_insert_with(|| Value::Object(Map::new()));
if let Value::Object(ext_map) = ext {
ext_map.insert("subgraph".to_string(), Value::String(subgraph_name.clone()));
}
merged_errors.push(attributed);
}
}
MergedResponse {
data: Value::Object(merged_data),
errors: merged_errors,
}
}
pub fn merge_entity_fields(target: &mut Value, entities: &[Value]) {
for entity in entities {
if let Value::Object(entity_map) = entity {
deep_merge_object(target, entity_map);
}
}
}
fn deep_merge_object(target: &mut Value, source: &Map<String, Value>) {
if let Value::Object(target_map) = target {
for (key, value) in source {
match (target_map.get_mut(key), value) {
(Some(Value::Object(existing)), Value::Object(incoming)) => {
deep_merge_object(&mut Value::Object(existing.clone()), incoming);
let mut merged = existing.clone();
for (k, v) in incoming {
merged.insert(k.clone(), v.clone());
}
target_map.insert(key.clone(), Value::Object(merged));
},
_ => {
target_map.insert(key.clone(), value.clone());
},
}
}
}
}