From 3c6076a469416565d0ab853ff6c0c18feb5df4d7 Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Sun, 4 Sep 2022 11:04:12 -0500 Subject: [PATCH 1/7] implement a simple approach to using the link directive this is probably the simple way to get the link directive in place for federation 2 support - simply add a flag to the registry, and print out a hardcoded link directive configured for the current needs of async-graphql. --- src/registry/export_sdl.rs | 9 ++++++++- src/registry/mod.rs | 6 ++++++ src/schema.rs | 10 ++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/registry/export_sdl.rs b/src/registry/export_sdl.rs index 2adf2596..d8ad4f26 100644 --- a/src/registry/export_sdl.rs +++ b/src/registry/export_sdl.rs @@ -111,7 +111,14 @@ impl Registry { writeln!(sdl).ok(); } - if !options.federation { + if options.federation { + if self.enable_apollo_link { + writeln!("extend schema @link(").ok(); + writelin!("\turl: \"https://specs.apollo.dev/federation/v2.0\",").ok(); + writeln!("\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\"]").ok(); + writeln!(")").ok(); + } + } else { writeln!(sdl, "schema {{").ok(); writeln!(sdl, "\tquery: {}", self.query_type).ok(); if let Some(mutation_type) = self.mutation_type.as_deref() { diff --git a/src/registry/mod.rs b/src/registry/mod.rs index cf67b893..88abf48a 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -413,6 +413,11 @@ pub struct MetaDirective { pub visible: Option, } +#[derive(Debug, Clone)] +pub struct ApolloLinkConfig { + pub extend_schema: bool, +} + #[derive(Default)] pub struct Registry { pub types: BTreeMap, @@ -423,6 +428,7 @@ pub struct Registry { pub subscription_type: Option, pub introspection_mode: IntrospectionMode, pub enable_federation: bool, + pub enable_apollo_link: bool, pub federation_subscription: bool, pub ignore_name_conflicts: HashSet, } diff --git a/src/schema.rs b/src/schema.rs index 39769ae3..f17b6de0 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -30,6 +30,7 @@ use crate::{ InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError, ServerResult, SubscriptionType, Variables, ID, }; +use crate::registry::ApolloLinkConfig; /// Introspection mode #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -173,6 +174,15 @@ impl SchemaBuilder 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. /// /// Note: Not included by default, in order to be compatible with Apollo From 95b4e613b1d8ffab388e9e43a032f814c18f3d57 Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Sun, 4 Sep 2022 11:30:06 -0500 Subject: [PATCH 2/7] clean up some mistakes, ensure existing tests pass --- src/registry/export_sdl.rs | 8 ++++---- src/registry/mod.rs | 5 ----- src/schema.rs | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/registry/export_sdl.rs b/src/registry/export_sdl.rs index d8ad4f26..be38f43b 100644 --- a/src/registry/export_sdl.rs +++ b/src/registry/export_sdl.rs @@ -113,10 +113,10 @@ impl Registry { if options.federation { if self.enable_apollo_link { - writeln!("extend schema @link(").ok(); - writelin!("\turl: \"https://specs.apollo.dev/federation/v2.0\",").ok(); - writeln!("\timport: [\"@key\", \"@tag\", \"@shareable\", \"@inaccessible\", \"@override\", \"@external\", \"@provides\", \"@requires\"]").ok(); - writeln!(")").ok(); + 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(); diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 88abf48a..5a6e6f34 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -413,11 +413,6 @@ pub struct MetaDirective { pub visible: Option, } -#[derive(Debug, Clone)] -pub struct ApolloLinkConfig { - pub extend_schema: bool, -} - #[derive(Default)] pub struct Registry { pub types: BTreeMap, diff --git a/src/schema.rs b/src/schema.rs index f17b6de0..7c54689b 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -30,7 +30,6 @@ use crate::{ InputType, ObjectType, OutputType, QueryEnv, Request, Response, ServerError, ServerResult, SubscriptionType, Variables, ID, }; -use crate::registry::ApolloLinkConfig; /// Introspection mode #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -389,6 +388,7 @@ where }, introspection_mode: IntrospectionMode::Enabled, enable_federation: false, + enable_apollo_link: false, federation_subscription: false, ignore_name_conflicts, }; From fb5805a44ebebe6cec5a679599e076a864849d6d Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Mon, 19 Sep 2022 16:14:21 -0500 Subject: [PATCH 3/7] add a test for the printing of link directive --- tests/federation.rs | 80 ++++++++++++++++++++ tests/schemas/test_fed2_link.schema.graphqls | 26 +++++++ 2 files changed, 106 insertions(+) create mode 100644 tests/schemas/test_fed2_link.schema.graphqls diff --git a/tests/federation.rs b/tests/federation.rs index 8b36594c..0acfa38d 100644 --- a/tests/federation.rs +++ b/tests/federation.rs @@ -547,6 +547,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 { + 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 { + 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] pub async fn test_entity_tag() { struct MyCustomObjTagged; diff --git a/tests/schemas/test_fed2_link.schema.graphqls b/tests/schemas/test_fed2_link.schema.graphqls new file mode 100644 index 00000000..dfb4a959 --- /dev/null +++ b/tests/schemas/test_fed2_link.schema.graphqls @@ -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"] +) From 406c6906b73cbc852aaaf49a1b3b8e683c1ae0a8 Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Mon, 19 Sep 2022 16:19:29 -0500 Subject: [PATCH 4/7] re-format --- src/schema.rs | 5 +++-- src/types/connection/mod.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/schema.rs b/src/schema.rs index 7c54689b..adcac107 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -173,8 +173,9 @@ impl SchemaBuilder 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 + /// 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 { diff --git a/src/types/connection/mod.rs b/src/types/connection/mod.rs index ecb56466..04cf6a82 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::*; /// From 393184a23c5c8af19efc66a55c96dfda18560c8e Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Tue, 20 Sep 2022 08:12:54 -0500 Subject: [PATCH 5/7] default to printing the applicaiton of the link directive change to providing a method to opt-out of printing it. --- src/registry/export_sdl.rs | 2 +- src/registry/mod.rs | 2 +- src/schema.rs | 14 +++++++------- tests/federation.rs | 8 +++++--- .../test_entity_inaccessible.schema.graphql | 4 ++++ tests/schemas/test_entity_tag.schema.graphql | 4 ++++ ...schema.graphqls => test_suppress_link.graphqls} | 4 ---- 7 files changed, 22 insertions(+), 16 deletions(-) rename tests/schemas/{test_fed2_link.schema.graphqls => test_suppress_link.graphqls} (57%) diff --git a/src/registry/export_sdl.rs b/src/registry/export_sdl.rs index be38f43b..92561e10 100644 --- a/src/registry/export_sdl.rs +++ b/src/registry/export_sdl.rs @@ -112,7 +112,7 @@ impl Registry { } if options.federation { - if self.enable_apollo_link { + if !self.suppress_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(); diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 5a6e6f34..3d493b6a 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -423,7 +423,7 @@ pub struct Registry { pub subscription_type: Option, pub introspection_mode: IntrospectionMode, pub enable_federation: bool, - pub enable_apollo_link: bool, + pub suppress_apollo_link: bool, pub federation_subscription: bool, pub ignore_name_conflicts: HashSet, } diff --git a/src/schema.rs b/src/schema.rs index adcac107..6c6c9875 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -173,13 +173,13 @@ impl SchemaBuilder 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. + /// By default, schema export in the federation mode will now automatically + /// print out a `@link` directive attached to an `extend schema` + /// element. If you need to prevent this printing for any reason, you + /// can use this method to prevent that printing. #[must_use] - pub fn enable_apollo_fed2_link(mut self) -> Self { - self.registry.enable_apollo_link = true; + pub fn suppress_apollo_link(mut self) -> Self { + self.registry.suppress_apollo_link = true; self } @@ -389,7 +389,7 @@ where }, introspection_mode: IntrospectionMode::Enabled, enable_federation: false, - enable_apollo_link: false, + suppress_apollo_link: false, federation_subscription: false, ignore_name_conflicts, }; diff --git a/tests/federation.rs b/tests/federation.rs index 0acfa38d..624a98f6 100644 --- a/tests/federation.rs +++ b/tests/federation.rs @@ -547,8 +547,10 @@ pub async fn test_entity_inaccessible() { } } +// tests suppressing the link directive. note that test_entity_inaccessible and +// test_entity_tag now verify the default printing of the link directive. #[tokio::test] -pub async fn test_link_directive() { +pub async fn test_suppress_link_directive() { struct User { id: ID, } @@ -614,12 +616,12 @@ pub async fn test_link_directive() { } let schema_sdl = Schema::build(Query, EmptyMutation, EmptySubscription) - .enable_apollo_fed2_link() + .suppress_apollo_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"); + .join("tests/schemas/test_suppress_link.graphqls"); let expected_schema = std::fs::read_to_string(&path).unwrap(); if schema_sdl != expected_schema { std::fs::write(path, schema_sdl).unwrap(); diff --git a/tests/schemas/test_entity_inaccessible.schema.graphql b/tests/schemas/test_entity_inaccessible.schema.graphql index 7da5863f..93f8f71a 100644 --- a/tests/schemas/test_entity_inaccessible.schema.graphql +++ b/tests/schemas/test_entity_inaccessible.schema.graphql @@ -65,3 +65,7 @@ extend type Query { } +extend schema @link( + url: "https://specs.apollo.dev/federation/v2.0", + import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"] +) diff --git a/tests/schemas/test_entity_tag.schema.graphql b/tests/schemas/test_entity_tag.schema.graphql index d7bdf7aa..1106ac6b 100644 --- a/tests/schemas/test_entity_tag.schema.graphql +++ b/tests/schemas/test_entity_tag.schema.graphql @@ -65,3 +65,7 @@ extend type Query { } +extend schema @link( + url: "https://specs.apollo.dev/federation/v2.0", + import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"] +) diff --git a/tests/schemas/test_fed2_link.schema.graphqls b/tests/schemas/test_suppress_link.graphqls similarity index 57% rename from tests/schemas/test_fed2_link.schema.graphqls rename to tests/schemas/test_suppress_link.graphqls index dfb4a959..eb878e1b 100644 --- a/tests/schemas/test_fed2_link.schema.graphqls +++ b/tests/schemas/test_suppress_link.graphqls @@ -20,7 +20,3 @@ extend type User @key(fields: "id") { reviews: [Review!]! } -extend schema @link( - url: "https://specs.apollo.dev/federation/v2.0", - import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"] -) From 2803cf312886209f5c15949a31c837cb3aadc646 Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Thu, 22 Sep 2022 09:48:10 -0500 Subject: [PATCH 6/7] Revert "default to printing the applicaiton of the link directive" This reverts commit 393184a23c5c8af19efc66a55c96dfda18560c8e. --- src/registry/export_sdl.rs | 2 +- src/registry/mod.rs | 2 +- src/schema.rs | 14 +++++++------- tests/federation.rs | 8 +++----- .../test_entity_inaccessible.schema.graphql | 4 ---- tests/schemas/test_entity_tag.schema.graphql | 4 ---- ...ink.graphqls => test_fed2_link.schema.graphqls} | 4 ++++ 7 files changed, 16 insertions(+), 22 deletions(-) rename tests/schemas/{test_suppress_link.graphqls => test_fed2_link.schema.graphqls} (57%) diff --git a/src/registry/export_sdl.rs b/src/registry/export_sdl.rs index 92561e10..be38f43b 100644 --- a/src/registry/export_sdl.rs +++ b/src/registry/export_sdl.rs @@ -112,7 +112,7 @@ impl Registry { } if options.federation { - if !self.suppress_apollo_link { + 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(); diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 3d493b6a..5a6e6f34 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -423,7 +423,7 @@ pub struct Registry { pub subscription_type: Option, pub introspection_mode: IntrospectionMode, pub enable_federation: bool, - pub suppress_apollo_link: bool, + pub enable_apollo_link: bool, pub federation_subscription: bool, pub ignore_name_conflicts: HashSet, } diff --git a/src/schema.rs b/src/schema.rs index 6c6c9875..adcac107 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -173,13 +173,13 @@ impl SchemaBuilder self } - /// By default, schema export in the federation mode will now automatically - /// print out a `@link` directive attached to an `extend schema` - /// element. If you need to prevent this printing for any reason, you - /// can use this method to prevent that printing. + /// 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 suppress_apollo_link(mut self) -> Self { - self.registry.suppress_apollo_link = true; + pub fn enable_apollo_fed2_link(mut self) -> Self { + self.registry.enable_apollo_link = true; self } @@ -389,7 +389,7 @@ where }, introspection_mode: IntrospectionMode::Enabled, enable_federation: false, - suppress_apollo_link: false, + enable_apollo_link: false, federation_subscription: false, ignore_name_conflicts, }; diff --git a/tests/federation.rs b/tests/federation.rs index 624a98f6..0acfa38d 100644 --- a/tests/federation.rs +++ b/tests/federation.rs @@ -547,10 +547,8 @@ pub async fn test_entity_inaccessible() { } } -// tests suppressing the link directive. note that test_entity_inaccessible and -// test_entity_tag now verify the default printing of the link directive. #[tokio::test] -pub async fn test_suppress_link_directive() { +pub async fn test_link_directive() { struct User { id: ID, } @@ -616,12 +614,12 @@ pub async fn test_suppress_link_directive() { } let schema_sdl = Schema::build(Query, EmptyMutation, EmptySubscription) - .suppress_apollo_link() + .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_suppress_link.graphqls"); + .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(); diff --git a/tests/schemas/test_entity_inaccessible.schema.graphql b/tests/schemas/test_entity_inaccessible.schema.graphql index 93f8f71a..7da5863f 100644 --- a/tests/schemas/test_entity_inaccessible.schema.graphql +++ b/tests/schemas/test_entity_inaccessible.schema.graphql @@ -65,7 +65,3 @@ extend type Query { } -extend schema @link( - url: "https://specs.apollo.dev/federation/v2.0", - import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"] -) diff --git a/tests/schemas/test_entity_tag.schema.graphql b/tests/schemas/test_entity_tag.schema.graphql index 1106ac6b..d7bdf7aa 100644 --- a/tests/schemas/test_entity_tag.schema.graphql +++ b/tests/schemas/test_entity_tag.schema.graphql @@ -65,7 +65,3 @@ extend type Query { } -extend schema @link( - url: "https://specs.apollo.dev/federation/v2.0", - import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"] -) diff --git a/tests/schemas/test_suppress_link.graphqls b/tests/schemas/test_fed2_link.schema.graphqls similarity index 57% rename from tests/schemas/test_suppress_link.graphqls rename to tests/schemas/test_fed2_link.schema.graphqls index eb878e1b..dfb4a959 100644 --- a/tests/schemas/test_suppress_link.graphqls +++ b/tests/schemas/test_fed2_link.schema.graphqls @@ -20,3 +20,7 @@ extend type User @key(fields: "id") { reviews: [Review!]! } +extend schema @link( + url: "https://specs.apollo.dev/federation/v2.0", + import: ["@key", "@tag", "@shareable", "@inaccessible", "@override", "@external", "@provides", "@requires"] +) From a1534072d1cae1acc0d6e5b416fc496f3cd3d5c4 Mon Sep 17 00:00:00 2001 From: aidan coyne Date: Thu, 22 Sep 2022 09:59:01 -0500 Subject: [PATCH 7/7] add documentation about enabling link directive --- docs/en/src/apollo_federation.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/en/src/apollo_federation.md b/docs/en/src/apollo_federation.md index ac03643a..6f8b7f7d 100644 --- a/docs/en/src/apollo_federation.md +++ b/docs/en/src/apollo_federation.md @@ -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 `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 ```rust