Merge pull request #1060 from raptros/add-link-directive
implement a simple approach to using the link directive
This commit is contained in:
commit
d75d79769a
|
@ -20,6 +20,26 @@
|
||||||
|
|
||||||
- The `override` directive is used to indicate that a field is now to be resolved by the current subgraph instead of the named subgraph.
|
- The `override` directive is used to indicate that a field is now to be resolved by the current subgraph instead of the named subgraph.
|
||||||
|
|
||||||
|
- The `link` directive is needed in order to indicate that the subgraph is Federation v2 compatible, enabling the `shareable`, `inaccessable`, and `override` directives.
|
||||||
|
|
||||||
|
## Enabling Federation v2 using the link directive
|
||||||
|
|
||||||
|
async-graphql provides a configuration function `enable_apollo_fed2_link` on the schema builder to have it print out an `extend schema` element with an appropriately configured `link` directive whenever the federation schema is requested.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||||
|
.enable_apollo_fed2_link()
|
||||||
|
.finish()
|
||||||
|
```
|
||||||
|
|
||||||
|
and the following (or similar) will be attached to the schema:
|
||||||
|
```
|
||||||
|
extend schema @link(
|
||||||
|
url: "https://specs.apollo.dev/federation/v2.0",
|
||||||
|
import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Entity lookup function
|
## Entity lookup function
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
|
@ -111,7 +111,14 @@ impl Registry {
|
||||||
writeln!(sdl).ok();
|
writeln!(sdl).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.federation {
|
if options.federation {
|
||||||
|
if self.enable_apollo_link {
|
||||||
|
writeln!(sdl, "extend schema @link(").ok();
|
||||||
|
writeln!(sdl, "\turl: \"https://specs.apollo.dev/federation/v2.0\",").ok();
|
||||||
|
writeln!(sdl, "\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\"]").ok();
|
||||||
|
writeln!(sdl, ")").ok();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
writeln!(sdl, "schema {{").ok();
|
writeln!(sdl, "schema {{").ok();
|
||||||
writeln!(sdl, "\tquery: {}", self.query_type).ok();
|
writeln!(sdl, "\tquery: {}", self.query_type).ok();
|
||||||
if let Some(mutation_type) = self.mutation_type.as_deref() {
|
if let Some(mutation_type) = self.mutation_type.as_deref() {
|
||||||
|
|
|
@ -423,6 +423,7 @@ pub struct Registry {
|
||||||
pub subscription_type: Option<String>,
|
pub subscription_type: Option<String>,
|
||||||
pub introspection_mode: IntrospectionMode,
|
pub introspection_mode: IntrospectionMode,
|
||||||
pub enable_federation: bool,
|
pub enable_federation: bool,
|
||||||
|
pub enable_apollo_link: bool,
|
||||||
pub federation_subscription: bool,
|
pub federation_subscription: bool,
|
||||||
pub ignore_name_conflicts: HashSet<String>,
|
pub ignore_name_conflicts: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,6 +173,16 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enables printing the apollo federation 2 `@link` directive during
|
||||||
|
/// federation schema export; the directive is attached to an "extend
|
||||||
|
/// schema" element, and will have values set to ensure that
|
||||||
|
/// the federation schema directives and types are named properly.
|
||||||
|
#[must_use]
|
||||||
|
pub fn enable_apollo_fed2_link(mut self) -> Self {
|
||||||
|
self.registry.enable_apollo_link = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Make the Federation SDL include subscriptions.
|
/// Make the Federation SDL include subscriptions.
|
||||||
///
|
///
|
||||||
/// Note: Not included by default, in order to be compatible with Apollo
|
/// Note: Not included by default, in order to be compatible with Apollo
|
||||||
|
@ -379,6 +389,7 @@ where
|
||||||
},
|
},
|
||||||
introspection_mode: IntrospectionMode::Enabled,
|
introspection_mode: IntrospectionMode::Enabled,
|
||||||
enable_federation: false,
|
enable_federation: false,
|
||||||
|
enable_apollo_link: false,
|
||||||
federation_subscription: false,
|
federation_subscription: false,
|
||||||
ignore_name_conflicts,
|
ignore_name_conflicts,
|
||||||
};
|
};
|
||||||
|
|
|
@ -222,7 +222,7 @@ where
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
///
|
///
|
||||||
/// use async_graphql::*;
|
/// use async_graphql::*;
|
||||||
/// use async_graphql::types::connection::*;
|
/// use async_graphql::types::connection::*;
|
||||||
///
|
///
|
||||||
|
|
|
@ -538,6 +538,86 @@ pub async fn test_entity_inaccessible() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_link_directive() {
|
||||||
|
struct User {
|
||||||
|
id: ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object(extends)]
|
||||||
|
impl User {
|
||||||
|
#[graphql(external)]
|
||||||
|
async fn id(&self) -> &ID {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reviews(&self) -> Vec<Review> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Review;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Review {
|
||||||
|
async fn body(&self) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn author(&self) -> User {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn product(&self) -> Product {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Product {
|
||||||
|
upc: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Object(extends)]
|
||||||
|
impl Product {
|
||||||
|
#[graphql(external)]
|
||||||
|
async fn upc(&self) -> &str {
|
||||||
|
&self.upc
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reviews(&self) -> Vec<Review> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Query;
|
||||||
|
|
||||||
|
#[Object]
|
||||||
|
impl Query {
|
||||||
|
#[graphql(entity)]
|
||||||
|
async fn find_user_by_id(&self, id: ID) -> User {
|
||||||
|
User { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[graphql(entity)]
|
||||||
|
async fn find_product_by_upc(&self, upc: String) -> Product {
|
||||||
|
Product { upc }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let schema_sdl = Schema::build(Query, EmptyMutation, EmptySubscription)
|
||||||
|
.enable_apollo_fed2_link()
|
||||||
|
.finish()
|
||||||
|
.sdl_with_options(SDLExportOptions::new().federation());
|
||||||
|
|
||||||
|
let path = std::path::Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||||
|
.join("tests/schemas/test_fed2_link.schema.graphqls");
|
||||||
|
let expected_schema = std::fs::read_to_string(&path).unwrap();
|
||||||
|
if schema_sdl != expected_schema {
|
||||||
|
std::fs::write(path, schema_sdl).unwrap();
|
||||||
|
panic!("schema was not up-to-date. verify changes and re-run if correct.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
pub async fn test_entity_tag() {
|
pub async fn test_entity_tag() {
|
||||||
struct MyCustomObjTagged;
|
struct MyCustomObjTagged;
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extend type Product @key(fields: "upc") {
|
||||||
|
upc: String! @external
|
||||||
|
reviews: [Review!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Review {
|
||||||
|
body: String!
|
||||||
|
author: User!
|
||||||
|
product: Product!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extend type User @key(fields: "id") {
|
||||||
|
id: ID! @external
|
||||||
|
reviews: [Review!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend schema @link(
|
||||||
|
url: "https://specs.apollo.dev/federation/v2.0",
|
||||||
|
import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"]
|
||||||
|
)
|
Loading…
Reference in New Issue