async-graphql/src/types/json.rs

182 lines
4.5 KiB
Rust
Raw Normal View History

2020-10-15 06:38:10 +00:00
use std::borrow::Cow;
use std::ops::{Deref, DerefMut};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
2020-09-06 06:16:36 +00:00
use crate::parser::types::Field;
2020-07-08 06:44:57 +00:00
use crate::registry::{MetaType, Registry};
use crate::{
from_value, to_value, ContextSelectionSet, InputValueResult, OutputType, Positioned, Scalar,
ScalarType, ServerResult, Type, Value,
2020-07-08 06:44:57 +00:00
};
2020-05-07 12:41:00 +00:00
/// A scalar that can represent any JSON value.
///
/// If the inner type cannot be serialized as JSON (e.g. it has non-string keys) it will be `null`.
2020-06-02 13:00:54 +00:00
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash, Default)]
#[serde(transparent)]
2020-05-07 12:41:00 +00:00
pub struct Json<T>(pub T);
impl<T> Deref for Json<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Json<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: DeserializeOwned + Serialize> From<T> for Json<T> {
fn from(value: T) -> Self {
2020-07-08 06:44:57 +00:00
Self(value)
}
}
/// A scalar that can represent any JSON value.
#[Scalar(internal, name = "JSON")]
2020-05-07 12:41:00 +00:00
impl<T: DeserializeOwned + Serialize + Send + Sync> ScalarType for Json<T> {
fn parse(value: Value) -> InputValueResult<Self> {
Ok(from_value(value)?)
2020-05-07 12:41:00 +00:00
}
fn to_value(&self) -> Value {
2020-10-16 10:37:59 +00:00
to_value(&self.0).unwrap_or_default()
2020-05-07 12:41:00 +00:00
}
}
/// A `Json` type that only implements `OutputType`.
2020-07-08 06:44:57 +00:00
#[derive(Serialize, Clone, Debug, Eq, PartialEq, Hash, Default)]
pub struct OutputJson<T>(pub T);
impl<T> Deref for OutputJson<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for OutputJson<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: Serialize> From<T> for OutputJson<T> {
fn from(value: T) -> Self {
2020-05-11 15:55:54 +00:00
Self(value)
}
}
2020-07-08 06:44:57 +00:00
impl<T> Type for OutputJson<T> {
fn type_name() -> Cow<'static, str> {
Cow::Borrowed("Json")
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_type::<OutputJson<T>, _>(|_| MetaType::Scalar {
name: Self::type_name().to_string(),
description: None,
is_valid: |_| true,
})
}
}
#[async_trait::async_trait]
impl<T: Serialize + Send + Sync> OutputType for OutputJson<T> {
2020-07-08 06:44:57 +00:00
async fn resolve(
&self,
_ctx: &ContextSelectionSet<'_>,
_field: &Positioned<Field>,
2020-10-10 02:32:43 +00:00
) -> ServerResult<Value> {
2020-10-16 10:37:59 +00:00
Ok(to_value(&self.0).ok().unwrap_or_default())
2020-07-08 06:44:57 +00:00
}
}
2020-05-07 12:41:00 +00:00
#[cfg(test)]
mod test {
use crate::*;
use serde::{Deserialize, Serialize};
2020-05-07 12:41:00 +00:00
use std::collections::HashMap;
#[async_std::test]
async fn test_json_type() {
#[derive(Serialize, Deserialize)]
struct MyStruct {
a: i32,
b: i32,
c: HashMap<String, i32>,
}
struct Query;
#[Object(internal)]
2020-05-07 12:41:00 +00:00
impl Query {
async fn obj(&self, input: Json<MyStruct>) -> Json<MyStruct> {
input
}
}
let query = r#"{ obj(input: { a: 1, b: 2, c: { a: 11, b: 22 } } ) }"#;
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(query).await.into_result().unwrap().data,
value!({
2020-05-07 12:41:00 +00:00
"obj": {
"a": 1,
"b": 2,
"c": { "a": 11, "b": 22 }
}
})
);
}
#[async_std::test]
async fn test_output_json_type() {
#[derive(Serialize)]
struct MyStruct {
a: i32,
b: i32,
c: HashMap<String, i32>,
}
struct Query;
#[Object(internal)]
impl Query {
async fn obj(&self) -> OutputJson<MyStruct> {
MyStruct {
a: 1,
b: 2,
c: {
let mut values = HashMap::new();
values.insert("a".to_string(), 11);
values.insert("b".to_string(), 22);
values
},
}
.into()
}
}
let query = r#"{ obj }"#;
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!(
schema.execute(query).await.into_result().unwrap().data,
value!({
"obj": {
"a": 1,
"b": 2,
"c": { "a": 11, "b": 22 }
}
})
);
}
2020-05-07 12:41:00 +00:00
}