graphql-stitching 1.0.5 → 1.1.0

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 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