Skip to content

Commit 1957da0

Browse files
authored
feat: Add support for interpreter runtime (#65)
* First crack at supporting the new interpreter * Refactor to avoid referencing Schema instance vars * Fix rubocop errors * 🎨 Cleanup * Fix compatibility with graphql-ruby 1.9 * FIx rubocop error
1 parent 615f93b commit 1957da0

File tree

9 files changed

+637
-529
lines changed

9 files changed

+637
-529
lines changed

example/accounts.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ def me
4848
end
4949

5050
class AccountSchema < GraphQL::Schema
51+
use GraphQL::Execution::Interpreter
52+
use GraphQL::Analysis::AST
5153
include ApolloFederation::Schema
5254

5355
query(Query)

example/inventory.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def shipping_estimate
4242
end
4343

4444
class InventorySchema < GraphQL::Schema
45+
use GraphQL::Execution::Interpreter
46+
use GraphQL::Analysis::AST
4547
include ApolloFederation::Schema
4648

4749
orphan_types Product

lib/apollo-federation/entities_field.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ def self.included(base)
1313
module ClassMethods
1414
extend GraphQL::Schema::Member::HasFields
1515

16-
def define_entities_field(entity_type)
16+
def define_entities_field(possible_entities)
17+
# If there are any "entities", define the Entity union and and the Query._entities field
18+
return if possible_entities.empty?
19+
20+
entity_type = Class.new(Entity) do
21+
possible_types(*possible_entities)
22+
end
23+
1724
field(:_entities, [entity_type, null: true], null: false) do
1825
argument :representations, [Any], required: true
1926
extension(EntityTypeResolutionExtension)
@@ -32,8 +39,8 @@ def _entities(representations:)
3239
' but no object type of that name was found in the schema'
3340
end
3441

35-
# TODO: Handle non-class types?
36-
type_class = type.metadata[:type_class]
42+
# TODO: What if the type is an interface?
43+
type_class = type.is_a?(GraphQL::ObjectType) ? type.metadata[:type_class] : type
3744
if type_class.respond_to?(:resolve_reference)
3845
result = type_class.resolve_reference(reference, context)
3946
else

lib/apollo-federation/federated_document_from_schema_definition.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ def build_object_type_node(object_type)
2323
end
2424
federation_fields.each { |field| object_node = object_node.delete_child(field) }
2525
end
26-
merge_directives(object_node, object_type.metadata[:federation_directives])
26+
merge_directives(object_node, object_type)
2727
end
2828

2929
def build_interface_type_node(interface_type)
3030
field_node = super
31-
merge_directives(field_node, interface_type.metadata[:federation_directives])
31+
merge_directives(field_node, interface_type)
3232
end
3333

3434
def build_field_node(field_type)
3535
field_node = super
36-
merge_directives(field_node, field_type.metadata[:federation_directives])
36+
merge_directives(field_node, field_type)
3737
end
3838

3939
def build_type_definition_nodes(types)
@@ -53,7 +53,15 @@ def query_type?(type)
5353
type == warden.root_type_for_operation('query')
5454
end
5555

56-
def merge_directives(node, directives)
56+
def merge_directives(node, type)
57+
if type.is_a?(ApolloFederation::HasDirectives)
58+
directives = type.federation_directives
59+
elsif type.is_a?(GraphQL::Define::InstanceDefinable)
60+
directives = type.metadata[:federation_directives]
61+
else
62+
directives = []
63+
end
64+
5765
(directives || []).each do |directive|
5866
node = node.merge_directive(
5967
name: directive[:name],

lib/apollo-federation/has_directives.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
module ApolloFederation
44
module HasDirectives
5+
attr_reader :federation_directives
6+
57
def add_directive(name:, arguments: nil)
68
@federation_directives ||= []
79
@federation_directives << { name: name, arguments: arguments }

lib/apollo-federation/schema.rb

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,94 @@
88
module ApolloFederation
99
module Schema
1010
def self.included(klass)
11-
klass.extend(ClassMethods)
11+
if Gem::Version.new(GraphQL::VERSION) >= Gem::Version.new('1.10.0')
12+
klass.extend(OneTenMethods)
13+
else
14+
klass.extend(OneNineMethods)
15+
end
16+
end
17+
18+
module CommonMethods
19+
def federation_sdl(context: nil)
20+
document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context)
21+
GraphQL::Language::Printer.new.print(document_from_schema.document)
22+
end
23+
24+
private
25+
26+
def federation_query(query_obj)
27+
# Build the new query object with the '_service' field
28+
if query_obj.nil?
29+
base = GraphQL::Schema::Object
30+
elsif Gem::Version.new(GraphQL::VERSION) >= Gem::Version.new('1.10.0')
31+
base = query_obj
32+
else
33+
base = query_obj.metadata[:type_class]
34+
end
35+
36+
Class.new(base) do
37+
# TODO: Maybe the name should inherit from the original Query name
38+
# Or MAYBE we should just modify the original class?
39+
graphql_name 'Query'
40+
41+
include EntitiesField
42+
include ServiceField
43+
end
44+
end
1245
end
1346

14-
module ClassMethods
47+
# TODO: Remove these once we drop support for graphql 1.9
48+
module OneNineMethods
49+
include CommonMethods
50+
1551
def to_graphql
1652
orig_defn = super
53+
@query_object = federation_query(query)
1754

1855
possible_entities = orig_defn.types.values.select do |type|
1956
!type.introspection? && !type.default_scalar? && type.is_a?(GraphQL::ObjectType) &&
2057
type.metadata[:federation_directives]&.any? { |directive| directive[:name] == 'key' }
2158
end
22-
23-
@query_object = federation_query
24-
25-
if !possible_entities.empty?
26-
entity_type = Class.new(Entity) do
27-
possible_types(*possible_entities)
28-
end
29-
# TODO: Should/can we encapsulate all of this inside the module? What's the best/most Ruby
30-
# way to split this out?
31-
@query_object.define_entities_field(entity_type)
32-
end
59+
@query_object.define_entities_field(possible_entities)
3360

3461
super
3562
end
63+
end
3664

37-
def federation_query
38-
if query.nil?
39-
base = GraphQL::Schema::Object
40-
elsif Gem::Version.new(GraphQL::VERSION) >= Gem::Version.new('1.10.0')
41-
base = query
65+
module OneTenMethods
66+
include CommonMethods
67+
68+
def query(new_query_object = nil)
69+
if new_query_object
70+
@orig_query_object = new_query_object
4271
else
43-
base = query.metadata[:type_class]
44-
end
72+
if !@federation_query_object
73+
@federation_query_object = federation_query(@orig_query_object)
74+
@federation_query_object.define_entities_field(schema_entities)
4575

46-
Class.new(base) do
47-
graphql_name 'Query'
76+
super(@federation_query_object)
77+
end
4878

49-
include EntitiesField
50-
include ServiceField
79+
super
5180
end
5281
end
5382

54-
def federation_sdl(context: nil)
55-
document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context)
56-
GraphQL::Language::Printer.new.print(document_from_schema.document)
83+
private
84+
85+
def schema_entities
86+
# Create a temporary schema that inherits from this one to extract the types
87+
types_schema = Class.new(self)
88+
# Add the original query objects to the types. We have to use orphan_types here to avoid
89+
# infinite recursion
90+
types_schema.orphan_types(@orig_query_object)
91+
92+
# Walk through all of the types and determine which ones are entities (any type with a
93+
# "key" directive)
94+
types_schema.types.values.select do |type|
95+
# TODO: Interfaces can have a key...
96+
!type.introspection? && type.include?(ApolloFederation::Object) &&
97+
type.federation_directives&.any? { |directive| directive[:name] == 'key' }
98+
end
5799
end
58100
end
59101
end

lib/apollo-federation/service_field.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ module ServiceField
1010
field(:_service, Service, null: false)
1111

1212
def _service
13-
{ sdl: context.schema.class.federation_sdl(context: context) }
13+
schema_class = context.schema.is_a?(GraphQL::Schema) ? context.schema.class : context.schema
14+
{ sdl: schema_class.federation_sdl(context: context) }
1415
end
1516
end
1617
end

0 commit comments

Comments
 (0)