-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add support for @composeDirective #253
base: main
Are you sure you want to change the base?
Changes from 5 commits
c8f5259
2f19d5e
66d060d
15cfca6
ede329b
96ec2ca
d373b7a
0701bff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
module ApolloFederation | ||
module Schema | ||
IMPORTED_DIRECTIVES = ['inaccessible', 'tag'].freeze | ||
IMPORTED_DIRECTIVES_V2_1 = ['composeDirective'].freeze | ||
|
||
def self.included(klass) | ||
klass.extend(CommonMethods) | ||
|
@@ -16,9 +17,16 @@ def self.included(klass) | |
module CommonMethods | ||
DEFAULT_LINK_NAMESPACE = 'federation' | ||
|
||
def federation(version: '1.0', link: {}) | ||
def federation(version: '1.0', default_link_namespace: nil, links: [], compose_directives: []) | ||
@federation_version = version | ||
@link = { as: DEFAULT_LINK_NAMESPACE }.merge(link) | ||
@default_link_namespace = default_link_namespace | ||
@links = links | ||
|
||
if !federation_2_1? && compose_directives.any? | ||
raise ArgumentError, 'composeDirective is available in Federation 2.1 and later' | ||
end | ||
|
||
@compose_directives = compose_directives | ||
end | ||
|
||
def federation_version | ||
|
@@ -29,6 +37,10 @@ def federation_2? | |
Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.0.0') | ||
end | ||
|
||
def federation_2_1? | ||
Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.1.0') | ||
end | ||
|
||
def federation_sdl(context: nil) | ||
document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context) | ||
|
||
|
@@ -37,8 +49,8 @@ def federation_sdl(context: nil) | |
output | ||
end | ||
|
||
def link_namespace | ||
@link ? @link[:as] : find_inherited_value(:link_namespace) | ||
def default_link_namespace | ||
@default_link_namespace || find_inherited_value(:default_link_namespace, DEFAULT_LINK_NAMESPACE) | ||
end | ||
|
||
def query(new_query_object = nil) | ||
|
@@ -62,14 +74,44 @@ def original_query | |
@orig_query_object || find_inherited_value(:original_query) | ||
end | ||
|
||
def compose_directives | ||
@compose_directives || find_inherited_value(:compose_directives, []) | ||
end | ||
|
||
def links | ||
@links || find_inherited_value(:links, []) | ||
end | ||
|
||
def all_links | ||
imported_directives = IMPORTED_DIRECTIVES | ||
imported_directives += IMPORTED_DIRECTIVES_V2_1 if federation_2_1? | ||
default_link = { | ||
url: 'https://specs.apollo.dev/federation/v2.3', | ||
import: imported_directives, | ||
} | ||
default_link[:as] = default_link_namespace if default_link_namespace != DEFAULT_LINK_NAMESPACE | ||
[default_link, *links] | ||
end | ||
|
||
def federation_2_prefix | ||
federation_namespace = ", as: \"#{link_namespace}\"" if link_namespace != DEFAULT_LINK_NAMESPACE | ||
schema = ['extend schema'] | ||
|
||
all_links.each do |link| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This printing logic is fairly complex. Can we make it easier to read? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried to do make it easier to read but not a ruby expert, please tell me if there's a better way :) |
||
link_str = " @link(url: \"#{link[:url]}\"" | ||
link_str += ", as: \"#{link[:as]}\"" if link[:as] | ||
link_str += ", import: [#{link[:import].map { |d| "\"@#{d}\"" }.join(', ')}]" if link[:import] | ||
link_str += ')' | ||
schema << link_str | ||
end | ||
|
||
compose_directives.each do |directive| | ||
schema << " @composeDirective(name: \"@#{directive}\")" | ||
end | ||
|
||
<<~SCHEMA | ||
extend schema | ||
@link(url: "https://specs.apollo.dev/federation/v2.3"#{federation_namespace}, import: [#{(IMPORTED_DIRECTIVES.map { |directive| "\"@#{directive}\"" }).join(', ')}]) | ||
schema << '' | ||
schema << '' | ||
|
||
SCHEMA | ||
schema.join("\n") | ||
end | ||
|
||
def schema_entities | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -128,6 +128,98 @@ def execute_sdl(schema) | |
) | ||
end | ||
|
||
it 'returns the federation SDL with multiple links for the schema' do | ||
product = Class.new(base_object) do | ||
graphql_name 'Product' | ||
|
||
field :upc, String, null: false | ||
end | ||
|
||
query_obj = Class.new(base_object) do | ||
graphql_name 'Query' | ||
|
||
field :product, product, null: true | ||
end | ||
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.3', | ||
links: [{ | ||
grxy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
url: 'https://specs.example.com/federation/v2.3', | ||
import: ['test'], | ||
}] | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
<<~GRAPHQL, | ||
extend schema | ||
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) | ||
@link(url: "https://specs.example.com/federation/v2.3", import: ["@test"]) | ||
|
||
type Product { | ||
upc: String! | ||
} | ||
|
||
type Query { | ||
product: Product | ||
} | ||
GRAPHQL | ||
) | ||
end | ||
|
||
it 'returns the federation SDL with compose directives for the schema' do | ||
complexity_directive = Class.new(GraphQL::Schema::Directive) do | ||
graphql_name 'complexity' | ||
argument :fixed, Integer, required: true | ||
description 'complexity of the field' | ||
locations GraphQL::Schema::Directive::FIELD_DEFINITION | ||
end | ||
|
||
product = Class.new(base_object) do | ||
graphql_name 'Product' | ||
|
||
field :upc, String, null: false | ||
end | ||
|
||
query_obj = Class.new(base_object) do | ||
graphql_name 'Query' | ||
|
||
field :product, product, null: true, directives: { complexity_directive => { fixed: 1 } } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is always going to fail on older GraphQL ruby versions. I think we have a couple options potentially:
I lean towards the first option. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the first option as well. Do you plan to drop support for GraphQL Ruby < 1.12 in a separate PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @utay Yeah we'd probably want to do it in a separate PR and publish a new major version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @grxy Any plan to do that in the near future? It was the main blocker here I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @utay I'll have to defer to the folks who have taken over maintenance on this gem. CC: @sethc2 @slauppy @sofie-c @simoncoffin |
||
end | ||
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.3', | ||
links: [{ | ||
url: 'https://specs.example.com/federation/v2.3', | ||
import: [complexity_directive.graphql_name], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one probably doesn't need importing with an @link since it is being defined inline. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like even if it's being defined inline it needs to be referenced via a custom
In the docs they're doing it similarly |
||
}], | ||
compose_directives: [complexity_directive.graphql_name] | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
<<~GRAPHQL, | ||
extend schema | ||
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) | ||
@link(url: "https://specs.example.com/federation/v2.3", import: ["@complexity"]) | ||
@composeDirective(name: "@complexity") | ||
|
||
""" | ||
complexity of the field | ||
""" | ||
directive @complexity(fixed: Int!) on FIELD_DEFINITION | ||
|
||
type Product { | ||
upc: String! | ||
} | ||
|
||
type Query { | ||
product: Product @complexity(fixed: 1) | ||
} | ||
GRAPHQL | ||
) | ||
end | ||
|
||
it 'returns valid SDL for type extensions' do | ||
product = Class.new(base_object) do | ||
graphql_name 'Product' | ||
|
@@ -291,7 +383,7 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
|
@@ -327,7 +419,7 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
|
@@ -364,7 +456,7 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
|
@@ -401,7 +493,7 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
|
@@ -437,7 +529,7 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
query query_obj | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
|
@@ -468,7 +560,7 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
orphan_types product | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
|
@@ -495,13 +587,13 @@ def execute_sdl(schema) | |
|
||
schema = Class.new(base_schema) do | ||
orphan_types product | ||
federation version: '2.3', link: { as: 'fed2' } | ||
federation version: '2.3', default_link_namespace: 'fed2' | ||
end | ||
|
||
expect(execute_sdl(schema)).to match_sdl( | ||
<<~GRAPHQL, | ||
extend schema | ||
@link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag"]) | ||
@link(url: "https://specs.apollo.dev/federation/v2.3", as: "fed2", import: ["@inaccessible", "@tag", "@composeDirective"]) | ||
|
||
type Product @fed2__interfaceObject @fed2__key(fields: "id") { | ||
id: ID! | ||
|
@@ -1421,7 +1513,7 @@ def execute_sdl(schema) | |
expect(execute_sdl(schema)).to match_sdl( | ||
<<~GRAPHQL, | ||
extend schema | ||
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag"]) | ||
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@inaccessible", "@tag", "@composeDirective"]) | ||
|
||
type Product @federation__interfaceObject @federation__key(fields: "id") { | ||
id: ID! | ||
|
@@ -1973,7 +2065,7 @@ def self.visible?(context) | |
end | ||
|
||
new_base_schema = Class.new(base_schema) do | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
end | ||
|
||
schema = Class.new(new_base_schema) do | ||
|
@@ -2011,7 +2103,7 @@ def self.visible?(context) | |
end | ||
|
||
new_base_schema = Class.new(base_schema) do | ||
federation version: '2.0', link: { as: 'fed2' } | ||
federation version: '2.0', default_link_namespace: 'fed2' | ||
query query_obj | ||
end | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should add specific minor version support. We do have some cleanup to do, but it's going to be easier to maintain this lib if we simply aim to support the latest version of federation instead of incrementally supporting minor versions of 2.x. See this issue for more context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks for the context. I removed this check, mutualized imported directives and fixed tests. I'll wait your call on how we plan to clean up versions