From 55cdb30a0b295297f05cae7de4337c65fc40fea3 Mon Sep 17 00:00:00 2001 From: Geoffroy Couprie Date: Fri, 28 Jul 2023 10:42:27 +0200 Subject: [PATCH] SubSelectionKey needs a custom serializer it is used as key in a HashMap, and in JSON keys must be serialized as strings --- apollo-router/src/spec/query/subselections.rs | 60 ++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/apollo-router/src/spec/query/subselections.rs b/apollo-router/src/spec/query/subselections.rs index cb5ae374e6f..4bbb16ac458 100644 --- a/apollo-router/src/spec/query/subselections.rs +++ b/apollo-router/src/spec/query/subselections.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use serde::de::Visitor; use serde::Deserialize; use serde::Serialize; use serde_json_bytes::ByteString; @@ -14,12 +15,69 @@ use crate::spec::IncludeSkip; use crate::spec::SpecError; use crate::Configuration; -#[derive(Debug, Serialize, Deserialize, PartialEq, Hash, Eq)] +#[derive(Debug, PartialEq, Hash, Eq)] pub(crate) struct SubSelectionKey { pub(crate) defer_label: Option, pub(crate) defer_conditions: BooleanValues, } +// Do not replace this with a derived Serialize implementation +// SubSelectionKey must serialize to a string because it is used as a JSON object key +impl Serialize for SubSelectionKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let s = format!( + "{:?}|{}", + self.defer_conditions.bits, + self.defer_label.as_deref().unwrap_or("") + ); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for SubSelectionKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(SubSelectionKeyVisitor) + } +} + +struct SubSelectionKeyVisitor; +impl<'de> Visitor<'de> for SubSelectionKeyVisitor { + type Value = SubSelectionKey; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter + .write_str("a string containing the defer label and defer conditions separated by |") + } + + fn visit_str(self, s: &str) -> Result + where + E: serde::de::Error, + { + if let Some((bits_str, label)) = s.split_once('|') { + Ok(SubSelectionKey { + defer_conditions: BooleanValues { + bits: bits_str + .parse::() + .map_err(|_| E::custom("expected a number"))?, + }, + defer_label: if label.is_empty() { + None + } else { + Some(label.to_string()) + }, + }) + } else { + Err(E::custom("invalid subselection")) + } + } +} + #[derive(Debug, Serialize, Deserialize)] pub(crate) struct SubSelectionValue { pub(crate) selection_set: Vec,