graphql-stitching 1.6.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql/stitching/{supergraph/resolver_directive.rb → composer/supergraph_directives.rb} +16 -1
- data/lib/graphql/stitching/composer.rb +83 -8
- data/lib/graphql/stitching/supergraph/from_definition.rb +82 -0
- data/lib/graphql/stitching/supergraph.rb +15 -15
- data/lib/graphql/stitching/type_resolver.rb +4 -0
- data/lib/graphql/stitching/version.rb +1 -1
- data/lib/graphql/stitching.rb +0 -6
- metadata +4 -6
- data/lib/graphql/stitching/supergraph/key_directive.rb +0 -13
- data/lib/graphql/stitching/supergraph/source_directive.rb +0 -12
- data/lib/graphql/stitching/supergraph/to_definition.rb +0 -164
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6b2cc734796d7455701bcc5b376a8efb49534c43b7a75de53bc1f2e686bf54c
|
4
|
+
data.tar.gz: 449c09b94257de6ae720b4b7938c13b8881da9f1f8dc7684aabdc4c493b2c6d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94345a14cc9bcee462854188b543a170b3dbd65cc948e55773e1a07357f47026f41018ea35eedbacc8647d8a92634be1f143d86543f0ea092f48ddb1281a86f5
|
7
|
+
data.tar.gz: ae7cb67b6f36ca209f327d43bd293fa6afc4e6a93e8e91e83f4acf2893b744831836959402cd236e789ade5602857bb39d484025ea58c7e16d19b9ec5eb69d67
|
data/lib/graphql/stitching/{supergraph/resolver_directive.rb → composer/supergraph_directives.rb}
RENAMED
@@ -1,7 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module GraphQL::Stitching
|
4
|
-
class
|
4
|
+
class Composer
|
5
|
+
class KeyDirective < GraphQL::Schema::Directive
|
6
|
+
graphql_name "key"
|
7
|
+
locations OBJECT, INTERFACE, UNION
|
8
|
+
argument :key, String, required: true
|
9
|
+
argument :location, String, required: true
|
10
|
+
repeatable true
|
11
|
+
end
|
12
|
+
|
5
13
|
class ResolverDirective < GraphQL::Schema::Directive
|
6
14
|
graphql_name "resolver"
|
7
15
|
locations OBJECT, INTERFACE, UNION
|
@@ -14,5 +22,12 @@ module GraphQL::Stitching
|
|
14
22
|
argument :type_name, String, required: false
|
15
23
|
repeatable true
|
16
24
|
end
|
25
|
+
|
26
|
+
class SourceDirective < GraphQL::Schema::Directive
|
27
|
+
graphql_name "source"
|
28
|
+
locations FIELD_DEFINITION
|
29
|
+
argument :location, String, required: true
|
30
|
+
repeatable true
|
31
|
+
end
|
17
32
|
end
|
18
33
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "composer/base_validator"
|
4
|
+
require_relative "composer/supergraph_directives"
|
4
5
|
require_relative "composer/validate_interfaces"
|
5
6
|
require_relative "composer/validate_type_resolvers"
|
6
7
|
require_relative "composer/type_resolver_config"
|
@@ -82,9 +83,16 @@ module GraphQL
|
|
82
83
|
|
83
84
|
schemas, executables = prepare_locations_input(locations_input)
|
84
85
|
|
86
|
+
directives_to_omit = [
|
87
|
+
GraphQL::Stitching.stitch_directive,
|
88
|
+
KeyDirective.graphql_name,
|
89
|
+
ResolverDirective.graphql_name,
|
90
|
+
SourceDirective.graphql_name,
|
91
|
+
]
|
92
|
+
|
85
93
|
# "directive_name" => "location" => subgraph_directive
|
86
94
|
@subgraph_directives_by_name_and_location = schemas.each_with_object({}) do |(location, schema), memo|
|
87
|
-
(schema.directives.keys - schema.default_directives.keys -
|
95
|
+
(schema.directives.keys - schema.default_directives.keys - directives_to_omit).each do |directive_name|
|
88
96
|
memo[directive_name] ||= {}
|
89
97
|
memo[directive_name][location] = schema.directives[directive_name]
|
90
98
|
end
|
@@ -154,25 +162,26 @@ module GraphQL
|
|
154
162
|
|
155
163
|
builder = self
|
156
164
|
schema = Class.new(GraphQL::Schema) do
|
165
|
+
object_types = schema_types.values.select { |t| t.respond_to?(:kind) && t.kind.object? }
|
157
166
|
add_type_and_traverse(schema_types.values, root: false)
|
158
|
-
orphan_types(
|
167
|
+
orphan_types(object_types)
|
159
168
|
query schema_types[builder.query_name]
|
160
169
|
mutation schema_types[builder.mutation_name]
|
161
170
|
subscription schema_types[builder.subscription_name]
|
162
171
|
directives builder.schema_directives.values
|
163
172
|
|
173
|
+
object_types.each do |t|
|
174
|
+
t.interfaces.each { _1.orphan_types(t) }
|
175
|
+
end
|
176
|
+
|
164
177
|
own_orphan_types.clear
|
165
178
|
end
|
166
179
|
|
167
180
|
select_root_field_locations(schema)
|
168
181
|
expand_abstract_resolvers(schema, schemas)
|
182
|
+
apply_supergraph_directives(schema, @resolver_map, @field_map)
|
169
183
|
|
170
|
-
supergraph = Supergraph.
|
171
|
-
schema: schema,
|
172
|
-
fields: @field_map,
|
173
|
-
resolvers: @resolver_map,
|
174
|
-
executables: executables,
|
175
|
-
)
|
184
|
+
supergraph = Supergraph.from_definition(schema, executables: executables)
|
176
185
|
|
177
186
|
COMPOSITION_VALIDATORS.each do |validator_class|
|
178
187
|
validator_class.new.perform(supergraph, self)
|
@@ -670,6 +679,72 @@ module GraphQL
|
|
670
679
|
memo[enum_name] << :write
|
671
680
|
end
|
672
681
|
end
|
682
|
+
|
683
|
+
def apply_supergraph_directives(schema, resolvers_by_type_name, locations_by_type_and_field)
|
684
|
+
schema_directives = {}
|
685
|
+
schema.types.each do |type_name, type|
|
686
|
+
if resolvers_for_type = resolvers_by_type_name.dig(type_name)
|
687
|
+
# Apply key directives for each unique type/key/location
|
688
|
+
# (this allows keys to be composite selections and/or omitted from the supergraph schema)
|
689
|
+
keys_for_type = resolvers_for_type.each_with_object({}) do |resolver, memo|
|
690
|
+
memo[resolver.key.to_definition] ||= Set.new
|
691
|
+
memo[resolver.key.to_definition].merge(resolver.key.locations)
|
692
|
+
end
|
693
|
+
|
694
|
+
keys_for_type.each do |key, locations|
|
695
|
+
locations.each do |location|
|
696
|
+
schema_directives[KeyDirective.graphql_name] ||= KeyDirective
|
697
|
+
type.directive(KeyDirective, key: key, location: location)
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
# Apply resolver directives for each unique query resolver
|
702
|
+
resolvers_for_type.each do |resolver|
|
703
|
+
params = {
|
704
|
+
location: resolver.location,
|
705
|
+
field: resolver.field,
|
706
|
+
list: resolver.list? || nil,
|
707
|
+
key: resolver.key.to_definition,
|
708
|
+
arguments: resolver.arguments.map(&:to_definition).join(", "),
|
709
|
+
argument_types: resolver.arguments.map(&:to_type_definition).join(", "),
|
710
|
+
type_name: (resolver.type_name if resolver.type_name != type_name),
|
711
|
+
}
|
712
|
+
|
713
|
+
schema_directives[ResolverDirective.graphql_name] ||= ResolverDirective
|
714
|
+
type.directive(ResolverDirective, **params.tap(&:compact!))
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
next unless type.kind.fields? && !type.introspection?
|
719
|
+
|
720
|
+
type.fields.each do |field_name, field|
|
721
|
+
if field.owner != type
|
722
|
+
# make a local copy of fields inherited from an interface
|
723
|
+
# to assure that source attributions reflect the object, not the interface.
|
724
|
+
field = type.field(
|
725
|
+
field.graphql_name,
|
726
|
+
description: field.description,
|
727
|
+
deprecation_reason: field.deprecation_reason,
|
728
|
+
type: Util.unwrap_non_null(field.type),
|
729
|
+
null: !field.type.non_null?,
|
730
|
+
connection: false,
|
731
|
+
camelize: false,
|
732
|
+
)
|
733
|
+
end
|
734
|
+
|
735
|
+
locations_for_field = locations_by_type_and_field.dig(type_name, field_name)
|
736
|
+
next if locations_for_field.nil?
|
737
|
+
|
738
|
+
# Apply source directives to annotate the possible locations of each field
|
739
|
+
locations_for_field.each do |location|
|
740
|
+
schema_directives[SourceDirective.graphql_name] ||= SourceDirective
|
741
|
+
field.directive(SourceDirective, location: location)
|
742
|
+
end
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
746
|
+
schema_directives.each_value { |directive_class| schema.directive(directive_class) }
|
747
|
+
end
|
673
748
|
end
|
674
749
|
end
|
675
750
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL::Stitching
|
4
|
+
class Supergraph
|
5
|
+
class << self
|
6
|
+
def validate_executable!(location, executable)
|
7
|
+
return true if executable.is_a?(Class) && executable <= GraphQL::Schema
|
8
|
+
return true if executable && executable.respond_to?(:call)
|
9
|
+
raise StitchingError, "Invalid executable provided for location `#{location}`."
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_definition(schema, executables:)
|
13
|
+
schema = GraphQL::Schema.from_definition(schema) if schema.is_a?(String)
|
14
|
+
field_map = {}
|
15
|
+
resolver_map = {}
|
16
|
+
possible_locations = {}
|
17
|
+
|
18
|
+
schema.types.each do |type_name, type|
|
19
|
+
next if type.introspection?
|
20
|
+
|
21
|
+
# Collect/build key definitions for each type
|
22
|
+
locations_by_key = type.directives.each_with_object({}) do |directive, memo|
|
23
|
+
next unless directive.graphql_name == Composer::KeyDirective.graphql_name
|
24
|
+
|
25
|
+
kwargs = directive.arguments.keyword_arguments
|
26
|
+
memo[kwargs[:key]] ||= []
|
27
|
+
memo[kwargs[:key]] << kwargs[:location]
|
28
|
+
end
|
29
|
+
|
30
|
+
key_definitions = locations_by_key.each_with_object({}) do |(key, locations), memo|
|
31
|
+
memo[key] = TypeResolver.parse_key(key, locations)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Collect/build resolver definitions for each type
|
35
|
+
type.directives.each do |directive|
|
36
|
+
next unless directive.graphql_name == Composer::ResolverDirective.graphql_name
|
37
|
+
|
38
|
+
kwargs = directive.arguments.keyword_arguments
|
39
|
+
resolver_map[type_name] ||= []
|
40
|
+
resolver_map[type_name] << TypeResolver.new(
|
41
|
+
location: kwargs[:location],
|
42
|
+
type_name: kwargs.fetch(:type_name, type_name),
|
43
|
+
field: kwargs[:field],
|
44
|
+
list: kwargs[:list] || false,
|
45
|
+
key: key_definitions[kwargs[:key]],
|
46
|
+
arguments: TypeResolver.parse_arguments_with_type_defs(kwargs[:arguments], kwargs[:argument_types]),
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
next unless type.kind.fields?
|
51
|
+
|
52
|
+
type.fields.each do |field_name, field|
|
53
|
+
# Collection locations for each field definition
|
54
|
+
field.directives.each do |d|
|
55
|
+
next unless d.graphql_name == Composer::SourceDirective.graphql_name
|
56
|
+
|
57
|
+
location = d.arguments.keyword_arguments[:location]
|
58
|
+
field_map[type_name] ||= {}
|
59
|
+
field_map[type_name][field_name] ||= []
|
60
|
+
field_map[type_name][field_name] << location
|
61
|
+
possible_locations[location] = true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
executables = possible_locations.keys.each_with_object({}) do |location, memo|
|
67
|
+
executable = executables[location] || executables[location.to_sym]
|
68
|
+
if validate_executable!(location, executable)
|
69
|
+
memo[location] = executable
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
new(
|
74
|
+
schema: schema,
|
75
|
+
fields: field_map,
|
76
|
+
resolvers: resolver_map,
|
77
|
+
executables: executables,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "supergraph/
|
3
|
+
require_relative "supergraph/from_definition"
|
4
4
|
|
5
5
|
module GraphQL
|
6
6
|
module Stitching
|
@@ -16,7 +16,10 @@ module GraphQL
|
|
16
16
|
# @return [Hash<String, Executable>] a map of executable resources by location.
|
17
17
|
attr_reader :executables
|
18
18
|
|
19
|
-
attr_reader :resolvers
|
19
|
+
attr_reader :resolvers
|
20
|
+
attr_reader :memoized_schema_types
|
21
|
+
attr_reader :memoized_introspection_types
|
22
|
+
attr_reader :locations_by_type_and_field
|
20
23
|
|
21
24
|
def initialize(schema:, fields: {}, resolvers: {}, executables: {})
|
22
25
|
@schema = schema
|
@@ -24,15 +27,14 @@ module GraphQL
|
|
24
27
|
@resolvers_by_version = nil
|
25
28
|
@fields_by_type_and_location = nil
|
26
29
|
@locations_by_type = nil
|
27
|
-
@memoized_introspection_types =
|
30
|
+
@memoized_introspection_types = @schema.introspection_system.types
|
31
|
+
@memoized_schema_types = @schema.types
|
28
32
|
@memoized_schema_fields = {}
|
29
|
-
@memoized_schema_types = nil
|
30
33
|
@possible_keys_by_type = {}
|
31
34
|
@possible_keys_by_type_and_location = {}
|
32
|
-
@static_validator = nil
|
33
35
|
|
34
36
|
# add introspection types into the fields mapping
|
35
|
-
@locations_by_type_and_field = memoized_introspection_types.each_with_object(fields) do |(type_name, type), memo|
|
37
|
+
@locations_by_type_and_field = @memoized_introspection_types.each_with_object(fields) do |(type_name, type), memo|
|
36
38
|
next unless type.kind.fields?
|
37
39
|
|
38
40
|
memo[type_name] = type.fields.keys.each_with_object({}) do |field_name, m|
|
@@ -46,6 +48,12 @@ module GraphQL
|
|
46
48
|
memo[location.to_s] = executable
|
47
49
|
end
|
48
50
|
end.freeze
|
51
|
+
|
52
|
+
@schema.use(GraphQL::Schema::AlwaysVisible)
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_definition
|
56
|
+
@schema.to_definition
|
49
57
|
end
|
50
58
|
|
51
59
|
def resolvers_by_version
|
@@ -62,17 +70,9 @@ module GraphQL
|
|
62
70
|
@executables.keys.reject { _1 == SUPERGRAPH_LOCATION }
|
63
71
|
end
|
64
72
|
|
65
|
-
def memoized_introspection_types
|
66
|
-
@memoized_introspection_types ||= schema.introspection_system.types
|
67
|
-
end
|
68
|
-
|
69
|
-
def memoized_schema_types
|
70
|
-
@memoized_schema_types ||= @schema.types
|
71
|
-
end
|
72
|
-
|
73
73
|
def memoized_schema_fields(type_name)
|
74
74
|
@memoized_schema_fields[type_name] ||= begin
|
75
|
-
fields = memoized_schema_types[type_name].fields
|
75
|
+
fields = @memoized_schema_types[type_name].fields
|
76
76
|
@schema.introspection_system.dynamic_fields.each do |field|
|
77
77
|
fields[field.name] ||= field # adds __typename
|
78
78
|
end
|
data/lib/graphql/stitching.rb
CHANGED
@@ -49,12 +49,6 @@ module GraphQL
|
|
49
49
|
def stitch_directive
|
50
50
|
@stitch_directive ||= "stitch"
|
51
51
|
end
|
52
|
-
|
53
|
-
# Names of stitching directives to omit from the composed supergraph.
|
54
|
-
# @returns [Array<String>] list of stitching directive names.
|
55
|
-
def stitching_directive_names
|
56
|
-
[stitch_directive]
|
57
|
-
end
|
58
52
|
end
|
59
53
|
end
|
60
54
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-stitching
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.6.
|
4
|
+
version: 1.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg MacWilliam
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- lib/graphql/stitching/client.rb
|
162
162
|
- lib/graphql/stitching/composer.rb
|
163
163
|
- lib/graphql/stitching/composer/base_validator.rb
|
164
|
+
- lib/graphql/stitching/composer/supergraph_directives.rb
|
164
165
|
- lib/graphql/stitching/composer/type_resolver_config.rb
|
165
166
|
- lib/graphql/stitching/composer/validate_interfaces.rb
|
166
167
|
- lib/graphql/stitching/composer/validate_type_resolvers.rb
|
@@ -175,10 +176,7 @@ files:
|
|
175
176
|
- lib/graphql/stitching/request.rb
|
176
177
|
- lib/graphql/stitching/request/skip_include.rb
|
177
178
|
- lib/graphql/stitching/supergraph.rb
|
178
|
-
- lib/graphql/stitching/supergraph/
|
179
|
-
- lib/graphql/stitching/supergraph/resolver_directive.rb
|
180
|
-
- lib/graphql/stitching/supergraph/source_directive.rb
|
181
|
-
- lib/graphql/stitching/supergraph/to_definition.rb
|
179
|
+
- lib/graphql/stitching/supergraph/from_definition.rb
|
182
180
|
- lib/graphql/stitching/type_resolver.rb
|
183
181
|
- lib/graphql/stitching/type_resolver/arguments.rb
|
184
182
|
- lib/graphql/stitching/type_resolver/keys.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL::Stitching
|
4
|
-
class Supergraph
|
5
|
-
class KeyDirective < GraphQL::Schema::Directive
|
6
|
-
graphql_name "key"
|
7
|
-
locations OBJECT, INTERFACE, UNION
|
8
|
-
argument :key, String, required: true
|
9
|
-
argument :location, String, required: true
|
10
|
-
repeatable true
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL::Stitching
|
4
|
-
class Supergraph
|
5
|
-
class SourceDirective < GraphQL::Schema::Directive
|
6
|
-
graphql_name "source"
|
7
|
-
locations FIELD_DEFINITION
|
8
|
-
argument :location, String, required: true
|
9
|
-
repeatable true
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,164 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative "./key_directive"
|
3
|
-
require_relative "./resolver_directive"
|
4
|
-
require_relative "./source_directive"
|
5
|
-
|
6
|
-
module GraphQL::Stitching
|
7
|
-
class Supergraph
|
8
|
-
class << self
|
9
|
-
def validate_executable!(location, executable)
|
10
|
-
return true if executable.is_a?(Class) && executable <= GraphQL::Schema
|
11
|
-
return true if executable && executable.respond_to?(:call)
|
12
|
-
raise StitchingError, "Invalid executable provided for location `#{location}`."
|
13
|
-
end
|
14
|
-
|
15
|
-
def from_definition(schema, executables:)
|
16
|
-
schema = GraphQL::Schema.from_definition(schema) if schema.is_a?(String)
|
17
|
-
field_map = {}
|
18
|
-
resolver_map = {}
|
19
|
-
possible_locations = {}
|
20
|
-
|
21
|
-
schema.types.each do |type_name, type|
|
22
|
-
next if type.introspection?
|
23
|
-
|
24
|
-
# Collect/build key definitions for each type
|
25
|
-
locations_by_key = type.directives.each_with_object({}) do |directive, memo|
|
26
|
-
next unless directive.graphql_name == KeyDirective.graphql_name
|
27
|
-
|
28
|
-
kwargs = directive.arguments.keyword_arguments
|
29
|
-
memo[kwargs[:key]] ||= []
|
30
|
-
memo[kwargs[:key]] << kwargs[:location]
|
31
|
-
end
|
32
|
-
|
33
|
-
key_definitions = locations_by_key.each_with_object({}) do |(key, locations), memo|
|
34
|
-
memo[key] = TypeResolver.parse_key(key, locations)
|
35
|
-
end
|
36
|
-
|
37
|
-
# Collect/build resolver definitions for each type
|
38
|
-
type.directives.each do |directive|
|
39
|
-
next unless directive.graphql_name == ResolverDirective.graphql_name
|
40
|
-
|
41
|
-
kwargs = directive.arguments.keyword_arguments
|
42
|
-
resolver_map[type_name] ||= []
|
43
|
-
resolver_map[type_name] << TypeResolver.new(
|
44
|
-
location: kwargs[:location],
|
45
|
-
type_name: kwargs.fetch(:type_name, type_name),
|
46
|
-
field: kwargs[:field],
|
47
|
-
list: kwargs[:list] || false,
|
48
|
-
key: key_definitions[kwargs[:key]],
|
49
|
-
arguments: TypeResolver.parse_arguments_with_type_defs(kwargs[:arguments], kwargs[:argument_types]),
|
50
|
-
)
|
51
|
-
end
|
52
|
-
|
53
|
-
next unless type.kind.fields?
|
54
|
-
|
55
|
-
type.fields.each do |field_name, field|
|
56
|
-
# Collection locations for each field definition
|
57
|
-
field.directives.each do |d|
|
58
|
-
next unless d.graphql_name == SourceDirective.graphql_name
|
59
|
-
|
60
|
-
location = d.arguments.keyword_arguments[:location]
|
61
|
-
field_map[type_name] ||= {}
|
62
|
-
field_map[type_name][field_name] ||= []
|
63
|
-
field_map[type_name][field_name] << location
|
64
|
-
possible_locations[location] = true
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
executables = possible_locations.keys.each_with_object({}) do |location, memo|
|
70
|
-
executable = executables[location] || executables[location.to_sym]
|
71
|
-
if validate_executable!(location, executable)
|
72
|
-
memo[location] = executable
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
new(
|
77
|
-
schema: schema,
|
78
|
-
fields: field_map,
|
79
|
-
resolvers: resolver_map,
|
80
|
-
executables: executables,
|
81
|
-
)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def to_definition
|
86
|
-
if @schema.directives[KeyDirective.graphql_name].nil?
|
87
|
-
@schema.directive(KeyDirective)
|
88
|
-
end
|
89
|
-
if @schema.directives[ResolverDirective.graphql_name].nil?
|
90
|
-
@schema.directive(ResolverDirective)
|
91
|
-
end
|
92
|
-
if @schema.directives[SourceDirective.graphql_name].nil?
|
93
|
-
@schema.directive(SourceDirective)
|
94
|
-
end
|
95
|
-
|
96
|
-
@schema.types.each do |type_name, type|
|
97
|
-
if resolvers_for_type = @resolvers.dig(type_name)
|
98
|
-
# Apply key directives for each unique type/key/location
|
99
|
-
# (this allows keys to be composite selections and/or omitted from the supergraph schema)
|
100
|
-
keys_for_type = resolvers_for_type.each_with_object({}) do |resolver, memo|
|
101
|
-
memo[resolver.key.to_definition] ||= Set.new
|
102
|
-
memo[resolver.key.to_definition].merge(resolver.key.locations)
|
103
|
-
end
|
104
|
-
|
105
|
-
keys_for_type.each do |key, locations|
|
106
|
-
locations.each do |location|
|
107
|
-
params = { key: key, location: location }
|
108
|
-
|
109
|
-
unless has_directive?(type, KeyDirective.graphql_name, params)
|
110
|
-
type.directive(KeyDirective, **params)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Apply resolver directives for each unique query resolver
|
116
|
-
resolvers_for_type.each do |resolver|
|
117
|
-
params = {
|
118
|
-
location: resolver.location,
|
119
|
-
field: resolver.field,
|
120
|
-
list: resolver.list? || nil,
|
121
|
-
key: resolver.key.to_definition,
|
122
|
-
arguments: resolver.arguments.map(&:to_definition).join(", "),
|
123
|
-
argument_types: resolver.arguments.map(&:to_type_definition).join(", "),
|
124
|
-
type_name: (resolver.type_name if resolver.type_name != type_name),
|
125
|
-
}
|
126
|
-
|
127
|
-
unless has_directive?(type, ResolverDirective.graphql_name, params)
|
128
|
-
type.directive(ResolverDirective, **params.tap(&:compact!))
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
next unless type.kind.fields?
|
134
|
-
|
135
|
-
type.fields.each do |field_name, field|
|
136
|
-
locations_for_field = @locations_by_type_and_field.dig(type_name, field_name)
|
137
|
-
next if locations_for_field.nil?
|
138
|
-
|
139
|
-
# Apply source directives to annotate the possible locations of each field
|
140
|
-
locations_for_field.each do |location|
|
141
|
-
params = { location: location }
|
142
|
-
|
143
|
-
unless has_directive?(field, SourceDirective.graphql_name, params)
|
144
|
-
field.directive(SourceDirective, **params)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
@schema.to_definition
|
151
|
-
end
|
152
|
-
|
153
|
-
private
|
154
|
-
|
155
|
-
def has_directive?(element, directive_name, params)
|
156
|
-
existing = element.directives.find do |d|
|
157
|
-
kwargs = d.arguments.keyword_arguments
|
158
|
-
d.graphql_name == directive_name && params.all? { |k, v| kwargs[k] == v }
|
159
|
-
end
|
160
|
-
|
161
|
-
!existing.nil?
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|