From 06973ee09b2e01906975f2c063096232fca577aa Mon Sep 17 00:00:00 2001 From: Nicolas Moutschen Date: Thu, 18 Aug 2022 11:40:04 +0200 Subject: [PATCH 1/4] feat: @inaccessible support --- derive/src/args.rs | 33 +++++ derive/src/complex_object.rs | 4 + derive/src/directive.rs | 1 + derive/src/enum.rs | 4 + derive/src/input_object.rs | 5 + derive/src/interface.rs | 6 + derive/src/merged_object.rs | 2 + derive/src/merged_subscription.rs | 1 + derive/src/newtype.rs | 2 + derive/src/object.rs | 7 + derive/src/oneof_object.rs | 5 + derive/src/scalar.rs | 3 + derive/src/simple_object.rs | 5 + derive/src/subscription.rs | 3 + derive/src/union.rs | 2 + src/registry/export_sdl.rs | 53 ++++++- src/registry/mod.rs | 16 ++ src/resolver_utils/scalar.rs | 2 + src/schema.rs | 2 + src/types/connection/mod.rs | 2 +- src/types/empty_mutation.rs | 1 + src/types/empty_subscription.rs | 1 + src/types/external/json_object/btreemap.rs | 2 + src/types/external/json_object/hashmap.rs | 2 + src/types/json.rs | 4 + src/types/merged_object.rs | 3 + src/types/query_root.rs | 3 + src/types/upload.rs | 1 + tests/federation.rs | 164 +++++++++++++++++++++ 29 files changed, 334 insertions(+), 5 deletions(-) diff --git a/derive/src/args.rs b/derive/src/args.rs index 6a5f8cd0..88c154c2 100644 --- a/derive/src/args.rs +++ b/derive/src/args.rs @@ -153,6 +153,8 @@ pub struct SimpleObjectField { #[darling(default)] pub shareable: bool, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub guard: Option>, #[darling(default)] pub visible: Option, @@ -202,6 +204,8 @@ pub struct SimpleObject { #[darling(default)] pub shareable: bool, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub visible: Option, #[darling(default, multiple, rename = "concrete")] pub concretes: Vec, @@ -226,6 +230,7 @@ pub struct Argument { pub process_with: Option, pub key: bool, // for entity pub visible: Option, + pub inaccessible: bool, pub secret: bool, } @@ -240,6 +245,7 @@ pub struct Object { pub cache_control: CacheControl, pub extends: bool, pub shareable: bool, + pub inaccessible: bool, pub use_type_description: bool, pub visible: Option, pub serial: bool, @@ -284,6 +290,7 @@ pub struct ObjectField { pub provides: Option, pub requires: Option, pub shareable: bool, + pub inaccessible: bool, pub guard: Option>, pub visible: Option, pub complexity: Option, @@ -321,6 +328,8 @@ pub struct Enum { pub remote: Option, #[darling(default)] pub visible: Option, + #[darling(default)] + pub inaccessible: bool, } #[derive(FromVariant)] @@ -336,6 +345,8 @@ pub struct EnumItem { pub deprecation: Deprecation, #[darling(default)] pub visible: Option, + #[darling(default)] + pub inaccessible: bool, } #[derive(FromDeriveInput)] @@ -352,6 +363,8 @@ pub struct Union { pub name: Option, #[darling(default)] pub visible: Option, + #[darling(default)] + pub inaccessible: bool, } #[derive(FromVariant)] @@ -394,6 +407,8 @@ pub struct InputObjectField { #[darling(default)] pub visible: Option, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub secret: bool, } @@ -415,6 +430,8 @@ pub struct InputObject { pub rename_fields: Option, #[darling(default)] pub visible: Option, + #[darling(default)] + pub inaccessible: bool, #[darling(default, multiple, rename = "concrete")] pub concretes: Vec, // for SimpleObject @@ -436,6 +453,8 @@ pub struct OneofObjectField { #[darling(default)] pub visible: Option, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub secret: bool, } @@ -455,6 +474,8 @@ pub struct OneofObject { pub rename_fields: Option, #[darling(default)] pub visible: Option, + #[darling(default)] + pub inaccessible: bool, #[darling(default, multiple, rename = "concrete")] pub concretes: Vec, } @@ -473,6 +494,8 @@ pub struct InterfaceFieldArgument { #[darling(default)] pub visible: Option, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub secret: bool, } @@ -498,6 +521,8 @@ pub struct InterfaceField { #[darling(default)] pub visible: Option, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub shareable: bool, } @@ -529,6 +554,8 @@ pub struct Interface { pub extends: bool, #[darling(default)] pub visible: Option, + #[darling(default)] + pub inaccessible: bool, } #[derive(FromMeta, Default)] @@ -538,6 +565,7 @@ pub struct Scalar { pub name: Option, pub use_type_description: bool, pub visible: Option, + pub inaccessible: bool, pub specified_by_url: Option, } @@ -604,6 +632,8 @@ pub struct MergedObject { #[darling(default)] pub shareable: bool, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub visible: Option, #[darling(default)] pub serial: bool, @@ -751,6 +781,8 @@ pub struct NewType { #[darling(default)] pub visible: Option, #[darling(default)] + pub inaccessible: bool, + #[darling(default)] pub specified_by_url: Option, } @@ -775,6 +807,7 @@ pub struct ComplexObjectField { pub provides: Option, pub requires: Option, pub shareable: bool, + pub inaccessible: bool, pub guard: Option>, pub visible: Option, pub complexity: Option, diff --git a/derive/src/complex_object.rs b/derive/src/complex_object.rs index 24e34cee..0329a2e6 100644 --- a/derive/src/complex_object.rs +++ b/derive/src/complex_object.rs @@ -173,6 +173,7 @@ pub fn generate( let field_deprecation = gen_deprecation(&method_args.deprecation, &crate_name); let external = method_args.external; let shareable = method_args.shareable; + let inaccessible = method_args.inaccessible; let requires = match &method_args.requires { Some(requires) => quote! { ::std::option::Option::Some(#requires) }, None => quote! { ::std::option::Option::None }, @@ -208,6 +209,7 @@ pub fn generate( validator, process_with, visible, + inaccessible, secret, .. }, @@ -242,6 +244,7 @@ pub fn generate( ty: <#ty as #crate_name::InputType>::create_type_info(registry), default_value: #schema_default, visible: #visible, + inaccessible: #inaccessible, is_secret: #secret, }); }); @@ -367,6 +370,7 @@ pub fn generate( provides: #provides, requires: #requires, shareable: #shareable, + inaccessible: #inaccessible, visible: #visible, compute_complexity: #complexity, })); diff --git a/derive/src/directive.rs b/derive/src/directive.rs index b0f79239..4b89a6d9 100644 --- a/derive/src/directive.rs +++ b/derive/src/directive.rs @@ -89,6 +89,7 @@ pub fn generate( ty: <#arg_ty as #crate_name::InputType>::create_type_info(registry), default_value: #schema_default, visible: #visible, + inaccessible: false, is_secret: #secret, }); }); diff --git a/derive/src/enum.rs b/derive/src/enum.rs index 433aa35e..3dee9199 100644 --- a/derive/src/enum.rs +++ b/derive/src/enum.rs @@ -21,6 +21,7 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult { .clone() .unwrap_or_else(|| RenameTarget::Type.rename(ident.to_string())); + let inaccessible = enum_args.inaccessible; let desc = get_rustdoc(&enum_args.attrs)? .map(|s| quote! { ::std::option::Option::Some(#s) }) .unwrap_or_else(|| quote! {::std::option::Option::None}); @@ -47,6 +48,7 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult { .rename_items .rename(variant.ident.unraw().to_string(), RenameTarget::EnumItem) }); + let inaccessible = variant.inaccessible; let item_deprecation = gen_deprecation(&variant.deprecation, &crate_name); let item_desc = get_rustdoc(&variant.attrs)? .map(|s| quote! { ::std::option::Option::Some(#s) }) @@ -67,6 +69,7 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult { description: #item_desc, deprecation: #item_deprecation, visible: #visible, + inaccessible: #inaccessible, }); }); } @@ -141,6 +144,7 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult { enum_items }, visible: #visible, + inaccessible: #inaccessible, rust_typename: ::std::any::type_name::(), } }) diff --git a/derive/src/input_object.rs b/derive/src/input_object.rs index 788428bb..281bdddb 100644 --- a/derive/src/input_object.rs +++ b/derive/src/input_object.rs @@ -12,6 +12,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult let crate_name = get_crate_name(object_args.internal); let (impl_generics, ty_generics, where_clause) = object_args.generics.split_for_impl(); let ident = &object_args.ident; + let inaccessible = object_args.inaccessible; let s = match &object_args.data { Data::Struct(s) => s, _ => { @@ -65,6 +66,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult .rename_fields .rename(ident.unraw().to_string(), RenameTarget::Field) }); + let inaccessible = field.inaccessible; if field.skip || field.skip_input { get_fields.push(quote! { @@ -188,6 +190,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult ty: <#ty as #crate_name::InputType>::create_type_info(registry), default_value: #schema_default, visible: #visible, + inaccessible: #inaccessible, is_secret: #secret, }); }) @@ -240,6 +243,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult fields }, visible: #visible, + inaccessible: #inaccessible, rust_typename: ::std::any::type_name::(), oneof: false, }) @@ -287,6 +291,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult fields }, visible: #visible, + inaccessible: #inaccessible, rust_typename: ::std::any::type_name::(), oneof: false, }) diff --git a/derive/src/interface.rs b/derive/src/interface.rs index 6a89798e..962a152a 100644 --- a/derive/src/interface.rs +++ b/derive/src/interface.rs @@ -31,6 +31,7 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult GeneratorResult GeneratorResult GeneratorResult::create_type_info(registry), default_value: #schema_default, visible: #visible, + inaccessible: #inaccessible, is_secret: #secret, }); }); @@ -276,6 +280,7 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult GeneratorResult(), } }) diff --git a/derive/src/merged_object.rs b/derive/src/merged_object.rs index 2da26995..b6108c76 100644 --- a/derive/src/merged_object.rs +++ b/derive/src/merged_object.rs @@ -15,6 +15,7 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult GeneratorResult GeneratorResult(), } diff --git a/derive/src/newtype.rs b/derive/src/newtype.rs index ba90573c..24d56054 100644 --- a/derive/src/newtype.rs +++ b/derive/src/newtype.rs @@ -12,6 +12,7 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult { let crate_name = get_crate_name(newtype_args.internal); let ident = &newtype_args.ident; let (impl_generics, ty_generics, where_clause) = newtype_args.generics.split_for_impl(); + let inaccessible = newtype_args.inaccessible; let gql_typename = match &newtype_args.name { NewTypeName::New(name) => Some(name.clone()), NewTypeName::Rust => Some(RenameTarget::Type.rename(ident.to_string())), @@ -51,6 +52,7 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult { description: #desc, is_valid: |value| <#ident as #crate_name::ScalarType>::is_valid(value), visible: #visible, + inaccessible: #inaccessible, specified_by_url: #specified_by_url, }) } diff --git a/derive/src/object.rs b/derive/src/object.rs index 3791c581..eb90d4f3 100644 --- a/derive/src/object.rs +++ b/derive/src/object.rs @@ -27,6 +27,7 @@ pub fn generate( let (impl_generics, _, where_clause) = item_impl.generics.split_for_impl(); let extends = object_args.extends; let shareable = object_args.shareable; + let inaccessible = object_args.inaccessible; let gql_typename = if !object_args.name_type { object_args .name @@ -317,6 +318,7 @@ pub fn generate( let field_deprecation = gen_deprecation(&method_args.deprecation, &crate_name); let external = method_args.external; let shareable = method_args.shareable; + let inaccessible = method_args.inaccessible; let requires = match &method_args.requires { Some(requires) => quote! { ::std::option::Option::Some(#requires) }, None => quote! { ::std::option::Option::None }, @@ -353,6 +355,7 @@ pub fn generate( validator, visible, secret, + inaccessible, .. }, ) in &args @@ -386,6 +389,7 @@ pub fn generate( ty: <#ty as #crate_name::InputType>::create_type_info(registry), default_value: #schema_default, visible: #visible, + inaccessible: #inaccessible, is_secret: #secret, }); }); @@ -510,6 +514,7 @@ pub fn generate( provides: #provides, requires: #requires, shareable: #shareable, + inaccessible: #inaccessible, visible: #visible, compute_complexity: #complexity, }); @@ -644,6 +649,7 @@ pub fn generate( cache_control: #cache_control, extends: #extends, shareable: #shareable, + inaccessible: #inaccessible, keys: ::std::option::Option::None, visible: #visible, is_subscription: false, @@ -684,6 +690,7 @@ pub fn generate( cache_control: #cache_control, extends: #extends, shareable: #shareable, + inaccessible: #inaccessible, keys: ::std::option::Option::None, visible: #visible, is_subscription: false, diff --git a/derive/src/oneof_object.rs b/derive/src/oneof_object.rs index 3b13021e..72d16121 100644 --- a/derive/src/oneof_object.rs +++ b/derive/src/oneof_object.rs @@ -16,6 +16,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult let desc = get_rustdoc(&object_args.attrs)? .map(|s| quote! { ::std::option::Option::Some(#s) }) .unwrap_or_else(|| quote! {::std::option::Option::None}); + let inaccessible = object_args.inaccessible; let gql_typename = object_args .name .clone() @@ -41,6 +42,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult .rename_fields .rename(enum_name.to_string(), RenameTarget::Field) }); + let inaccessible = variant.inaccessible; let desc = get_rustdoc(&object_args.attrs)? .map(|s| quote! { ::std::option::Option::Some(#s) }) .unwrap_or_else(|| quote! {::std::option::Option::None}); @@ -80,6 +82,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult ty: <::std::option::Option<#ty> as #crate_name::InputType>::create_type_info(registry), default_value: ::std::option::Option::None, visible: #visible, + inaccessible: #inaccessible, is_secret: #secret, }); }); @@ -133,6 +136,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult fields }, visible: #visible, + inaccessible: #inaccessible, rust_typename: ::std::any::type_name::(), oneof: true, }) @@ -183,6 +187,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult fields }, visible: #visible, + inaccessible: #inaccessible, rust_typename: ::std::any::type_name::(), oneof: true, }) diff --git a/derive/src/scalar.rs b/derive/src/scalar.rs index a8f99a18..b11deb44 100644 --- a/derive/src/scalar.rs +++ b/derive/src/scalar.rs @@ -30,6 +30,7 @@ pub fn generate( let generic = &item_impl.generics; let where_clause = &item_impl.generics.where_clause; let visible = visible_fn(&scalar_args.visible); + let inaccessible = scalar_args.inaccessible; 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 }, @@ -52,6 +53,7 @@ pub fn generate( description: #desc, is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value), visible: #visible, + inaccessible: #inaccessible, specified_by_url: #specified_by_url, }) } @@ -82,6 +84,7 @@ pub fn generate( description: #desc, is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value), visible: #visible, + inaccessible: #inaccessible, specified_by_url: #specified_by_url, }) } diff --git a/derive/src/simple_object.rs b/derive/src/simple_object.rs index d274fc67..37088634 100644 --- a/derive/src/simple_object.rs +++ b/derive/src/simple_object.rs @@ -31,6 +31,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult GeneratorResult quote! { ::std::option::Option::Some(#requires) }, None => quote! { ::std::option::Option::None }, @@ -177,6 +179,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult GeneratorResult GeneratorResult::create_type_info(registry), default_value: #schema_default, visible: #visible, + inaccessible: false, is_secret: #secret, }); }); @@ -269,6 +270,7 @@ pub fn generate( provides: ::std::option::Option::None, shareable: false, visible: #visible, + inaccessible: false, compute_complexity: #complexity, }); }); @@ -410,6 +412,7 @@ pub fn generate( keys: ::std::option::Option::None, visible: #visible, shareable: false, + inaccessible: false, is_subscription: true, rust_typename: ::std::any::type_name::(), }) diff --git a/derive/src/union.rs b/derive/src/union.rs index dceb5a07..05af9d61 100644 --- a/derive/src/union.rs +++ b/derive/src/union.rs @@ -28,6 +28,7 @@ pub fn generate(union_args: &args::Union) -> GeneratorResult { .clone() .unwrap_or_else(|| RenameTarget::Type.rename(ident.to_string())); + let inaccessible = union_args.inaccessible; let desc = get_rustdoc(&union_args.attrs)? .map(|s| quote! { ::std::option::Option::Some(#s) }) .unwrap_or_else(|| quote! {::std::option::Option::None}); @@ -193,6 +194,7 @@ pub fn generate(union_args: &args::Union) -> GeneratorResult { possible_types }, visible: #visible, + inaccessible: #inaccessible, rust_typename: ::std::any::type_name::(), } }) diff --git a/src/registry/export_sdl.rs b/src/registry/export_sdl.rs index 472f144c..0b937c4f 100644 --- a/src/registry/export_sdl.rs +++ b/src/registry/export_sdl.rs @@ -152,6 +152,10 @@ impl Registry { sdl.push_str(", "); } sdl.push_str(&export_input_value(arg)); + + if options.federation && arg.inaccessible { + write!(sdl, " @inaccessible").ok(); + } } write!(sdl, "): {}", field.ty).ok(); } else { @@ -173,6 +177,9 @@ impl Registry { if field.shareable { write!(sdl, " @shareable").ok(); } + if field.inaccessible { + write!(sdl, " @inaccessible").ok(); + } } writeln!(sdl).ok(); @@ -182,7 +189,10 @@ impl Registry { fn export_type(&self, ty: &MetaType, sdl: &mut String, options: &SDLExportOptions) { match ty { MetaType::Scalar { - name, description, .. + name, + description, + inaccessible, + .. } => { let mut export_scalar = !SYSTEM_SCALARS.contains(&name.as_str()); if options.federation && FEDERATION_SCALARS.contains(&name.as_str()) { @@ -192,7 +202,12 @@ impl Registry { if let Some(description) = description { export_description(sdl, options, true, description); } - writeln!(sdl, "scalar {}", name).ok(); + write!(sdl, "scalar {} ", name).ok(); + + if options.federation && *inaccessible { + write!(sdl, "@inaccessible ").ok(); + } + writeln!(sdl).ok(); } } MetaType::Object { @@ -202,6 +217,7 @@ impl Registry { keys, description, shareable, + inaccessible, .. } => { if Some(name.as_str()) == self.subscription_type.as_deref() @@ -248,6 +264,10 @@ impl Registry { if *shareable { write!(sdl, "@shareable ").ok(); } + + if *inaccessible { + write!(sdl, "@inaccessible ").ok(); + } } writeln!(sdl, "{{").ok(); @@ -260,6 +280,7 @@ impl Registry { extends, keys, description, + inaccessible, .. } => { if let Some(description) = description { @@ -277,6 +298,9 @@ impl Registry { write!(sdl, "@key(fields: \"{}\") ", key).ok(); } } + if *inaccessible { + write!(sdl, "@inaccessible ").ok(); + } } self.write_implements(sdl, name); @@ -288,6 +312,7 @@ impl Registry { name, enum_values, description, + inaccessible, .. } => { if let Some(description) = description { @@ -295,6 +320,9 @@ impl Registry { } write!(sdl, "enum {} ", name).ok(); + if options.federation && *inaccessible { + write!(sdl, "@inaccessible ").ok(); + } writeln!(sdl, "{{").ok(); let mut values = enum_values.values().collect::>(); @@ -305,6 +333,9 @@ impl Registry { for value in values { write!(sdl, "\t{}", value.name).ok(); write_deprecated(sdl, &value.deprecation); + if options.federation && value.inaccessible { + write!(sdl, " @inaccessible").ok(); + } writeln!(sdl).ok(); } @@ -314,6 +345,7 @@ impl Registry { name, input_fields, description, + inaccessible, oneof, .. } => { @@ -326,6 +358,9 @@ impl Registry { if *oneof { write!(sdl, "@oneof ").ok(); } + if options.federation && *inaccessible { + write!(sdl, "@inaccessible ").ok(); + } writeln!(sdl, "{{").ok(); let mut fields = input_fields.values().collect::>(); @@ -337,7 +372,11 @@ impl Registry { if let Some(description) = field.description { export_description(sdl, options, false, description); } - writeln!(sdl, "\t{}", export_input_value(&field)).ok(); + write!(sdl, "\t{} ", export_input_value(&field)).ok(); + if options.federation && field.inaccessible { + write!(sdl, "@inaccessible ").ok(); + } + writeln!(sdl).ok(); } writeln!(sdl, "}}").ok(); @@ -346,13 +385,19 @@ impl Registry { name, possible_types, description, + inaccessible, .. } => { if let Some(description) = description { export_description(sdl, options, true, description); } - write!(sdl, "union {} =", name).ok(); + write!(sdl, "union {} ", name).ok(); + if options.federation && *inaccessible { + write!(sdl, "@inaccessible ").ok(); + } + write!(sdl, "=").ok(); + for (idx, ty) in possible_types.iter().enumerate() { if idx == 0 { write!(sdl, " {}", ty).ok(); diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 44648602..42cba04f 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -111,6 +111,7 @@ pub struct MetaInputValue { pub ty: String, pub default_value: Option, pub visible: Option, + pub inaccessible: bool, pub is_secret: bool, } @@ -167,6 +168,7 @@ pub struct MetaField { pub provides: Option<&'static str>, pub visible: Option, pub shareable: bool, + pub inaccessible: bool, pub compute_complexity: Option, } @@ -176,6 +178,7 @@ pub struct MetaEnumValue { pub description: Option<&'static str>, pub deprecation: Deprecation, pub visible: Option, + pub inaccessible: bool, } type MetaVisibleFn = fn(&Context<'_>) -> bool; @@ -210,6 +213,7 @@ pub enum MetaType { description: Option<&'static str>, is_valid: fn(value: &Value) -> bool, visible: Option, + inaccessible: bool, specified_by_url: Option<&'static str>, }, Object { @@ -221,6 +225,7 @@ pub enum MetaType { shareable: bool, keys: Option>, visible: Option, + inaccessible: bool, is_subscription: bool, rust_typename: &'static str, }, @@ -232,6 +237,7 @@ pub enum MetaType { extends: bool, keys: Option>, visible: Option, + inaccessible: bool, rust_typename: &'static str, }, Union { @@ -239,6 +245,7 @@ pub enum MetaType { description: Option<&'static str>, possible_types: IndexSet, visible: Option, + inaccessible: bool, rust_typename: &'static str, }, Enum { @@ -246,6 +253,7 @@ pub enum MetaType { description: Option<&'static str>, enum_values: IndexMap<&'static str, MetaEnumValue>, visible: Option, + inaccessible: bool, rust_typename: &'static str, }, InputObject { @@ -253,6 +261,7 @@ pub enum MetaType { description: Option<&'static str>, input_fields: IndexMap, visible: Option, + inaccessible: bool, rust_typename: &'static str, oneof: bool, }, @@ -495,6 +504,7 @@ impl Registry { cache_control: Default::default(), extends: false, shareable: false, + inaccessible: false, keys: None, visible: None, is_subscription: false, @@ -623,6 +633,7 @@ impl Registry { requires: None, provides: None, shareable: false, + inaccessible: false, visible: None, compute_complexity: None, }, @@ -637,6 +648,7 @@ impl Registry { description: None, possible_types, visible: None, + inaccessible: false, rust_typename: "async_graphql::federation::Entity", }, ); @@ -657,6 +669,7 @@ impl Registry { ty: "[_Any!]!".to_string(), default_value: None, visible: None, + inaccessible: false, is_secret: false, }, ); @@ -670,6 +683,7 @@ impl Registry { provides: None, shareable: false, visible: None, + inaccessible: false, compute_complexity: None, }, ); @@ -701,6 +715,7 @@ impl Registry { provides: None, shareable: false, visible: None, + inaccessible: false, compute_complexity: None, }, ); @@ -711,6 +726,7 @@ impl Registry { shareable: false, keys: None, visible: None, + inaccessible: false, is_subscription: false, rust_typename: "async_graphql::federation::Service", }, diff --git a/src/resolver_utils/scalar.rs b/src/resolver_utils/scalar.rs index e46538f7..39c50866 100644 --- a/src/resolver_utils/scalar.rs +++ b/src/resolver_utils/scalar.rs @@ -164,6 +164,7 @@ macro_rules! scalar_internal { description: $desc, is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value), visible: ::std::option::Option::None, + inaccessible: false, specified_by_url: $specified_by_url, } }) @@ -199,6 +200,7 @@ macro_rules! scalar_internal { description: $desc, is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value), visible: ::std::option::Option::None, + inaccessible: false, specified_by_url: $specified_by_url, } }) diff --git a/src/schema.rs b/src/schema.rs index 096b50d7..36f46773 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -399,6 +399,7 @@ where ty: "Boolean!".to_string(), default_value: None, visible: None, + inaccessible: false, is_secret: false, }); args @@ -423,6 +424,7 @@ where ty: "Boolean!".to_string(), default_value: None, visible: None, + inaccessible: false, is_secret: false, }); args diff --git a/src/types/connection/mod.rs b/src/types/connection/mod.rs index 73592dd2..1b9d06f6 100644 --- a/src/types/connection/mod.rs +++ b/src/types/connection/mod.rs @@ -222,7 +222,7 @@ where /// # Examples /// /// ```rust -/// +/// /// use async_graphql::*; /// use async_graphql::types::connection::*; /// diff --git a/src/types/empty_mutation.rs b/src/types/empty_mutation.rs index 972040a8..96ad2fc1 100644 --- a/src/types/empty_mutation.rs +++ b/src/types/empty_mutation.rs @@ -57,6 +57,7 @@ impl OutputType for EmptyMutation { shareable: false, keys: None, visible: None, + inaccessible: false, is_subscription: false, rust_typename: std::any::type_name::(), }) diff --git a/src/types/empty_subscription.rs b/src/types/empty_subscription.rs index c9efc7ec..a9a3f4d8 100644 --- a/src/types/empty_subscription.rs +++ b/src/types/empty_subscription.rs @@ -26,6 +26,7 @@ impl SubscriptionType for EmptySubscription { shareable: false, keys: None, visible: None, + inaccessible: false, is_subscription: true, rust_typename: std::any::type_name::(), }) diff --git a/src/types/external/json_object/btreemap.rs b/src/types/external/json_object/btreemap.rs index 16328ea9..1034d7b7 100644 --- a/src/types/external/json_object/btreemap.rs +++ b/src/types/external/json_object/btreemap.rs @@ -29,6 +29,7 @@ where description: Some("A scalar that can represent any JSON Object value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, }) } @@ -84,6 +85,7 @@ where description: Some("A scalar that can represent any JSON Object value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, }) } diff --git a/src/types/external/json_object/hashmap.rs b/src/types/external/json_object/hashmap.rs index 2725fa82..cae73228 100644 --- a/src/types/external/json_object/hashmap.rs +++ b/src/types/external/json_object/hashmap.rs @@ -36,6 +36,7 @@ where description: Some("A scalar that can represent any JSON Object value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, }) } @@ -92,6 +93,7 @@ where description: Some("A scalar that can represent any JSON Object value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, }) } diff --git a/src/types/json.rs b/src/types/json.rs index 5f1873b1..c5b05639 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -54,6 +54,7 @@ impl InputType for Json { description: Some("A scalar that can represent any JSON value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, }) } @@ -83,6 +84,7 @@ impl OutputType for Json { description: Some("A scalar that can represent any JSON value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, }) } @@ -110,6 +112,7 @@ impl InputType for serde_json::Value { description: Some("A scalar that can represent any JSON value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, } }) @@ -141,6 +144,7 @@ impl OutputType for serde_json::Value { description: Some("A scalar that can represent any JSON value."), is_valid: |_| true, visible: None, + inaccessible: false, specified_by_url: None, } }) diff --git a/src/types/merged_object.rs b/src/types/merged_object.rs index 94fb9556..81d3dee6 100644 --- a/src/types/merged_object.rs +++ b/src/types/merged_object.rs @@ -80,6 +80,7 @@ where shareable: false, keys: None, visible: None, + inaccessible: false, is_subscription: false, rust_typename: std::any::type_name::(), } @@ -139,6 +140,7 @@ where shareable: false, keys: None, visible: None, + inaccessible: false, is_subscription: false, rust_typename: std::any::type_name::(), } @@ -173,6 +175,7 @@ impl SubscriptionType for MergedObjectTail { shareable: false, keys: None, visible: None, + inaccessible: false, is_subscription: false, rust_typename: std::any::type_name::(), }) diff --git a/src/types/query_root.rs b/src/types/query_root.rs index 6043dd05..f38cdfb5 100644 --- a/src/types/query_root.rs +++ b/src/types/query_root.rs @@ -132,6 +132,7 @@ impl OutputType for QueryRoot { requires: None, provides: None, shareable: false, + inaccessible: false, visible: None, compute_complexity: None, }, @@ -152,6 +153,7 @@ impl OutputType for QueryRoot { ty: "String!".to_string(), default_value: None, visible: None, + inaccessible: false, is_secret: false, }, ); @@ -164,6 +166,7 @@ impl OutputType for QueryRoot { requires: None, provides: None, shareable: false, + inaccessible: false, visible: None, compute_complexity: None, }, diff --git a/src/types/upload.rs b/src/types/upload.rs index 2820dcf4..5733a121 100644 --- a/src/types/upload.rs +++ b/src/types/upload.rs @@ -113,6 +113,7 @@ impl InputType for Upload { description: None, is_valid: |value| matches!(value, Value::String(_)), visible: None, + inaccessible: false, specified_by_url: Some("https://github.com/jaydenseric/graphql-multipart-request-spec"), }) } diff --git a/tests/federation.rs b/tests/federation.rs index a3b82178..28febc4c 100644 --- a/tests/federation.rs +++ b/tests/federation.rs @@ -327,3 +327,167 @@ pub async fn test_entity_shareable() { true ); } + +#[tokio::test] +pub async fn test_entity_inaccessible() { + #[derive(SimpleObject)] + struct MyObjFieldInaccessible { + #[graphql(inaccessible)] + obj_field_inaccessible_a: i32, + } + + #[derive(SimpleObject)] + #[graphql(inaccessible)] + struct MyObjInaccessible { + a: i32, + } + + #[derive(InputObject)] + struct MyInputObjFieldInaccessible { + #[graphql(inaccessible)] + input_field_inaccessible_a: i32, + } + + #[derive(InputObject)] + #[graphql(inaccessible)] + struct MyInputObjInaccessible { + a: i32, + } + + #[derive(Enum, PartialEq, Eq, Copy, Clone)] + enum MyEnumVariantInaccessible { + #[graphql(inaccessible)] + OptionAInaccessible, + OptionB, + OptionC, + } + + #[derive(Enum, PartialEq, Eq, Copy, Clone)] + #[graphql(inaccessible)] + enum MyEnumInaccessible { + OptionA, + OptionB, + OptionC, + } + + #[derive(SimpleObject)] + struct MyInterfaceObjA { + inaccessible_interface_value: String, + } + + #[derive(SimpleObject)] + #[graphql(inaccessible)] + struct MyInterfaceObjB { + inaccessible_interface_value: String, + } + + #[derive(Interface)] + #[graphql(field(name = "inaccessible_interface_value", type = "String", inaccessible))] + #[graphql(inaccessible)] + enum MyInterfaceInaccessible { + MyInterfaceObjA(MyInterfaceObjA), + MyInterfaceObjB(MyInterfaceObjB), + } + + #[derive(Union)] + #[graphql(inaccessible)] + enum MyUnionInaccessible { + MyInterfaceObjA(MyInterfaceObjA), + MyInterfaceObjB(MyInterfaceObjB), + } + + struct MyNumberInaccessible(i32); + + #[Scalar(inaccessible)] + impl ScalarType for MyNumberInaccessible { + fn parse(_value: Value) -> InputValueResult { + todo!() + } + + fn to_value(&self) -> Value { + todo!() + } + } + + struct Query; + + #[Object(extends)] + impl Query { + #[graphql(entity)] + async fn find_obj_field_inaccessible(&self, _id: i32) -> MyObjFieldInaccessible { + todo!() + } + + #[graphql(entity)] + async fn find_obj_inaccessible(&self, _id: i32) -> MyObjInaccessible { + todo!() + } + + async fn enum_variant_inaccessible(&self, _id: i32) -> MyEnumVariantInaccessible { + todo!() + } + + async fn enum_inaccessible(&self, _id: i32) -> MyEnumInaccessible { + todo!() + } + + #[graphql(inaccessible)] + async fn inaccessible_field(&self, _id: i32) -> i32 { + todo!() + } + + async fn inaccessible_argument(&self, #[graphql(inaccessible)] _id: i32) -> i32 { + todo!() + } + + async fn inaccessible_interface(&self) -> MyInterfaceInaccessible { + todo!() + } + + async fn inaccessible_union(&self) -> MyUnionInaccessible { + todo!() + } + + async fn inaccessible_scalar(&self) -> MyNumberInaccessible { + todo!() + } + + async fn inaccessible_input_field(&self, _value: MyInputObjFieldInaccessible) -> i32 { + todo!() + } + + async fn inaccessible_input(&self, _value: MyInputObjInaccessible) -> i32 { + todo!() + } + } + + let schema_sdl = Schema::new(Query, EmptyMutation, EmptySubscription) + .sdl_with_options(SDLExportOptions::new().federation()); + + println!("{}", schema_sdl); + + // FIELD_DEFINITION + assert!(schema_sdl.contains("inaccessibleField(id: Int!): Int! @inaccessible")); + assert!(schema_sdl.contains("objFieldInaccessibleA: Int! @inaccessible")); + assert!(schema_sdl.contains("inaccessibleInterfaceValue: String! @inaccessible")); + // INTERFACE + assert!(schema_sdl.contains("interface MyInterfaceInaccessible @inaccessible")); + // OBJECT + assert!(schema_sdl.contains(r#"type MyObjInaccessible @key(fields: "id") @inaccessible"#)); + assert!(schema_sdl + .contains("type MyInterfaceObjB implements MyInterfaceInaccessible @inaccessible")); + // UNION + assert!(schema_sdl.contains("union MyUnionInaccessible @inaccessible =")); + // ARGUMENT_DEFINITION + assert!(schema_sdl.contains("inaccessibleArgument(id: Int! @inaccessible): Int!")); + // SCALAR + assert!(schema_sdl.contains("scalar MyNumberInaccessible @inaccessible")); + // ENUM + assert!(schema_sdl.contains("enum MyEnumInaccessible @inaccessible")); + // ENUM_VALUE + assert!(schema_sdl.contains("OPTION_A_INACCESSIBLE @inaccessible")); + // INPUT_OBJECT + assert!(schema_sdl.contains("input MyInputObjInaccessible @inaccessible")); + // INPUT_FIELD_DEFINITION + assert!(schema_sdl.contains("inputFieldInaccessibleA: Int! @inaccessible")); +} From dd4515c18f4b5fd47c55ecd73c9424cb4e760536 Mon Sep 17 00:00:00 2001 From: Nicolas Moutschen Date: Thu, 18 Aug 2022 14:31:01 +0200 Subject: [PATCH 2/4] docs: add docs for inaccessible directive --- docs/en/src/apollo_federation.md | 2 ++ src/docs/complex_object.md | 3 +++ src/docs/enum.md | 16 +++++++++------- src/docs/input_object.md | 2 ++ src/docs/interface.md | 32 ++++++++++++++++++-------------- src/docs/merged_object.md | 1 + src/docs/newtype.md | 1 + src/docs/object.md | 3 +++ src/docs/scalar.md | 1 + src/docs/simple_object.md | 3 +++ src/docs/union.md | 11 ++++++----- 11 files changed, 49 insertions(+), 26 deletions(-) diff --git a/docs/en/src/apollo_federation.md b/docs/en/src/apollo_federation.md index 93d515d5..c9fa8bed 100644 --- a/docs/en/src/apollo_federation.md +++ b/docs/en/src/apollo_federation.md @@ -14,6 +14,8 @@ - The `shareable` directive is used to indicate that an object type's field is allowed to be resolved by multiple subgraphs (by default, each field can be resolved by only one subgraph). +- The `inaccessible` directive is used to indicate that a location in the schema cannot be queried at the supergraph level, but can still be queried at the subgraph level. + ## Entity lookup function ```rust diff --git a/src/docs/complex_object.md b/src/docs/complex_object.md index d17cb294..0c7e4d08 100644 --- a/src/docs/complex_object.md +++ b/src/docs/complex_object.md @@ -16,6 +16,7 @@ some simple fields, and use the `ComplexObject` macro to define some other field | rename_fields | Rename all the fields according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y | | rename_args | Rename all the arguments according to the given case convention. The possible values are "lowercase", "UPPERCASE", "PascalCase", "camelCase", "snake_case", "SCREAMING_SNAKE_CASE". | string | Y | | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | +| inaccessible | Indicate that an object is not accessible from a supergraph when using Apollo Federation | bool | Y | # Field attributes @@ -31,6 +32,7 @@ some simple fields, and use the `ComplexObject` macro to define some other field | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | | shareable | Indicate that a field is allowed to be resolved by multiple subgraphs | bool | Y | +| inaccessible | Indicate that a field is not accessible from a supergraph when using Apollo Federation | bool | Y | | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | @@ -49,6 +51,7 @@ some simple fields, and use the `ComplexObject` macro to define some other field | default | Argument default value | literal | Y | | default_with | Expression to generate default value | code string | Y | | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | +| inaccessible | Indicate that a field argument is not accessible from a supergraph when using Apollo Federation | bool | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | diff --git a/src/docs/enum.md b/src/docs/enum.md index 1ce6029f..7957ea5e 100644 --- a/src/docs/enum.md +++ b/src/docs/enum.md @@ -11,16 +11,18 @@ Define a GraphQL enum | remote | Derive a remote enum | string | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that an enum is not accessible from a supergraph when using Apollo Federation | bool | Y | # Item attributes -| Attribute | description | Type | Optional | -|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------| -| name | Item name | string | Y | -| deprecation | Item deprecated | bool | Y | -| deprecation | Item deprecation reason | string | Y | -| visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| Attribute | description | Type | Optional | +|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------| +| name | Item name | string | Y | +| deprecation | Item deprecated | bool | Y | +| deprecation | Item deprecation reason | string | Y | +| visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that an item is not accessible from a supergraph when using Apollo Federation | bool | Y | # Examples diff --git a/src/docs/input_object.md b/src/docs/input_object.md index c93f1d88..c05c95ce 100644 --- a/src/docs/input_object.md +++ b/src/docs/input_object.md @@ -11,6 +11,7 @@ Define a GraphQL input object | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | | concretes | Specify how the concrete type of the generic SimpleObject should be implemented. | ConcreteType | Y | +| inaccessible | Indicate that an input object is not accessible from a supergraph when using Apollo Federation | bool | Y | # Field attributes @@ -28,6 +29,7 @@ Define a GraphQL input object | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | +| inaccessible | Indicate that a field is not accessible from a supergraph when using Apollo Federation | bool | Y | # Examples diff --git a/src/docs/interface.md b/src/docs/interface.md index d630af42..34200a3a 100644 --- a/src/docs/interface.md +++ b/src/docs/interface.md @@ -13,23 +13,25 @@ Define a GraphQL interface | extends | Add fields to an entity that's defined in another service | bool | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that an interface is not accessible from a supergraph when using Apollo Federation | bool | Y | # Field attributes -| Attribute | description | Type | Optional | -|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|----------| -| name | Field name | string | N | -| type | Field type | string | N | -| method | Rust resolver method name. If specified, `name` will not be camelCased in schema definition | string | Y | -| desc | Field description | string | Y | -| deprecation | Field deprecated | bool | Y | -| deprecation | Field deprecation reason | string | Y | -| arg | Field arguments | InterfaceFieldArgument | Y | -| external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | -| provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | -| requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | -| visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| Attribute | description | Type | Optional | +|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------|----------| +| name | Field name | string | N | +| type | Field type | string | N | +| method | Rust resolver method name. If specified, `name` will not be camelCased in schema definition | string | Y | +| desc | Field description | string | Y | +| deprecation | Field deprecated | bool | Y | +| deprecation | Field deprecation reason | string | Y | +| arg | Field arguments | InterfaceFieldArgument | Y | +| external | Mark a field as owned by another service. This allows service A to use fields from service B while also knowing at runtime the types of that field. | bool | Y | +| provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | +| requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | +| visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that a field is not accessible from a supergraph when using Apollo Federation | bool | Y | # Field argument attributes @@ -44,6 +46,8 @@ Define a GraphQL interface | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | +| inaccessible | Indicate that an argument is not accessible from a supergraph when using Apollo Federation | bool | Y | + # Define an interface diff --git a/src/docs/merged_object.md b/src/docs/merged_object.md index 459cc8aa..57382bff 100644 --- a/src/docs/merged_object.md +++ b/src/docs/merged_object.md @@ -12,6 +12,7 @@ Define a merged object with multiple object types. | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | | serial | Resolve each field sequentially. | bool | Y | +| inaccessible | Indicate that an object is not accessible from a supergraph when using Apollo Federation | bool | Y | # Examples diff --git a/src/docs/newtype.md b/src/docs/newtype.md index 9dc55454..762d2390 100644 --- a/src/docs/newtype.md +++ b/src/docs/newtype.md @@ -11,6 +11,7 @@ It also implements `From` and `Into`. | 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 | | 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 | +| inaccessible | Indicate that an object is not accessible from a supergraph when using Apollo Federation | bool | Y | # Examples diff --git a/src/docs/object.md b/src/docs/object.md index 3a821719..19138ad8 100644 --- a/src/docs/object.md +++ b/src/docs/object.md @@ -17,6 +17,7 @@ All methods are converted to camelCase. | use_type_description | Specifies that the description of the type is on the type declaration. [`Description`]()(derive.Description.html) | bool | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that an object is not accessible from a supergraph when using Apollo Federation | bool | Y | | serial | Resolve each field sequentially. | bool | Y | | concretes | Specify how the concrete type of the generic SimpleObject should be implemented. | ConcreteType | Y | | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | @@ -35,6 +36,7 @@ All methods are converted to camelCase. | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | | shareable | Indicate that a field is allowed to be resolved by multiple subgraphs | bool | Y | +| inaccessible | Indicate that a field is not accessible from a supergraph when using Apollo Federation | bool | Y | | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | @@ -55,6 +57,7 @@ All methods are converted to camelCase. | validator | Input value validator *[See also the Book](https://async-graphql.github.io/async-graphql/en/input_value_validators.html)* | object | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that an argument is not accessible from a supergraph when using Apollo Federation | bool | Y | | secret | Mark this field as a secret, it will not output the actual value in the log. | bool | Y | | key | Is entity key(for Federation) | bool | Y | | process_with | Upon successful parsing, invokes specified function. Its signature must be `fn(&mut T)`. | code path | Y | diff --git a/src/docs/scalar.md b/src/docs/scalar.md index bf8d01a1..e6a7418e 100644 --- a/src/docs/scalar.md +++ b/src/docs/scalar.md @@ -6,3 +6,4 @@ Define a Scalar |------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------| | 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 | +| inaccessible | Indicate that a scalar is not accessible from a supergraph when using Apollo Federation | bool | Y | \ No newline at end of file diff --git a/src/docs/simple_object.md b/src/docs/simple_object.md index cac68a51..139f8e65 100644 --- a/src/docs/simple_object.md +++ b/src/docs/simple_object.md @@ -13,6 +13,7 @@ Similar to `Object`, but defined on a structure that automatically generates get | cache_control | Object cache control | [`CacheControl`](struct.CacheControl.html) | Y | | extends | Add fields to an entity that's defined in another service | bool | Y | | shareable | Indicate that an object type's field is allowed to be resolved by multiple subgraphs | bool | Y | +| inaccessible | Indicate that an object is not accessible from a supergraph when using Apollo Federation | bool | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | | concretes | Specify how the concrete type of the generic SimpleObject should be implemented. *[See also the Book](https://async-graphql.github.io/async-graphql/en/define_simple_object.html#generic-simpleobjects) | ConcreteType | Y | @@ -35,6 +36,8 @@ Similar to `Object`, but defined on a structure that automatically generates get | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | | shareable | Indicate that a field is allowed to be resolved by multiple subgraphs | bool | Y | + +| inaccessible | Indicate that a field is not accessible from a supergraph when using Apollo Federation | bool | Y | | guard | Field of guard *[See also the Book](https://async-graphql.github.io/async-graphql/en/field_guard.html)* | string | Y | | visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | diff --git a/src/docs/union.md b/src/docs/union.md index a2c3cfe3..78615184 100644 --- a/src/docs/union.md +++ b/src/docs/union.md @@ -4,11 +4,12 @@ Define a GraphQL union # Macro attributes -| Attribute | description | Type | Optional | -|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------| -| name | Object name | string | Y | -| visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| Attribute | description | Type | Optional | +|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------|--------|----------| +| name | Object name | string | Y | +| visible | 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 | Call the specified function. If the return value is `false`, it will not be displayed in introspection. | string | Y | +| inaccessible | Indicate that an union is not accessible from a supergraph when using Apollo Federation | bool | Y | # Item attributes From 0f8b2c52bd3c1736703938054f0d1a837876e7db Mon Sep 17 00:00:00 2001 From: Nicolas Moutschen Date: Thu, 18 Aug 2022 14:31:27 +0200 Subject: [PATCH 3/4] tests: add inaccessible case for Object macro --- tests/federation.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/federation.rs b/tests/federation.rs index 28febc4c..6dccdf9b 100644 --- a/tests/federation.rs +++ b/tests/federation.rs @@ -330,6 +330,20 @@ pub async fn test_entity_shareable() { #[tokio::test] pub async fn test_entity_inaccessible() { + struct MyCustomObjInaccessible; + + #[Object(inaccessible)] + impl MyCustomObjInaccessible { + async fn a(&self) -> i32 { + todo!() + } + + #[graphql(inaccessible)] + async fn custom_object_inaccessible(&self) -> i32 { + todo!() + } + } + #[derive(SimpleObject)] struct MyObjFieldInaccessible { #[graphql(inaccessible)] @@ -459,20 +473,24 @@ pub async fn test_entity_inaccessible() { async fn inaccessible_input(&self, _value: MyInputObjInaccessible) -> i32 { todo!() } + + async fn inaccessible_custom_object(&self) -> MyCustomObjInaccessible { + todo!() + } } let schema_sdl = Schema::new(Query, EmptyMutation, EmptySubscription) .sdl_with_options(SDLExportOptions::new().federation()); - println!("{}", schema_sdl); - // FIELD_DEFINITION assert!(schema_sdl.contains("inaccessibleField(id: Int!): Int! @inaccessible")); assert!(schema_sdl.contains("objFieldInaccessibleA: Int! @inaccessible")); assert!(schema_sdl.contains("inaccessibleInterfaceValue: String! @inaccessible")); + assert!(schema_sdl.contains("customObjectInaccessible: Int! @inaccessible")); // INTERFACE assert!(schema_sdl.contains("interface MyInterfaceInaccessible @inaccessible")); // OBJECT + assert!(schema_sdl.contains("type MyCustomObjInaccessible @inaccessible")); assert!(schema_sdl.contains(r#"type MyObjInaccessible @key(fields: "id") @inaccessible"#)); assert!(schema_sdl .contains("type MyInterfaceObjB implements MyInterfaceInaccessible @inaccessible")); From 7ddde89cc53b972383725b21792c1c72a79e8e3f Mon Sep 17 00:00:00 2001 From: Nicolas Moutschen Date: Thu, 18 Aug 2022 15:12:28 +0200 Subject: [PATCH 4/4] fix: cargo +nightly fmt --- src/types/connection/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/connection/mod.rs b/src/types/connection/mod.rs index 1b9d06f6..73592dd2 100644 --- a/src/types/connection/mod.rs +++ b/src/types/connection/mod.rs @@ -222,7 +222,7 @@ where /// # Examples /// /// ```rust -/// +/// /// use async_graphql::*; /// use async_graphql::types::connection::*; ///