graphql-stitching 1.4.1 → 1.4.3

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: b7c50a82a3fa6298b224bef6923478217e82e6ddd4b6b09ca33df72f690edbdc
4
- data.tar.gz: 545e2817523bbcedcb84b2512942eb92ae66e5353c46d4556eb892590fa868b3
3
+ metadata.gz: a72adc399f618790fc1aefe1b6dca07709d45f10cc2936f5c3978d75b5d3f8c2
4
+ data.tar.gz: d45d7b73045755a750d6d4334e00e30f33c5124f83c849c24669c98f7912ca92
5
5
  SHA512:
6
- metadata.gz: 426d87b634cb77ad8bb185c8a23fbbc1439abbc60b55f6561999eb64d9c08642496be4eafd3415ccd47aebef21b39f349aca38be7c6ada4d34f763e7910a3b67
7
- data.tar.gz: 50711d59f93fc17809c2953a014438a3f59ea952030c0112393acacd8066d90e9824b332dfda9546d3bdc74e7a030442e5b74f3b2fdda61df356d7832469020b
6
+ metadata.gz: 5302083cb9047ad77aa5e6276d02e3bdac8256ef328edb8c72b9248efbb279c48bb3e34b8e220f131d3efa541fdaa1b682fe4622501900defe797217cbc365c8
7
+ data.tar.gz: 4e238de4607764406ac52634daafc293c1b5fe672457106ff1070e77bc4d52cdc1ace02ce46ca4cacbcde7648d8a4f2bb8cc0321157dfb3c85c3ec40d5f82e43
data/README.md CHANGED
@@ -101,7 +101,7 @@ This directive (or [static configuration](#sdl-based-schemas)) is applied to roo
101
101
 
102
102
  ```ruby
103
103
  products_schema = <<~GRAPHQL
104
- directive @stitch(key: String!) repeatable on FIELD_DEFINITION
104
+ directive @stitch(key: String!, arguments: String) repeatable on FIELD_DEFINITION
105
105
 
106
106
  type Product {
107
107
  id: ID!
@@ -114,7 +114,7 @@ products_schema = <<~GRAPHQL
114
114
  GRAPHQL
115
115
 
116
116
  catalog_schema = <<~GRAPHQL
117
- directive @stitch(key: String!) repeatable on FIELD_DEFINITION
117
+ directive @stitch(key: String!, arguments: String) repeatable on FIELD_DEFINITION
118
118
 
119
119
  type Product {
120
120
  id: ID!
@@ -226,7 +226,7 @@ type Query {
226
226
  }
227
227
  ```
228
228
 
229
- Key insertions are prefixed by `$` and specify a dot-notation path to any selections made by the resolver `key`, or `__typename`. This syntax allows sending multiple arguments that intermix stitching keys with complex input shapes and other static values:
229
+ Key insertions are prefixed by `$` and specify a dot-notation path to any selections made by the resolver key, or `__typename`. This syntax allows sending multiple arguments that intermix stitching keys with complex input shapes and other static values:
230
230
 
231
231
  ```graphql
232
232
  type Product {
@@ -237,10 +237,14 @@ input EntityKey {
237
237
  id: ID!
238
238
  type: String!
239
239
  }
240
+ enum EntitySource {
241
+ DATABASE
242
+ CACHE
243
+ }
240
244
 
241
245
  type Query {
242
- entities(keys: [EntityKey!]!, source: String="database"): [Entity]!
243
- @stitch(key: "id", arguments: "keys: { id: $.id, type: $.__typename }, source: 'cache'")
246
+ entities(keys: [EntityKey!]!, source: EntitySource = DATABASE): [Entity]!
247
+ @stitch(key: "id", arguments: "keys: { id: $.id, type: $.__typename }, source: CACHE")
244
248
  }
245
249
  ```
246
250
 
@@ -265,6 +269,7 @@ input CustomFieldLookup {
265
269
  ownerType: String!
266
270
  key: String!
267
271
  }
272
+
268
273
  type Query {
269
274
  customFields(lookups: [CustomFieldLookup!]!): [CustomField]! @stitch(
270
275
  key: "owner { id type } key",
@@ -320,6 +325,7 @@ class StitchingResolver < GraphQL::Schema::Directive
320
325
  locations FIELD_DEFINITION
321
326
  repeatable true
322
327
  argument :key, String, required: true
328
+ argument :arguments, String, required: false
323
329
  end
324
330
 
325
331
  class Query < GraphQL::Schema::Object
@@ -354,7 +360,7 @@ supergraph = GraphQL::Stitching::Composer.new.perform({
354
360
  executable: ->() { ... },
355
361
  stitch: [
356
362
  { field_name: "productById", key: "id" },
357
- { field_name: "productBySku", key: "sku" },
363
+ { field_name: "productBySku", key: "sku", arguments: "mySku: $.sku" },
358
364
  ]
359
365
  },
360
366
  # ...
data/docs/mechanics.md CHANGED
@@ -90,7 +90,7 @@ class GraphQlController
90
90
  variables: params[:variables],
91
91
  operation_name: params[:operation_name]
92
92
  )
93
- end
93
+ end
94
94
  end
95
95
  ```
96
96
 
@@ -345,4 +345,4 @@ type Query {
345
345
  }
346
346
  ```
347
347
 
348
- In this graph, `Widget` is a merged type without a resolver query in location C. This works because all of its fields are resolvable in other locations; that means location C can provide outbound representations of this type without ever needing to resolve inbound requests for it. Outbound types do still require a shared key field (such as `id` above) that allow them to join with data in other resolver locations (such as `price` above).
348
+ In this graph, `Widget` is a merged type without a resolver query in location C. This works because all of its fields are resolvable in other locations; that means location C can provide outbound representations of this type without ever needing to resolve inbound requests for it. Outbound types do still require a shared key field (such as `id` above) that allow them to join with data in other resolver locations (such as `price` above). Support for this pattern is limited to single-field keys, [composite keys](../README.md#composite-type-keys) require a resolver definition.
@@ -55,8 +55,10 @@ module GraphQL::Stitching
55
55
  end
56
56
 
57
57
  # All locations of a merged type must include at least one resolver key
58
- supergraph.fields_by_type_and_location[type.graphql_name].each_key do |location|
59
- if resolver_keys.none? { _1.locations.include?(location) }
58
+ supergraph.fields_by_type_and_location[type.graphql_name].each do |location, field_names|
59
+ has_resolver_key = resolver_keys.any? { _1.locations.include?(location) }
60
+ has_primitive_match = resolver_keys.any? { field_names.include?(_1.primitive_name) }
61
+ unless has_resolver_key || has_primitive_match
60
62
  raise ValidationError, "A resolver key is required for `#{type.graphql_name}` in #{location} to join with other locations."
61
63
  end
62
64
  end
@@ -557,7 +557,7 @@ module GraphQL
557
557
  argument = if subgraph_field.arguments.size == 1
558
558
  subgraph_field.arguments.values.first
559
559
  else
560
- subgraph_field.arguments[key.default_argument_name]
560
+ subgraph_field.arguments[key.primitive_name]
561
561
  end
562
562
 
563
563
  unless argument
@@ -565,7 +565,7 @@ module GraphQL
565
565
  "An argument mapping is required for unmatched names and composite keys."
566
566
  end
567
567
 
568
- "#{argument.graphql_name}: $.#{key.default_argument_name}"
568
+ "#{argument.graphql_name}: $.#{key.primitive_name}"
569
569
  end
570
570
 
571
571
  arguments = Resolver.parse_arguments_with_field(arguments_format, subgraph_field)
@@ -1,8 +1,8 @@
1
1
  # typed: false
2
2
  # frozen_string_literal: true
3
3
 
4
- module GraphQL
5
- module Stitching
4
+ module GraphQL::Stitching
5
+ class Executor
6
6
  # Shapes the final results payload to the request selection and schema definition.
7
7
  # This eliminates unrequested export selections and applies null bubbling.
8
8
  # @api private
@@ -3,6 +3,7 @@
3
3
  require "json"
4
4
  require_relative "./executor/resolver_source"
5
5
  require_relative "./executor/root_source"
6
+ require_relative "./executor/shaper"
6
7
 
7
8
  module GraphQL
8
9
  module Stitching
@@ -33,7 +34,7 @@ module GraphQL
33
34
  result = {}
34
35
 
35
36
  if @data && @data.length > 0
36
- result["data"] = raw ? @data : GraphQL::Stitching::Shaper.new(@request).perform!(@data)
37
+ result["data"] = raw ? @data : Shaper.new(@request).perform!(@data)
37
38
  end
38
39
 
39
40
  if @errors.length > 0
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GraphQL
4
4
  module Stitching
5
- # Immutable structures representing a query plan.
5
+ # Immutable (in theory) structures representing a query plan.
6
6
  # May serialize to/from JSON.
7
7
  class Plan
8
8
  Op = Struct.new(
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module GraphQL
4
- module Stitching
3
+ module GraphQL::Stitching
4
+ class Planner
5
5
  # A planned step in the sequence of stitching entrypoints together.
6
6
  # This is a mutable object that may change throughout the planning process.
7
7
  # It ultimately builds an immutable Plan::Op at the end of planning.
8
- class PlannerStep
8
+ class Step
9
9
  GRAPHQL_PRINTER = GraphQL::Language::Printer.new
10
10
 
11
11
  attr_reader :index, :location, :parent_type, :operation_type, :path
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "./planner/step"
4
+
3
5
  module GraphQL
4
6
  module Stitching
5
7
  class Planner
@@ -85,7 +87,7 @@ module GraphQL
85
87
  end
86
88
 
87
89
  if step.nil?
88
- @steps_by_entrypoint[entrypoint] = PlannerStep.new(
90
+ @steps_by_entrypoint[entrypoint] = Step.new(
89
91
  index: next_index,
90
92
  after: parent_index,
91
93
  location: location,
@@ -261,7 +263,7 @@ module GraphQL
261
263
 
262
264
  # B.4) Add a `__typename` export to abstracts and types that implement
263
265
  # fragments so that resolved type information is available during execution.
264
- if requires_typename
266
+ if requires_typename && !locale_selections.include?(Resolver::TYPENAME_EXPORT_NODE)
265
267
  locale_selections << Resolver::TYPENAME_EXPORT_NODE
266
268
  end
267
269
 
@@ -277,6 +279,10 @@ module GraphQL
277
279
  route.reduce(locale_selections) do |parent_selections, resolver|
278
280
  # E.1) Add the key of each resolver query into the prior location's selection set.
279
281
  parent_selections.push(*resolver.key.export_nodes) if resolver.key
282
+ parent_selections.uniq! do |node|
283
+ export_node = node.is_a?(GraphQL::Language::Nodes::Field) && Resolver.export_key?(node.alias)
284
+ export_node ? node.alias : node
285
+ end
280
286
 
281
287
  # E.2) Add a planner step for each new entrypoint location.
282
288
  add_step(
@@ -289,8 +295,6 @@ module GraphQL
289
295
  ).selections
290
296
  end
291
297
  end
292
-
293
- locale_selections.uniq! { _1.alias || _1.name }
294
298
  end
295
299
 
296
300
  locale_selections
@@ -42,7 +42,7 @@ module GraphQL::Stitching
42
42
  to_definition == other.to_definition
43
43
  end
44
44
 
45
- def default_argument_name
45
+ def primitive_name
46
46
  length == 1 ? first.name : nil
47
47
  end
48
48
 
@@ -47,7 +47,7 @@ module GraphQL
47
47
  end
48
48
 
49
49
  def version
50
- @version ||= Digest::SHA2.hexdigest(as_json.to_json)
50
+ @version ||= Digest::SHA2.hexdigest("#{Stitching::VERSION}/#{as_json.to_json}")
51
51
  end
52
52
 
53
53
  def ==(other)
@@ -147,7 +147,11 @@ module GraphQL
147
147
  def possible_keys_for_type_and_location(type_name, location)
148
148
  possible_keys_by_type = @possible_keys_by_type_and_location[type_name] ||= {}
149
149
  possible_keys_by_type[location] ||= possible_keys_for_type(type_name).select do |key|
150
- key.locations.include?(location)
150
+ next true if key.locations.include?(location)
151
+
152
+ # Outbound-only locations without resolver queries may dynamically match primitive keys
153
+ location_fields = fields_by_type_and_location[type_name][location] || GraphQL::Stitching::EMPTY_ARRAY
154
+ location_fields.include?(key.primitive_name)
151
155
  end
152
156
  end
153
157
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Stitching
5
- VERSION = "1.4.1"
5
+ VERSION = "1.4.3"
6
6
  end
7
7
  end
@@ -26,16 +26,14 @@ module GraphQL
26
26
  end
27
27
 
28
28
  require_relative "stitching/supergraph"
29
- require_relative "stitching/resolver"
30
29
  require_relative "stitching/client"
31
30
  require_relative "stitching/composer"
32
31
  require_relative "stitching/executor"
33
32
  require_relative "stitching/http_executable"
34
33
  require_relative "stitching/plan"
35
- require_relative "stitching/planner_step"
36
34
  require_relative "stitching/planner"
37
35
  require_relative "stitching/request"
38
- require_relative "stitching/shaper"
36
+ require_relative "stitching/resolver"
39
37
  require_relative "stitching/skip_include"
40
38
  require_relative "stitching/util"
41
39
  require_relative "stitching/version"
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.4.1
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg MacWilliam
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-03 00:00:00.000000000 Z
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -120,15 +120,15 @@ files:
120
120
  - lib/graphql/stitching/executor.rb
121
121
  - lib/graphql/stitching/executor/resolver_source.rb
122
122
  - lib/graphql/stitching/executor/root_source.rb
123
+ - lib/graphql/stitching/executor/shaper.rb
123
124
  - lib/graphql/stitching/http_executable.rb
124
125
  - lib/graphql/stitching/plan.rb
125
126
  - lib/graphql/stitching/planner.rb
126
- - lib/graphql/stitching/planner_step.rb
127
+ - lib/graphql/stitching/planner/step.rb
127
128
  - lib/graphql/stitching/request.rb
128
129
  - lib/graphql/stitching/resolver.rb
129
130
  - lib/graphql/stitching/resolver/arguments.rb
130
131
  - lib/graphql/stitching/resolver/keys.rb
131
- - lib/graphql/stitching/shaper.rb
132
132
  - lib/graphql/stitching/skip_include.rb
133
133
  - lib/graphql/stitching/supergraph.rb
134
134
  - lib/graphql/stitching/supergraph/key_directive.rb