apollo-federation 3.0.0 → 3.3.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: 97e0cffbe36d7e21115386a2c0cffa379b62eeff24922d1203a2abf1dbdf7059
4
- data.tar.gz: 2220d5e08b981826d4494d98d9728a2bc4c1ef1a6aae9381f7f467204051c2c4
3
+ metadata.gz: 3133ca250d8e6ed9689a172c2fdaf373ae0ee0fcb371848cffeb75938f97be62
4
+ data.tar.gz: 5467c27805efb85724124ede7c1014abfffbd679aba915e188ec74edfc8a8051
5
5
  SHA512:
6
- metadata.gz: e30f475dfa19d57e26b9374b7e6b59b33ff7125fdd57e22014448e0f7aaa741c188b7f72bde98b20953d4519fbd4fb76585a0336757123d34a95dee87e2b6044
7
- data.tar.gz: df9e6125583486cdba492c97376d6fea8a9ff31b7fb72f2940375f53b426ec8cc3109178e9a3ca9d23f261df4dc5acc94244cbf76f1be26281ad264e4e94a075
6
+ metadata.gz: 446e436bbc11437122d01ba8c49097919b0c1d281ab40427ea330d3494d05dc21d10e3795394ef41bea9b24d5d33eb9c7ba854116f96cb32cc88318c51cde0ac
7
+ data.tar.gz: b22c3f55c0fb6114f68aefcedd08ed5f8acaebda5699f26be21e4cc9ad61c3d225aa6beb1f1253e64533dae56da6d869c8962a4b6aa75d548a9e032e01d3a8dc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # [3.3.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.2.0...v3.3.0) (2022-08-24)
2
+
3
+
4
+ ### Features
5
+
6
+ * introduce optional `resolve_references` method ([#206](https://github.com/Gusto/apollo-federation-ruby/issues/206)) ([1e3b631](https://github.com/Gusto/apollo-federation-ruby/commit/1e3b631609e1dfec8c3f126cd9dc8e0a2b3a0a57))
7
+
8
+ # [3.2.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.1.0...v3.2.0) (2022-08-15)
9
+
10
+
11
+ ### Features
12
+
13
+ * allow custom namespace for linked directives ([03fdfea](https://github.com/Gusto/apollo-federation-ruby/commit/03fdfeafaaea3c98ca4b7a734ce760ea08410530))
14
+
15
+ # [3.1.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.0.0...v3.1.0) (2022-06-21)
16
+
17
+
18
+ ### Features
19
+
20
+ * Support Federation v2 ([#196](https://github.com/Gusto/apollo-federation-ruby/issues/196)) ([238736c](https://github.com/Gusto/apollo-federation-ruby/commit/238736cdb6f12121ce2a295c7a28fba3990012b9)), closes [/www.apollographql.com/docs/federation/federation-2/moving-to-federation-2/#opt-in-to-federation-2](https://github.com//www.apollographql.com/docs/federation/federation-2/moving-to-federation-2//issues/opt-in-to-federation-2)
21
+
1
22
  # [3.0.0](https://github.com/Gusto/apollo-federation-ruby/compare/v2.2.4...v3.0.0) (2022-04-05)
2
23
 
3
24
 
data/README.md CHANGED
@@ -61,6 +61,15 @@ class MySchema < GraphQL::Schema
61
61
  end
62
62
  ```
63
63
 
64
+ **Optional:** To opt in to Federation v2, specify the version in your schema:
65
+
66
+ ```ruby
67
+ class MySchema < GraphQL::Schema
68
+ include ApolloFederation::Schema
69
+ federation version: '2.0'
70
+ end
71
+ ```
72
+
64
73
  ## Example
65
74
 
66
75
  The [`example`](./example/) folder contains a Ruby implementation of Apollo's [`federation-demo`](https://github.com/apollographql/federation-demo). To run it locally, install the Ruby dependencies:
@@ -160,6 +169,59 @@ end
160
169
  ```
161
170
  See [field set syntax](#field-set-syntax) for more details on the format of the `fields` option.
162
171
 
172
+ ### The `@shareable` directive (Apollo Federation v2)
173
+
174
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#shareable)
175
+
176
+ Call `shareable` within your class definition:
177
+
178
+ ```ruby
179
+ class User < BaseObject
180
+ shareable
181
+ end
182
+ ```
183
+
184
+ Pass the `shareable: true` option to your field definition:
185
+
186
+ ```ruby
187
+ class User < BaseObject
188
+ field :id, ID, null: false, shareable: true
189
+ end
190
+ ```
191
+
192
+ ### The `@inaccessible` directive (Apollo Federation v2)
193
+
194
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible)
195
+
196
+ Call `inaccessible` within your class definition:
197
+
198
+ ```ruby
199
+ class User < BaseObject
200
+ inaccessible
201
+ end
202
+ ```
203
+
204
+ Pass the `inaccessible: true` option to your field definition:
205
+
206
+ ```ruby
207
+ class User < BaseObject
208
+ field :id, ID, null: false, inaccessible: true
209
+ end
210
+ ```
211
+
212
+ ### The `@override` directive (Apollo Federation v2)
213
+
214
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#override)
215
+
216
+ Pass the `override:` option to your field definition:
217
+
218
+ ```ruby
219
+ class Product < BaseObject
220
+ field :id, ID, null: false
221
+ field :inStock, Boolean, null: false, override: { from: 'Products' }
222
+ end
223
+ ```
224
+
163
225
  ### Field set syntax
164
226
 
165
227
  Field sets can be either strings encoded with the Apollo Field Set [syntax]((https://www.apollographql.com/docs/apollo-server/federation/federation-spec/#scalar-_fieldset)) or arrays, hashes and snake case symbols that follow the graphql-ruby conventions:
@@ -27,8 +27,10 @@ module ApolloFederation
27
27
  end
28
28
 
29
29
  def _entities(representations:)
30
- representations.map do |reference|
31
- typename = reference[:__typename]
30
+ grouped_references = representations.group_by { |r| r[:__typename] }
31
+
32
+ final_results = []
33
+ grouped_references.each do |typename, references|
32
34
  # TODO: Use warden or schema?
33
35
  type = context.warden.get_type(typename)
34
36
  if type.nil? || type.kind != GraphQL::TypeKinds::OBJECT
@@ -40,21 +42,27 @@ module ApolloFederation
40
42
  # TODO: What if the type is an interface?
41
43
  type_class = class_of_type(type)
42
44
 
43
- if type_class.respond_to?(:resolve_reference)
44
- result = type_class.resolve_reference(reference, context)
45
+ if type_class.respond_to?(:resolve_references)
46
+ results = type_class.resolve_references(references, context)
47
+ elsif type_class.respond_to?(:resolve_reference)
48
+ results = references.map { |reference| type_class.resolve_reference(reference, context) }
45
49
  else
46
- result = reference
50
+ results = references
47
51
  end
48
52
 
49
- context.schema.after_lazy(result) do |resolved_value|
50
- # TODO: This isn't 100% correct: if (for some reason) 2 different resolve_reference calls
51
- # return the same object, it might not have the right type
52
- # Right now, apollo-federation just adds a __typename property to the result,
53
- # but I don't really like the idea of modifying the resolved object
54
- context[resolved_value] = type
55
- resolved_value
53
+ results = results.map do |result|
54
+ context.schema.after_lazy(result) do |resolved_value|
55
+ # TODO: This isn't 100% correct: if (for some reason) 2 different resolve_reference
56
+ # calls return the same object, it might not have the right type
57
+ # Right now, apollo-federation just adds a __typename property to the result,
58
+ # but I don't really like the idea of modifying the resolved object
59
+ context[resolved_value] = type
60
+ resolved_value
61
+ end
56
62
  end
63
+ final_results = final_results.concat(results)
57
64
  end
65
+ final_results
58
66
  end
59
67
 
60
68
  private
@@ -55,20 +55,28 @@ module ApolloFederation
55
55
 
56
56
  def merge_directives(node, type)
57
57
  if type.is_a?(ApolloFederation::HasDirectives)
58
- directives = type.federation_directives
58
+ directives = type.federation_directives || []
59
59
  else
60
60
  directives = []
61
61
  end
62
62
 
63
- (directives || []).each do |directive|
63
+ directives.each do |directive|
64
64
  node = node.merge_directive(
65
- name: directive[:name],
65
+ name: directive_name(directive),
66
66
  arguments: build_arguments_node(directive[:arguments]),
67
67
  )
68
68
  end
69
69
  node
70
70
  end
71
71
 
72
+ def directive_name(directive)
73
+ if schema.federation_2?
74
+ "#{schema.link_namespace}__#{directive[:name]}"
75
+ else
76
+ directive[:name]
77
+ end
78
+ end
79
+
72
80
  def build_arguments_node(arguments)
73
81
  (arguments || []).map do |arg|
74
82
  GraphQL::Language::Nodes::Argument.new(name: arg[:name], value: arg[:values])
@@ -7,10 +7,29 @@ module ApolloFederation
7
7
  module Field
8
8
  include HasDirectives
9
9
 
10
- def initialize(*args, external: false, requires: nil, provides: nil, **kwargs, &block)
10
+ VERSION_1_DIRECTIVES = %i[external requires provides].freeze
11
+ VERSION_2_DIRECTIVES = %i[shareable inaccessible override].freeze
12
+
13
+ def initialize(*args, **kwargs, &block)
14
+ add_v1_directives(**kwargs)
15
+ add_v2_directives(**kwargs)
16
+
17
+ # Remove the custom kwargs
18
+ kwargs = kwargs.delete_if do |k, _|
19
+ VERSION_1_DIRECTIVES.include?(k) || VERSION_2_DIRECTIVES.include?(k)
20
+ end
21
+
22
+ # Pass on the default args:
23
+ super(*args, **kwargs, &block)
24
+ end
25
+
26
+ private
27
+
28
+ def add_v1_directives(external: nil, requires: nil, provides: nil, **_kwargs)
11
29
  if external
12
30
  add_directive(name: 'external')
13
31
  end
32
+
14
33
  if requires
15
34
  add_directive(
16
35
  name: 'requires',
@@ -23,6 +42,7 @@ module ApolloFederation
23
42
  ],
24
43
  )
25
44
  end
45
+
26
46
  if provides
27
47
  add_directive(
28
48
  name: 'provides',
@@ -36,8 +56,29 @@ module ApolloFederation
36
56
  )
37
57
  end
38
58
 
39
- # Pass on the default args:
40
- super(*args, **kwargs, &block)
59
+ nil
60
+ end
61
+
62
+ def add_v2_directives(shareable: nil, inaccessible: nil, override: nil, **_kwargs)
63
+ if shareable
64
+ add_directive(name: 'shareable')
65
+ end
66
+
67
+ if inaccessible
68
+ add_directive(name: 'inaccessible')
69
+ end
70
+
71
+ if override
72
+ add_directive(
73
+ name: 'override',
74
+ arguments: [
75
+ name: 'from',
76
+ values: override[:from],
77
+ ],
78
+ )
79
+ end
80
+
81
+ nil
41
82
  end
42
83
  end
43
84
  end
@@ -18,6 +18,10 @@ module ApolloFederation
18
18
  add_directive(name: 'extends')
19
19
  end
20
20
 
21
+ def inaccessible
22
+ add_directive(name: 'inaccessible')
23
+ end
24
+
21
25
  def key(fields:, camelize: true)
22
26
  add_directive(
23
27
  name: 'key',
@@ -16,6 +16,14 @@ module ApolloFederation
16
16
  add_directive(name: 'extends')
17
17
  end
18
18
 
19
+ def shareable
20
+ add_directive(name: 'shareable')
21
+ end
22
+
23
+ def inaccessible
24
+ add_directive(name: 'inaccessible')
25
+ end
26
+
19
27
  def key(fields:, camelize: true)
20
28
  add_directive(
21
29
  name: 'key',
@@ -12,9 +12,35 @@ module ApolloFederation
12
12
  end
13
13
 
14
14
  module CommonMethods
15
+ FEDERATION_2_PREFIX = <<~SCHEMA
16
+ extend schema
17
+ @link(url: "https://specs.apollo.dev/federation/v2.0")
18
+
19
+ SCHEMA
20
+
21
+ def federation(version: '1.0', link: {})
22
+ @federation_version = version
23
+ @link = { as: 'federation' }.merge(link)
24
+ end
25
+
26
+ def federation_version
27
+ @federation_version || '1.0'
28
+ end
29
+
30
+ def federation_2?
31
+ Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.0.0')
32
+ end
33
+
15
34
  def federation_sdl(context: nil)
16
35
  document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context)
17
- GraphQL::Language::Printer.new.print(document_from_schema.document)
36
+
37
+ output = GraphQL::Language::Printer.new.print(document_from_schema.document)
38
+ output.prepend(FEDERATION_2_PREFIX) if federation_2?
39
+ output
40
+ end
41
+
42
+ def link_namespace
43
+ @link[:as]
18
44
  end
19
45
 
20
46
  def query(new_query_object = nil)
@@ -34,6 +60,14 @@ module ApolloFederation
34
60
 
35
61
  private
36
62
 
63
+ def federation_2_prefix
64
+ <<~SCHEMA
65
+ extend schema
66
+ @link(url: "https://specs.apollo.dev/federation/v2.0", as: "#{link_namespace}")
67
+
68
+ SCHEMA
69
+ end
70
+
37
71
  def schema_entities
38
72
  # Create a temporary schema that inherits from this one to extract the types
39
73
  types_schema = Class.new(self)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApolloFederation
4
- VERSION = '3.0.0'
4
+ VERSION = '3.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apollo-federation
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noa Elad
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-04-05 00:00:00.000000000 Z
12
+ date: 2022-08-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: graphql