graphql-stitching 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d90338dfde5c26515a55afdee7f5bd936a4563b2b8f83e1ffc1a4420c56a5cf
4
- data.tar.gz: d728c5dfabb5e0250c38a6718ac7b535cfa7a183872dc60fa95a9b91b777a64a
3
+ metadata.gz: 66c85693da3a07eb7b3ab81fe76b69ce095aaffa8b9cab68a1f34b86e20963ba
4
+ data.tar.gz: f1e8bbbe3ffdaf23189f219cad6fe604203527e12911a0609fb1711375effe58
5
5
  SHA512:
6
- metadata.gz: cc5ff912f354e08b9f204028d5e6eed85a81bf283bd5dc13bb7ee9b69b83d0bb39ba83f9fbaac66be1363e86ce3f6bd1b3f7cc4466ef83998a751e3dbccf9013
7
- data.tar.gz: 055e1c09f88e8f893a3ebb0aa3d7558156ec5c4a33b704cca69499efee4fd02e3a70e6efedeca1332f92392c7093ea8024ba9aefb159ce77b4f11510cd8d7a1c
6
+ metadata.gz: a86ac6f20dda8dc5ae68d548d85d9c4482856c56e86a07cee9ff5689ba95cf73f19dadd80d6f711a55a0b9e25796522b1b1feb4c7d70f7fa499a0f38beb0d24a
7
+ data.tar.gz: 61b2d6f21dc8001fab5e6d8c27d3cc04582fc506daa17f45354475cb9ef6812a355ec5819257d9d582d7f4a312b85b10b539322e3fa14b73a255376a75687658
data/docs/client.md CHANGED
@@ -25,11 +25,9 @@ client = GraphQL::Stitching::Client.new(locations: {
25
25
  Alternatively, you may pass a prebuilt `Supergraph` instance to the `Client` constructor. This is useful when [exporting and rehydrating](./supergraph.md#export-and-caching) supergraph instances, which bypasses the need for runtime composition:
26
26
 
27
27
  ```ruby
28
- exported_schema = "type Query { ..."
29
- exported_mapping = JSON.parse("{ ... }")
30
- supergraph = GraphQL::Stitching::Supergraph.from_export(
31
- schema: exported_schema,
32
- delegation_map: exported_mapping,
28
+ supergraph_sdl = File.read("precomposed_schema.graphql")
29
+ supergraph = GraphQL::Stitching::Supergraph.from_definition(
30
+ supergraph_sdl,
33
31
  executables: { ... },
34
32
  )
35
33
 
@@ -61,16 +59,16 @@ Arguments for the `execute` method include:
61
59
  The client provides cache hooks to enable caching query plans across requests. Without caching, every request made to the client will be planned individually. With caching, a query may be planned once, cached, and then executed from cache for subsequent requests. Cache keys are a normalized digest of each query string.
62
60
 
63
61
  ```ruby
64
- client.on_cache_read do |key, _context|
65
- $redis.get(key) # << 3P code
62
+ client.on_cache_read do |request|
63
+ $redis.get(request.digest) # << 3P code
66
64
  end
67
65
 
68
- client.on_cache_write do |key, payload, _context|
69
- $redis.set(key, payload) # << 3P code
66
+ client.on_cache_write do |request, payload|
67
+ $redis.set(request.digest, payload) # << 3P code
70
68
  end
71
69
  ```
72
70
 
73
- Note that inlined input data works against caching, so you should _avoid_ this:
71
+ Note that inlined input data works against caching, so you should _avoid_ these input literals when possible:
74
72
 
75
73
  ```graphql
76
74
  query {
@@ -78,7 +76,7 @@ query {
78
76
  }
79
77
  ```
80
78
 
81
- Instead, always leverage variables in queries so that the document body remains consistent across requests:
79
+ Instead, leverage query variables so that the document body remains consistent across requests:
82
80
 
83
81
  ```graphql
84
82
  query($id: ID!) {
@@ -93,11 +91,11 @@ query($id: ID!) {
93
91
  The client also provides an error hook. Any program errors rescued during execution will be passed to the `on_error` handler, which can report on the error as needed and return a formatted error message for the client to add to the [GraphQL errors](https://spec.graphql.org/June2018/#sec-Errors) result.
94
92
 
95
93
  ```ruby
96
- client.on_error do |err, context|
94
+ client.on_error do |request, err|
97
95
  # log the error
98
96
  Bugsnag.notify(err)
99
97
 
100
98
  # return a formatted message for the public response
101
- "Whoops, please contact support abount request '#{context[:request_id]}'"
99
+ "Whoops, please contact support abount request '#{request.context[:request_id]}'"
102
100
  end
103
101
  ```
data/docs/request.md CHANGED
@@ -3,18 +3,22 @@
3
3
  A `Request` contains a parsed GraphQL document and variables, and handles the logistics of extracting the appropriate operation, variable definitions, and fragments. A `Request` should be built once per server request and passed through to other stitching components that utilize request information.
4
4
 
5
5
  ```ruby
6
- document = "query FetchMovie($id: ID!) { movie(id:$id) { id genre } }"
7
- request = GraphQL::Stitching::Request.new(document, variables: { "id" => "1" }, operation_name: "FetchMovie")
8
-
9
- request.document # parsed AST via GraphQL.parse
10
- request.variables # user-submitted variables
11
- request.string # normalized printed document string
12
- request.digest # SHA digest of the normalized document string
13
-
14
- request.variable_definitions # a mapping of variable names to their type definitions
15
- request.fragment_definitions # a mapping of fragment names to their fragment definitions
6
+ source = "query FetchMovie($id: ID!) { movie(id:$id) { id genre } }"
7
+ request = GraphQL::Stitching::Request.new(source, variables: { "id" => "1" }, operation_name: "FetchMovie")
16
8
  ```
17
9
 
10
+ A `Request` provides the following information:
11
+
12
+ - `req.document`: parsed AST of the GraphQL source
13
+ - `req.variables`: a hash of user-submitted variables
14
+ - `req.string`: the original GraphQL source string, or printed document
15
+ - `req.digest`: a SHA2 of the request string
16
+ - `req.normalized_string`: printed document string with consistent whitespace
17
+ - `req.normalized_digest`: a SHA2 of the normalized string
18
+ - `req.operation`: the operation definition selected for the request
19
+ - `req.variable_definitions`: a mapping of variable names to their type definitions
20
+ - `req.fragment_definitions`: a mapping of fragment names to their fragment definitions
21
+
18
22
  ### Preparing requests
19
23
 
20
24
  A request should be prepared for stitching using the `prepare!` method _after_ validations have been run:
data/docs/supergraph.md CHANGED
@@ -4,29 +4,25 @@ A `Supergraph` is the singuar representation of a stitched graph. `Supergraph` i
4
4
 
5
5
  ### Export and caching
6
6
 
7
- A Supergraph is designed to be composed, cached, and restored. Calling the `export` method will return an SDL (Schema Definition Language) print of the combined graph schema and a delegation mapping hash. These can be persisted in any raw format that suits your stack:
7
+ A Supergraph is designed to be composed, cached, and restored. Calling `to_definition` will return an SDL (Schema Definition Language) print of the combined graph schema with delegation mapping directives. This pre-composed schema can be persisted in any raw format that suits your stack:
8
8
 
9
9
  ```ruby
10
- supergraph_sdl, delegation_map = supergraph.export
10
+ supergraph_sdl = supergraph.to_definition
11
11
 
12
- # stash these resources in Redis...
12
+ # stash this composed schema in a cache...
13
13
  $redis.set("cached_supergraph_sdl", supergraph_sdl)
14
- $redis.set("cached_delegation_map", JSON.generate(delegation_map))
15
14
 
16
- # or, write the resources as files and commit them to your repo...
15
+ # or, write the composed schema as a file into your repo...
17
16
  File.write("supergraph/schema.graphql", supergraph_sdl)
18
- File.write("supergraph/delegation_map.json", JSON.generate(delegation_map))
19
17
  ```
20
18
 
21
- To restore a Supergraph, call `from_export` proving the cached SDL string, the parsed JSON delegation mapping, and a hash of executables keyed by their location names:
19
+ To restore a Supergraph, call `from_definition` providing the cached SDL string and a hash of executables keyed by their location names:
22
20
 
23
21
  ```ruby
24
22
  supergraph_sdl = $redis.get("cached_supergraph_sdl")
25
- delegation_map = JSON.parse($redis.get("cached_delegation_map"))
26
23
 
27
- supergraph = GraphQL::Stitching::Supergraph.from_export(
28
- schema: supergraph_sdl,
29
- delegation_map: delegation_map,
24
+ supergraph = GraphQL::Stitching::Supergraph.from_definition(
25
+ supergraph_sdl,
30
26
  executables: {
31
27
  my_remote: GraphQL::Stitching::HttpExecutable.new(url: "http://localhost:3000"),
32
28
  my_local: MyLocalSchema,
@@ -52,7 +52,7 @@ module GraphQL
52
52
  rescue GraphQL::ParseError, GraphQL::ExecutionError => e
53
53
  error_result([e])
54
54
  rescue StandardError => e
55
- custom_message = @on_error.call(e, request.context) if @on_error
55
+ custom_message = @on_error.call(request, e) if @on_error
56
56
  error_result([{ "message" => custom_message || "An unexpected error occured." }])
57
57
  end
58
58
 
@@ -75,14 +75,14 @@ module GraphQL
75
75
 
76
76
  def fetch_plan(request)
77
77
  if @on_cache_read
78
- cached_plan = @on_cache_read.call(request.digest, request.context)
78
+ cached_plan = @on_cache_read.call(request)
79
79
  return GraphQL::Stitching::Plan.from_json(JSON.parse(cached_plan)) if cached_plan
80
80
  end
81
81
 
82
82
  plan = yield
83
83
 
84
84
  if @on_cache_write
85
- @on_cache_write.call(request.digest, JSON.generate(plan.as_json), request.context)
85
+ @on_cache_write.call(request, JSON.generate(plan.as_json))
86
86
  end
87
87
 
88
88
  plan
@@ -53,7 +53,7 @@ module GraphQL
53
53
  end
54
54
  end
55
55
 
56
- # "Typename" => merged_directive
56
+ # "directive_name" => merged_directive
57
57
  @schema_directives = @candidate_directives_by_name_and_location.each_with_object({}) do |(directive_name, directives_by_location), memo|
58
58
  memo[directive_name] = build_directive(directive_name, directives_by_location)
59
59
  end
@@ -88,19 +88,20 @@ module GraphQL
88
88
 
89
89
  # "Typename" => merged_type
90
90
  schema_types = @candidate_types_by_name_and_location.each_with_object({}) do |(type_name, types_by_location), memo|
91
- kinds = types_by_location.values.map { _1.kind.name }.uniq
91
+ kinds = types_by_location.values.map { _1.kind.name }.tap(&:uniq!)
92
92
 
93
93
  if kinds.length > 1
94
94
  raise ComposerError, "Cannot merge different kinds for `#{type_name}`. Found: #{kinds.join(", ")}."
95
95
  end
96
96
 
97
+ extract_boundaries(type_name, types_by_location) if type_name == @query_name
98
+
97
99
  memo[type_name] = case kinds.first
98
100
  when "SCALAR"
99
101
  build_scalar_type(type_name, types_by_location)
100
102
  when "ENUM"
101
103
  build_enum_type(type_name, types_by_location, enum_usage)
102
104
  when "OBJECT"
103
- extract_boundaries(type_name, types_by_location) if type_name == @query_name
104
105
  build_object_type(type_name, types_by_location)
105
106
  when "INTERFACE"
106
107
  build_interface_type(type_name, types_by_location)
@@ -200,7 +201,7 @@ module GraphQL
200
201
  graphql_name(directive_name)
201
202
  description(builder.merge_descriptions(directive_name, directives_by_location))
202
203
  repeatable(directives_by_location.values.any?(&:repeatable?))
203
- locations(*directives_by_location.values.flat_map(&:locations).uniq)
204
+ locations(*directives_by_location.values.flat_map(&:locations).tap(&:uniq!))
204
205
  builder.build_merged_arguments(directive_name, directives_by_location, self, directive_name: directive_name)
205
206
  end
206
207
  end
@@ -262,7 +263,7 @@ module GraphQL
262
263
  description(builder.merge_descriptions(type_name, types_by_location))
263
264
 
264
265
  interface_names = types_by_location.values.flat_map { _1.interfaces.map(&:graphql_name) }
265
- interface_names.uniq.each do |interface_name|
266
+ interface_names.tap(&:uniq!).each do |interface_name|
266
267
  implements(builder.build_type_binding(interface_name))
267
268
  end
268
269
 
@@ -280,7 +281,7 @@ module GraphQL
280
281
  description(builder.merge_descriptions(type_name, types_by_location))
281
282
 
282
283
  interface_names = types_by_location.values.flat_map { _1.interfaces.map(&:graphql_name) }
283
- interface_names.uniq.each do |interface_name|
284
+ interface_names.tap(&:uniq!).each do |interface_name|
284
285
  implements(builder.build_type_binding(interface_name))
285
286
  end
286
287
 
@@ -296,7 +297,7 @@ module GraphQL
296
297
  graphql_name(type_name)
297
298
  description(builder.merge_descriptions(type_name, types_by_location))
298
299
 
299
- possible_names = types_by_location.values.flat_map { _1.possible_types.map(&:graphql_name) }.uniq
300
+ possible_names = types_by_location.values.flat_map { _1.possible_types.map(&:graphql_name) }.tap(&:uniq!)
300
301
  possible_types(*possible_names.map { builder.build_type_binding(_1) })
301
302
  builder.build_merged_directives(type_name, types_by_location, self)
302
303
  end
@@ -540,7 +541,7 @@ module GraphQL
540
541
  field: field_candidate.name,
541
542
  arg: argument_name,
542
543
  list: boundary_structure.first.list?,
543
- federation: kwargs[:federation],
544
+ federation: kwargs[:federation] || false,
544
545
  )
545
546
  end
546
547
  end
@@ -160,7 +160,8 @@ module GraphQL
160
160
  errors_result.concat(pathed_errors_by_object_id.values)
161
161
  end
162
162
  end
163
- errors_result.flatten!
163
+
164
+ errors_result.tap(&:flatten!)
164
165
  end
165
166
 
166
167
  private
@@ -3,7 +3,7 @@
3
3
  module GraphQL
4
4
  module Stitching
5
5
  class Planner
6
- SUPERGRAPH_LOCATIONS = [Supergraph::LOCATION].freeze
6
+ SUPERGRAPH_LOCATIONS = [Supergraph::SUPERGRAPH_LOCATION].freeze
7
7
  TYPENAME = "__typename"
8
8
  QUERY_OP = "query"
9
9
  MUTATION_OP = "mutation"
@@ -4,14 +4,13 @@ module GraphQL
4
4
  module Stitching
5
5
  class Request
6
6
  SUPPORTED_OPERATIONS = ["query", "mutation"].freeze
7
+ SKIP_INCLUDE_DIRECTIVE = /@(?:skip|include)/
7
8
 
8
9
  attr_reader :document, :variables, :operation_name, :context
9
10
 
10
11
  def initialize(document, operation_name: nil, variables: nil, context: nil)
11
- @may_contain_runtime_directives = true
12
-
13
12
  @document = if document.is_a?(String)
14
- @may_contain_runtime_directives = document.include?("@")
13
+ @string = document
15
14
  GraphQL.parse(document)
16
15
  else
17
16
  document
@@ -23,13 +22,21 @@ module GraphQL
23
22
  end
24
23
 
25
24
  def string
26
- @string ||= @document.to_query_string
25
+ @string || normalized_string
26
+ end
27
+
28
+ def normalized_string
29
+ @normalized_string ||= @document.to_query_string
27
30
  end
28
31
 
29
32
  def digest
30
33
  @digest ||= Digest::SHA2.hexdigest(string)
31
34
  end
32
35
 
36
+ def normalized_digest
37
+ @normalized_digest ||= Digest::SHA2.hexdigest(normalized_string)
38
+ end
39
+
33
40
  def operation
34
41
  @operation ||= begin
35
42
  operation_defs = @document.definitions.select do |d|
@@ -69,18 +76,15 @@ module GraphQL
69
76
 
70
77
  def prepare!
71
78
  operation.variables.each do |v|
72
- @variables[v.name] = v.default_value if @variables[v.name].nil?
79
+ @variables[v.name] = v.default_value if @variables[v.name].nil? && !v.default_value.nil?
73
80
  end
74
81
 
75
- if @may_contain_runtime_directives
76
- @document, modified = SkipInclude.render(@document, @variables)
77
-
78
- if modified
79
- @string = nil
80
- @digest = nil
81
- @operation = nil
82
- @variable_definitions = nil
83
- @fragment_definitions = nil
82
+ if @string.nil? || @string.match?(SKIP_INCLUDE_DIRECTIVE)
83
+ SkipInclude.render(@document, @variables) do |modified_ast|
84
+ @document = modified_ast
85
+ @string = @normalized_string = nil
86
+ @digest = @normalized_digest = nil
87
+ @operation = @operation_directives = @variable_definitions = nil
84
88
  end
85
89
  end
86
90
 
@@ -15,7 +15,11 @@ module GraphQL
15
15
  definition
16
16
  end
17
17
 
18
- return document.merge(definitions: definitions), changed
18
+ return document unless changed
19
+
20
+ document = document.merge(definitions: definitions)
21
+ yield(document) if block_given?
22
+ document
19
23
  end
20
24
 
21
25
  private
@@ -3,34 +3,89 @@
3
3
  module GraphQL
4
4
  module Stitching
5
5
  class Supergraph
6
- LOCATION = "__super"
6
+ SUPERGRAPH_LOCATION = "__super"
7
+
8
+ class ResolverDirective < GraphQL::Schema::Directive
9
+ graphql_name "resolver"
10
+ locations OBJECT, INTERFACE, UNION
11
+ argument :location, String, required: true
12
+ argument :key, String, required: true
13
+ argument :field, String, required: true
14
+ argument :arg, String, required: true
15
+ argument :list, Boolean, required: false
16
+ argument :federation, Boolean, required: false
17
+ repeatable true
18
+ end
7
19
 
8
- def self.validate_executable!(location, executable)
9
- return true if executable.is_a?(Class) && executable <= GraphQL::Schema
10
- return true if executable && executable.respond_to?(:call)
11
- raise StitchingError, "Invalid executable provided for location `#{location}`."
20
+ class SourceDirective < GraphQL::Schema::Directive
21
+ graphql_name "source"
22
+ locations FIELD_DEFINITION
23
+ argument :location, String, required: true
24
+ repeatable true
12
25
  end
13
26
 
14
- def self.from_export(schema:, delegation_map:, executables:)
15
- schema = GraphQL::Schema.from_definition(schema) if schema.is_a?(String)
27
+ class << self
28
+ def validate_executable!(location, executable)
29
+ return true if executable.is_a?(Class) && executable <= GraphQL::Schema
30
+ return true if executable && executable.respond_to?(:call)
31
+ raise StitchingError, "Invalid executable provided for location `#{location}`."
32
+ end
33
+
34
+ def from_definition(schema, executables:)
35
+ schema = GraphQL::Schema.from_definition(schema) if schema.is_a?(String)
36
+ field_map = {}
37
+ boundary_map = {}
38
+ possible_locations = {}
39
+ introspection_types = schema.introspection_system.types.keys
40
+
41
+ schema.types.each do |type_name, type|
42
+ next if introspection_types.include?(type_name)
43
+
44
+ type.directives.each do |directive|
45
+ next unless directive.graphql_name == ResolverDirective.graphql_name
46
+
47
+ kwargs = directive.arguments.keyword_arguments
48
+ boundary_map[type_name] ||= []
49
+ boundary_map[type_name] << Boundary.new(
50
+ type_name: type_name,
51
+ location: kwargs[:location],
52
+ key: kwargs[:key],
53
+ field: kwargs[:field],
54
+ arg: kwargs[:arg],
55
+ list: kwargs[:list] || false,
56
+ federation: kwargs[:federation] || false,
57
+ )
58
+ end
59
+
60
+ next unless type.kind.fields?
16
61
 
17
- executables = delegation_map["locations"].each_with_object({}) do |location, memo|
18
- executable = executables[location] || executables[location.to_sym]
19
- if validate_executable!(location, executable)
20
- memo[location] = executable
62
+ type.fields.each do |field_name, field|
63
+ field.directives.each do |d|
64
+ next unless d.graphql_name == SourceDirective.graphql_name
65
+
66
+ location = d.arguments.keyword_arguments[:location]
67
+ field_map[type_name] ||= {}
68
+ field_map[type_name][field_name] ||= []
69
+ field_map[type_name][field_name] << location
70
+ possible_locations[location] = true
71
+ end
72
+ end
21
73
  end
22
- end
23
74
 
24
- boundaries = delegation_map["boundaries"].map do |k, b|
25
- [k, b.map { Boundary.new(**_1) }]
26
- end
75
+ executables = possible_locations.keys.each_with_object({}) do |location, memo|
76
+ executable = executables[location] || executables[location.to_sym]
77
+ if validate_executable!(location, executable)
78
+ memo[location] = executable
79
+ end
80
+ end
27
81
 
28
- new(
29
- schema: schema,
30
- fields: delegation_map["fields"],
31
- boundaries: boundaries.to_h,
32
- executables: executables,
33
- )
82
+ new(
83
+ schema: schema,
84
+ fields: field_map,
85
+ boundaries: boundary_map,
86
+ executables: executables,
87
+ )
88
+ end
34
89
  end
35
90
 
36
91
  attr_reader :schema, :boundaries, :locations_by_type_and_field, :executables
@@ -48,32 +103,77 @@ module GraphQL
48
103
  next unless type.kind.fields?
49
104
 
50
105
  memo[type_name] = type.fields.keys.each_with_object({}) do |field_name, m|
51
- m[field_name] = [LOCATION]
106
+ m[field_name] = [SUPERGRAPH_LOCATION]
52
107
  end
53
108
  end.freeze
54
109
 
55
110
  # validate and normalize executable references
56
- @executables = executables.each_with_object({ LOCATION => @schema }) do |(location, executable), memo|
111
+ @executables = executables.each_with_object({ SUPERGRAPH_LOCATION => @schema }) do |(location, executable), memo|
57
112
  if self.class.validate_executable!(location, executable)
58
113
  memo[location.to_s] = executable
59
114
  end
60
115
  end.freeze
61
116
  end
62
117
 
118
+ def to_definition
119
+ if @schema.directives[ResolverDirective.graphql_name].nil?
120
+ @schema.directive(ResolverDirective)
121
+ end
122
+ if @schema.directives[SourceDirective.graphql_name].nil?
123
+ @schema.directive(SourceDirective)
124
+ end
125
+
126
+ @schema.types.each do |type_name, type|
127
+ if boundaries_for_type = @boundaries.dig(type_name)
128
+ boundaries_for_type.each do |boundary|
129
+ existing = type.directives.find do |d|
130
+ kwargs = d.arguments.keyword_arguments
131
+ d.graphql_name == ResolverDirective.graphql_name &&
132
+ kwargs[:location] == boundary.location &&
133
+ kwargs[:key] == boundary.key &&
134
+ kwargs[:field] == boundary.field &&
135
+ kwargs[:arg] == boundary.arg &&
136
+ kwargs.fetch(:list, false) == boundary.list &&
137
+ kwargs.fetch(:federation, false) == boundary.federation
138
+ end
139
+
140
+ type.directive(ResolverDirective, **{
141
+ location: boundary.location,
142
+ key: boundary.key,
143
+ field: boundary.field,
144
+ arg: boundary.arg,
145
+ list: boundary.list || nil,
146
+ federation: boundary.federation || nil,
147
+ }.tap(&:compact!)) if existing.nil?
148
+ end
149
+ end
150
+
151
+ next unless type.kind.fields?
152
+
153
+ type.fields.each do |field_name, field|
154
+ locations_for_field = @locations_by_type_and_field.dig(type_name, field_name)
155
+ next if locations_for_field.nil?
156
+
157
+ locations_for_field.each do |location|
158
+ existing = field.directives.find do |d|
159
+ d.graphql_name == SourceDirective.graphql_name &&
160
+ d.arguments.keyword_arguments[:location] == location
161
+ end
162
+
163
+ field.directive(SourceDirective, location: location) if existing.nil?
164
+ end
165
+ end
166
+ end
167
+
168
+ @schema.to_definition
169
+ end
170
+
63
171
  def fields
64
172
  @locations_by_type_and_field.reject { |k, _v| memoized_introspection_types[k] }
65
173
  end
66
174
 
67
175
  def locations
68
- @executables.keys.reject { _1 == LOCATION }
69
- end
70
-
71
- def export
72
- return GraphQL::Schema::Printer.print_schema(@schema), {
73
- "locations" => locations,
74
- "fields" => fields,
75
- "boundaries" => @boundaries.map { |k, b| [k, b.map(&:as_json)] }.to_h,
76
- }
176
+ @executables.keys.reject { _1 == SUPERGRAPH_LOCATION }
77
177
  end
78
178
 
79
179
  def memoized_introspection_types
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Stitching
5
- VERSION = "1.0.5"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  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.0.5
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg MacWilliam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-19 00:00:00.000000000 Z
11
+ date: 2023-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql