async_graphql/types/
json.rs

1use std::{
2    borrow::Cow,
3    ops::{Deref, DerefMut},
4};
5
6use serde::{Deserialize, Serialize, de::DeserializeOwned};
7
8use crate::{
9    ContextSelectionSet, InputType, InputValueResult, OutputType, Positioned, ServerResult, Value,
10    from_value,
11    parser::types::Field,
12    registry::{MetaType, MetaTypeId, Registry},
13    to_value,
14};
15
16/// A scalar that can represent any JSON value.
17///
18/// If the inner type cannot be serialized as JSON (e.g. it has non-string keys)
19/// it will be `null`.
20#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash, Default)]
21#[serde(transparent)]
22pub struct Json<T>(pub T);
23
24impl<T> Deref for Json<T> {
25    type Target = T;
26
27    fn deref(&self) -> &Self::Target {
28        &self.0
29    }
30}
31
32impl<T> DerefMut for Json<T> {
33    fn deref_mut(&mut self) -> &mut Self::Target {
34        &mut self.0
35    }
36}
37
38impl<T> From<T> for Json<T> {
39    fn from(value: T) -> Self {
40        Self(value)
41    }
42}
43
44impl<T: DeserializeOwned + Serialize + Send + Sync> InputType for Json<T> {
45    type RawValueType = T;
46
47    fn type_name() -> Cow<'static, str> {
48        Cow::Borrowed("JSON")
49    }
50
51    fn create_type_info(registry: &mut Registry) -> String {
52        registry.create_input_type::<Json<T>, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
53            name: <Self as InputType>::type_name().to_string(),
54            description: Some("A scalar that can represent any JSON value.".to_string()),
55            is_valid: None,
56            visible: None,
57            inaccessible: false,
58            tags: Default::default(),
59            specified_by_url: None,
60            directive_invocations: Default::default(),
61            requires_scopes: Default::default(),
62        })
63    }
64
65    fn parse(value: Option<Value>) -> InputValueResult<Self> {
66        Ok(from_value(value.unwrap_or_default())?)
67    }
68
69    fn to_value(&self) -> Value {
70        Value::String(serde_json::to_string(&self.0).unwrap_or_default())
71    }
72
73    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
74        Some(&self.0)
75    }
76}
77
78#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
79impl<T: Serialize + Send + Sync> OutputType for Json<T> {
80    fn type_name() -> Cow<'static, str> {
81        Cow::Borrowed("JSON")
82    }
83
84    fn create_type_info(registry: &mut Registry) -> String {
85        registry.create_output_type::<Json<T>, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
86            name: <Self as OutputType>::type_name().to_string(),
87            description: Some("A scalar that can represent any JSON value.".to_string()),
88            is_valid: None,
89            visible: None,
90            inaccessible: false,
91            tags: Default::default(),
92            specified_by_url: None,
93            directive_invocations: Default::default(),
94            requires_scopes: Default::default(),
95        })
96    }
97
98    async fn resolve(
99        &self,
100        _ctx: &ContextSelectionSet<'_>,
101        _field: &Positioned<Field>,
102    ) -> ServerResult<Value> {
103        Ok(to_value(&self.0).ok().unwrap_or_default())
104    }
105}
106
107impl InputType for serde_json::Value {
108    type RawValueType = serde_json::Value;
109
110    fn type_name() -> Cow<'static, str> {
111        Cow::Borrowed("JSON")
112    }
113
114    fn create_type_info(registry: &mut Registry) -> String {
115        registry.create_input_type::<serde_json::Value, _>(MetaTypeId::Scalar, |_| {
116            MetaType::Scalar {
117                name: <Self as InputType>::type_name().to_string(),
118                description: Some("A scalar that can represent any JSON value.".to_string()),
119                is_valid: None,
120                visible: None,
121                inaccessible: false,
122                tags: Default::default(),
123                specified_by_url: None,
124                directive_invocations: Default::default(),
125                requires_scopes: Default::default(),
126            }
127        })
128    }
129
130    fn parse(value: Option<Value>) -> InputValueResult<Self> {
131        Ok(from_value(value.unwrap_or_default())?)
132    }
133
134    fn to_value(&self) -> Value {
135        Value::String(self.to_string())
136    }
137
138    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
139        Some(&self)
140    }
141}
142
143#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
144impl OutputType for serde_json::Value {
145    fn type_name() -> Cow<'static, str> {
146        Cow::Borrowed("JSON")
147    }
148
149    fn create_type_info(registry: &mut Registry) -> String {
150        registry.create_output_type::<serde_json::Value, _>(MetaTypeId::Scalar, |_| {
151            MetaType::Scalar {
152                name: <Self as OutputType>::type_name().to_string(),
153                description: Some("A scalar that can represent any JSON value.".to_string()),
154                is_valid: None,
155                visible: None,
156                inaccessible: false,
157                tags: Default::default(),
158                specified_by_url: None,
159                directive_invocations: Default::default(),
160                requires_scopes: Default::default(),
161            }
162        })
163    }
164
165    async fn resolve(
166        &self,
167        _ctx: &ContextSelectionSet<'_>,
168        _field: &Positioned<Field>,
169    ) -> ServerResult<Value> {
170        Ok(to_value(self).ok().unwrap_or_default())
171    }
172}
173
174#[cfg(test)]
175mod test {
176    use std::collections::HashMap;
177
178    use serde::{Deserialize, Serialize};
179
180    use crate::*;
181
182    #[tokio::test]
183    async fn test_json_type() {
184        #[derive(Serialize, Deserialize)]
185        struct MyStruct {
186            a: i32,
187            b: i32,
188            c: HashMap<String, i32>,
189        }
190
191        struct Query;
192
193        #[Object(internal)]
194        impl Query {
195            async fn obj(&self, input: Json<MyStruct>) -> Json<MyStruct> {
196                input
197            }
198        }
199
200        let query = r#"{ obj(input: { a: 1, b: 2, c: { a: 11, b: 22 } } ) }"#;
201        let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
202        assert_eq!(
203            schema.execute(query).await.into_result().unwrap().data,
204            value!({
205             "obj": {
206                 "a": 1,
207                 "b": 2,
208                 "c": { "a": 11, "b": 22 }
209             }
210            })
211        );
212    }
213
214    #[tokio::test]
215    async fn test_json_type_for_serialize_only() {
216        #[derive(Serialize)]
217        struct MyStruct {
218            a: i32,
219            b: i32,
220            c: HashMap<String, i32>,
221        }
222
223        struct Query;
224
225        #[Object(internal)]
226        impl Query {
227            async fn obj(&self) -> Json<MyStruct> {
228                MyStruct {
229                    a: 1,
230                    b: 2,
231                    c: {
232                        let mut values = HashMap::new();
233                        values.insert("a".to_string(), 11);
234                        values.insert("b".to_string(), 22);
235                        values
236                    },
237                }
238                .into()
239            }
240        }
241
242        let query = r#"{ obj }"#;
243        let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
244        assert_eq!(
245            schema.execute(query).await.into_result().unwrap().data,
246            value!({
247             "obj": {
248                 "a": 1,
249                 "b": 2,
250                 "c": { "a": 11, "b": 22 }
251             }
252            })
253        );
254    }
255}