Specified By - [GraphQL - October 2021] #677

This commit is contained in:
Sunli 2021-10-28 15:21:42 +08:00
parent 7a4b0c0aa3
commit 91bb9e81fb
10 changed files with 74 additions and 8 deletions

View File

@ -446,6 +446,7 @@ pub struct Scalar {
pub name: Option<String>, pub name: Option<String>,
pub use_type_description: bool, pub use_type_description: bool,
pub visible: Option<Visible>, pub visible: Option<Visible>,
pub specified_by_url: Option<String>,
} }
#[derive(FromMeta, Default)] #[derive(FromMeta, Default)]
@ -652,6 +653,8 @@ pub struct NewType {
pub name: NewTypeName, pub name: NewTypeName,
#[darling(default)] #[darling(default)]
pub visible: Option<Visible>, pub visible: Option<Visible>,
#[darling(default)]
pub specified_by_url: Option<String>,
} }
#[derive(FromMeta, Default)] #[derive(FromMeta, Default)]

View File

@ -38,12 +38,18 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
None => quote! { <#inner_ty as #crate_name::Type>::type_name() }, None => quote! { <#inner_ty as #crate_name::Type>::type_name() },
}; };
let create_type_info = if let Some(name) = &gql_typename { let create_type_info = if let Some(name) = &gql_typename {
let specified_by_url = match &newtype_args.specified_by_url {
Some(specified_by_url) => quote! { ::std::option::Option::Some(#specified_by_url) },
None => quote! { ::std::option::Option::None },
};
quote! { quote! {
registry.create_type::<#ident, _>(|_| #crate_name::registry::MetaType::Scalar { registry.create_type::<#ident, _>(|_| #crate_name::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned(#name), name: ::std::borrow::ToOwned::to_owned(#name),
description: #desc, description: #desc,
is_valid: |value| <#ident as #crate_name::ScalarType>::is_valid(value), is_valid: |value| <#ident as #crate_name::ScalarType>::is_valid(value),
visible: #visible, visible: #visible,
specified_by_url: #specified_by_url,
}) })
} }
} else { } else {

View File

@ -30,6 +30,11 @@ pub fn generate(
let generic = &item_impl.generics; let generic = &item_impl.generics;
let where_clause = &item_impl.generics.where_clause; let where_clause = &item_impl.generics.where_clause;
let visible = visible_fn(&scalar_args.visible); let visible = visible_fn(&scalar_args.visible);
let specified_by_url = match &scalar_args.specified_by_url {
Some(specified_by_url) => quote! { ::std::option::Option::Some(#specified_by_url) },
None => quote! { ::std::option::Option::None },
};
let expanded = quote! { let expanded = quote! {
#item_impl #item_impl
@ -45,6 +50,7 @@ pub fn generate(
description: #desc, description: #desc,
is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value), is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value),
visible: #visible, visible: #visible,
specified_by_url: #specified_by_url,
}) })
} }
} }

View File

@ -987,6 +987,7 @@ pub use async_graphql_derive::Subscription;
/// | Attribute | description | Type | Optional | /// | Attribute | description | Type | Optional |
/// |-------------|---------------------------|----------|----------| /// |-------------|---------------------------|----------|----------|
/// | name | Scalar name | string | Y | /// | name | Scalar name | string | Y |
/// | specified_by_url | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y |
/// ///
pub use async_graphql_derive::Scalar; pub use async_graphql_derive::Scalar;
@ -1000,8 +1001,9 @@ pub use async_graphql_derive::Scalar;
/// |-------------|---------------------------|----------|----------| /// |-------------|---------------------------|----------|----------|
/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y | /// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | string | Y |
/// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y | /// | name | If this attribute is provided then define a new scalar, otherwise it is just a transparent proxy for the internal scalar. | bool | Y |
/// | visible(Only valid for new scalars.) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y | /// | visible(Only valid for new scalars) | If `false`, it will not be displayed in introspection. *[See also the Book](https://async-graphql.github.io/async-graphql/en/visibility.html).* | bool | Y |
/// | visible(Only valid for new scalars.) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | /// | visible(Only valid for new scalars) | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y |
/// | specified_by_url(Only valid for new scalars) | Provide a specification URL for this scalar type, it must link to a human-readable specification of the data format, serialization and coercion rules for this scalar. | string | Y |
/// ///
/// # Examples /// # Examples
/// ///

View File

@ -218,4 +218,16 @@ impl<'a> __Type<'a> {
None None
} }
} }
#[graphql(name = "specifiedByURL")]
async fn specified_by_url(&self) -> Option<&'a str> {
if let TypeDetail::Named(registry::MetaType::Scalar {
specified_by_url, ..
}) = &self.detail
{
*specified_by_url
} else {
None
}
}
} }

View File

@ -189,6 +189,7 @@ pub enum MetaType {
description: Option<&'static str>, description: Option<&'static str>,
is_valid: fn(value: &Value) -> bool, is_valid: fn(value: &Value) -> bool,
visible: Option<MetaVisibleFn>, visible: Option<MetaVisibleFn>,
specified_by_url: Option<&'static str>,
}, },
Object { Object {
name: String, name: String,

View File

@ -68,6 +68,9 @@ pub trait ScalarType: Sized + Send {
/// // Rename to `MV` and add description. /// // Rename to `MV` and add description.
/// // scalar!(MyValue, "MV", "This is my value"); /// // scalar!(MyValue, "MV", "This is my value");
/// ///
/// // Rename to `MV`, add description and specifiedByURL.
/// // scalar!(MyValue, "MV", "This is my value", "https://tools.ietf.org/html/rfc4122");
///
/// struct Query; /// struct Query;
/// ///
/// #[Object] /// #[Object]
@ -92,23 +95,47 @@ pub trait ScalarType: Sized + Send {
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! scalar { macro_rules! scalar {
($ty:ty, $name:literal, $desc:literal, $specified_by_url:literal) => {
$crate::scalar_internal!(
$ty,
$name,
::std::option::Option::Some($desc),
::std::option::Option::Some($specified_by_url)
);
};
($ty:ty, $name:literal, $desc:literal) => { ($ty:ty, $name:literal, $desc:literal) => {
$crate::scalar_internal!($ty, $name, ::std::option::Option::Some($desc)); $crate::scalar_internal!(
$ty,
$name,
::std::option::Option::Some($desc),
::std::option::Option::None
);
}; };
($ty:ty, $name:literal) => { ($ty:ty, $name:literal) => {
$crate::scalar_internal!($ty, $name, ::std::option::Option::None); $crate::scalar_internal!(
$ty,
$name,
::std::option::Option::None,
::std::option::Option::None
);
}; };
($ty:ty) => { ($ty:ty) => {
$crate::scalar_internal!($ty, ::std::stringify!($ty), ::std::option::Option::None); $crate::scalar_internal!(
$ty,
::std::stringify!($ty),
::std::option::Option::None,
::std::option::Option::None
);
}; };
} }
#[macro_export] #[macro_export]
#[doc(hidden)] #[doc(hidden)]
macro_rules! scalar_internal { macro_rules! scalar_internal {
($ty:ty, $name:expr, $desc:expr) => { ($ty:ty, $name:expr, $desc:expr, $specified_by_url:expr) => {
impl $crate::Type for $ty { impl $crate::Type for $ty {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> { fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed($name) ::std::borrow::Cow::Borrowed($name)
@ -122,6 +149,7 @@ macro_rules! scalar_internal {
description: $desc, description: $desc,
is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value), is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value),
visible: ::std::option::Option::None, visible: ::std::option::Option::None,
specified_by_url: $specified_by_url,
}) })
} }
} }

View File

@ -85,6 +85,7 @@ impl<T> Type for OutputJson<T> {
description: None, description: None,
is_valid: |_| true, is_valid: |_| true,
visible: None, visible: None,
specified_by_url: None,
}) })
} }
} }

View File

@ -111,6 +111,7 @@ impl Type for Upload {
description: None, description: None,
is_valid: |value| matches!(value, Value::String(_)), is_valid: |value| matches!(value, Value::String(_)),
visible: None, visible: None,
specified_by_url: None,
}) })
} }
} }

View File

@ -11,7 +11,12 @@ mod test_mod {
#[tokio::test] #[tokio::test]
pub async fn test_scalar_macro() { pub async fn test_scalar_macro() {
scalar!(test_mod::MyValue, "MV", "DESC"); scalar!(
test_mod::MyValue,
"MV",
"DESC",
"https://tools.ietf.org/html/rfc4122"
);
struct Query; struct Query;
@ -26,7 +31,7 @@ pub async fn test_scalar_macro() {
let schema = Schema::new(Query, EmptyMutation, EmptySubscription); let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
assert_eq!( assert_eq!(
schema schema
.execute(r#"{ __type(name:"MV") { name description } }"#) .execute(r#"{ __type(name:"MV") { name description specifiedByURL } }"#)
.await .await
.into_result() .into_result()
.unwrap() .unwrap()
@ -35,6 +40,7 @@ pub async fn test_scalar_macro() {
"__type": { "__type": {
"name": "MV", "name": "MV",
"description": "DESC", "description": "DESC",
"specifiedByURL": "https://tools.ietf.org/html/rfc4122",
} }
}) })
); );