define override directive on fields

* define the directive field on the field struct
* implement printing it on the federation sdl
* update the derive stuff
* add a test
This commit is contained in:
aidan coyne 2022-08-19 16:47:56 -05:00
parent f84781bf08
commit 6f57a13b71
15 changed files with 84 additions and 16 deletions

View File

@ -155,6 +155,8 @@ pub struct SimpleObjectField {
#[darling(default)]
pub inaccessible: bool,
#[darling(default)]
pub override_from: Option<String>,
#[darling(default)]
pub guard: Option<SpannedValue<String>>,
#[darling(default)]
pub visible: Option<Visible>,
@ -291,6 +293,7 @@ pub struct ObjectField {
pub requires: Option<String>,
pub shareable: bool,
pub inaccessible: bool,
pub override_from: Option<String>,
pub guard: Option<SpannedValue<String>>,
pub visible: Option<Visible>,
pub complexity: Option<ComplexityType>,
@ -524,6 +527,8 @@ pub struct InterfaceField {
pub inaccessible: bool,
#[darling(default)]
pub shareable: bool,
#[darling(default)]
pub override_from: Option<String>,
}
#[derive(FromVariant)]
@ -808,6 +813,7 @@ pub struct ComplexObjectField {
pub requires: Option<String>,
pub shareable: bool,
pub inaccessible: bool,
pub override_from: Option<String>,
pub guard: Option<SpannedValue<String>>,
pub visible: Option<Visible>,
pub complexity: Option<ComplexityType>,

View File

@ -173,6 +173,10 @@ pub fn generate(
let field_deprecation = gen_deprecation(&method_args.deprecation, &crate_name);
let external = method_args.external;
let shareable = method_args.shareable;
let override_from = match &method_args.override_from {
Some(from) => quote! { ::std::option::Option::Some(#from) },
None => quote! { ::std::option::Option::None },
};
let inaccessible = method_args.inaccessible;
let requires = match &method_args.requires {
Some(requires) => quote! { ::std::option::Option::Some(#requires) },
@ -371,6 +375,7 @@ pub fn generate(
requires: #requires,
shareable: #shareable,
inaccessible: #inaccessible,
override_from: #override_from,
visible: #visible,
compute_complexity: #complexity,
}));

View File

@ -140,6 +140,7 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream
visible,
shareable,
inaccessible,
override_from,
} in &interface_args.fields
{
let (name, method_name) = if let Some(method) = method {
@ -170,6 +171,10 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream
Some(provides) => quote! { ::std::option::Option::Some(#provides) },
None => quote! { ::std::option::Option::None },
};
let override_from = match &override_from {
Some(from) => quote! { ::std::option::Option::Some(#from) },
None => quote! { ::std::option::Option::None },
};
decl_params.push(quote! { ctx: &'ctx #crate_name::Context<'ctx> });
use_params.push(quote! { ctx });
@ -281,6 +286,7 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream
requires: #requires,
shareable: #shareable,
inaccessible: #inaccessible,
override_from: #override_from,
visible: #visible,
compute_complexity: ::std::option::Option::None,
});

View File

@ -319,6 +319,10 @@ pub fn generate(
let external = method_args.external;
let shareable = method_args.shareable;
let inaccessible = method_args.inaccessible;
let override_from = match &method_args.override_from {
Some(from) => quote! { ::std::option::Option::Some(#from) },
None => quote! { ::std::option::Option::None },
};
let requires = match &method_args.requires {
Some(requires) => quote! { ::std::option::Option::Some(#requires) },
None => quote! { ::std::option::Option::None },
@ -515,6 +519,7 @@ pub fn generate(
requires: #requires,
shareable: #shareable,
inaccessible: #inaccessible,
override_from: #override_from,
visible: #visible,
compute_complexity: #complexity,
});

View File

@ -131,6 +131,10 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
let external = field.external;
let shareable = field.shareable;
let inaccessible = field.inaccessible;
let override_from = match &field.override_from {
Some(from) => quote! { ::std::option::Option::Some(#from) },
None => quote! { ::std::option::Option::None },
};
let requires = match &field.requires {
Some(requires) => quote! { ::std::option::Option::Some(#requires) },
None => quote! { ::std::option::Option::None },
@ -180,6 +184,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
requires: #requires,
shareable: #shareable,
inaccessible: #inaccessible,
override_from: #override_from,
visible: #visible,
compute_complexity: ::std::option::Option::None,
});

View File

@ -269,6 +269,7 @@ pub fn generate(
requires: ::std::option::Option::None,
provides: ::std::option::Option::None,
shareable: false,
override_from: ::std::option::Option::None,
visible: #visible,
inaccessible: false,
compute_complexity: #complexity,

View File

@ -16,6 +16,8 @@
- 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.
- The `override` directive is used to indicate that a field is now to be resolved by the current subgraph instead of the named subgraph.
## Entity lookup function
```rust

View File

@ -33,6 +33,7 @@ some simple fields, and use the `ComplexObject` macro to define some other field
| 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 |
| override_from | Mark the field as overriding a field currently present on another subgraph. It is used to migrate fields between subgraphs. | string | 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 |

View File

@ -17,21 +17,22 @@ Define a GraphQL interface
# 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 |
| inaccessible | Indicate that a field is not accessible from a supergraph when using Apollo Federation | bool | 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 |
| override_from | Mark the field as overriding a field currently present on another subgraph. It is used to migrate fields between subgraphs. | 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

View File

@ -37,6 +37,7 @@ All methods are converted to camelCase.
| 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 |
| override_from | Mark the field as overriding a field currently present on another subgraph. It is used to migrate fields between subgraphs. | string | 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 |

View File

@ -36,8 +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 |
| override_from | Mark the field as overriding a field currently present on another subgraph. It is used to migrate fields between subgraphs. | string | 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 |

View File

@ -180,6 +180,9 @@ impl Registry {
if field.inaccessible {
write!(sdl, " @inaccessible").ok();
}
if let Some(from) = field.override_from {
write!(sdl, " @override(from: \"{}\")", from).ok();
}
}
writeln!(sdl).ok();

View File

@ -169,6 +169,7 @@ pub struct MetaField {
pub visible: Option<MetaVisibleFn>,
pub shareable: bool,
pub inaccessible: bool,
pub override_from: Option<&'static str>,
pub compute_complexity: Option<ComplexityType>,
}
@ -634,6 +635,7 @@ impl Registry {
provides: None,
shareable: false,
inaccessible: false,
override_from: None,
visible: None,
compute_complexity: None,
},
@ -684,6 +686,7 @@ impl Registry {
shareable: false,
visible: None,
inaccessible: false,
override_from: None,
compute_complexity: None,
},
);
@ -716,6 +719,7 @@ impl Registry {
shareable: false,
visible: None,
inaccessible: false,
override_from: None,
compute_complexity: None,
},
);

View File

@ -135,6 +135,7 @@ impl<T: ObjectType> OutputType for QueryRoot<T> {
inaccessible: false,
visible: None,
compute_complexity: None,
override_from: None,
},
);
@ -167,6 +168,7 @@ impl<T: ObjectType> OutputType for QueryRoot<T> {
provides: None,
shareable: false,
inaccessible: false,
override_from: None,
visible: None,
compute_complexity: None,
},

View File

@ -328,6 +328,32 @@ pub async fn test_entity_shareable() {
);
}
#[tokio::test]
pub async fn test_field_override_directive() {
#[derive(SimpleObject)]
struct MyObjFieldOverride {
#[graphql(override_from = "AnotherSubgraph")]
field_override_a: i32,
}
struct Query;
#[Object(extends)]
impl Query {
#[graphql(entity)]
async fn find_obj_field_override(&self, _id: i32) -> MyObjFieldOverride {
todo!()
}
}
let schema_sdl = Schema::new(Query, EmptyMutation, EmptySubscription)
.sdl_with_options(SDLExportOptions::new().federation());
assert_eq!(
schema_sdl.contains("fieldOverrideA: Int! @override(from: \"AnotherSubgraph\")"),
true
);
}
#[tokio::test]
pub async fn test_entity_inaccessible() {
struct MyCustomObjInaccessible;