apollo-federation 2.2.3 → 3.3.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: 2640264c9bd136989c737d434129afc4bdf507e1e60be0bf40a7260ee87dadee
4
- data.tar.gz: '088b4e6b3adf2717bc778a6022069a16ab819ac40c9bc3b3d9f8773eb3c44f6a'
3
+ metadata.gz: 3133ca250d8e6ed9689a172c2fdaf373ae0ee0fcb371848cffeb75938f97be62
4
+ data.tar.gz: 5467c27805efb85724124ede7c1014abfffbd679aba915e188ec74edfc8a8051
5
5
  SHA512:
6
- metadata.gz: a6abd4b01d28d71e2fd943dc492ab8192cc82b17c8f4f3ede5da9b0bc18846f5b52ac6b123b55f0d3b3caaa858a2d88e7ebb427f7553b0f6d14c62141b2c3244
7
- data.tar.gz: 65e28108093d158475d361cf4f3524b5a72ee009aa417ba0c5704c31f84be521331ec80505a325632b205c6a3bb9f43908c12e3aa28adc7c68a38fb5cdabe0f6
6
+ metadata.gz: 446e436bbc11437122d01ba8c49097919b0c1d281ab40427ea330d3494d05dc21d10e3795394ef41bea9b24d5d33eb9c7ba854116f96cb32cc88318c51cde0ac
7
+ data.tar.gz: b22c3f55c0fb6114f68aefcedd08ed5f8acaebda5699f26be21e4cc9ad61c3d225aa6beb1f1253e64533dae56da6d869c8962a4b6aa75d548a9e032e01d3a8dc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,45 @@
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
+
22
+ # [3.0.0](https://github.com/Gusto/apollo-federation-ruby/compare/v2.2.4...v3.0.0) (2022-04-05)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * camelize string fields to match sym behavior ([8f0382b](https://github.com/Gusto/apollo-federation-ruby/commit/8f0382b346d2cde5be252138275d67373b36acd7))
28
+
29
+
30
+ ### BREAKING CHANGES
31
+
32
+ * string fields will be camelized by default rather than passed as is.
33
+
34
+ ## [2.2.4](https://github.com/Gusto/apollo-federation-ruby/compare/v2.2.3...v2.2.4) (2022-04-01)
35
+
36
+
37
+ ### Bug Fixes
38
+
39
+ * add linux and darwin platforms to lockfile ([#188](https://github.com/Gusto/apollo-federation-ruby/issues/188)) ([fbbb856](https://github.com/Gusto/apollo-federation-ruby/commit/fbbb856b315400f21c189c26488efbf030792ae1))
40
+ * bump circleci cache version ([#189](https://github.com/Gusto/apollo-federation-ruby/issues/189)) ([1b6c9d8](https://github.com/Gusto/apollo-federation-ruby/commit/1b6c9d8fc647c19a7eb4c95ab76b276aae9131c5))
41
+ * set env variables for release step ([#191](https://github.com/Gusto/apollo-federation-ruby/issues/191)) ([db0d1e6](https://github.com/Gusto/apollo-federation-ruby/commit/db0d1e688b93bfa2114eb91248d2f507a50fca8a))
42
+
1
43
  ## [2.2.3](https://github.com/Gusto/apollo-federation-ruby/compare/v2.2.2...v2.2.3) (2022-03-22)
2
44
 
3
45
 
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,31 +7,78 @@ 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',
17
36
  arguments: [
18
37
  name: 'fields',
19
- values: ApolloFederation::FieldSetSerializer.serialize(requires[:fields]),
38
+ values: ApolloFederation::FieldSetSerializer.serialize(
39
+ requires[:fields],
40
+ camelize: requires.fetch(:camelize, true),
41
+ ),
20
42
  ],
21
43
  )
22
44
  end
45
+
23
46
  if provides
24
47
  add_directive(
25
48
  name: 'provides',
26
49
  arguments: [
27
50
  name: 'fields',
28
- values: ApolloFederation::FieldSetSerializer.serialize(provides[:fields]),
51
+ values: ApolloFederation::FieldSetSerializer.serialize(
52
+ provides[:fields],
53
+ camelize: provides.fetch(:camelize, true),
54
+ ),
29
55
  ],
30
56
  )
31
57
  end
32
58
 
33
- # Pass on the default args:
34
- 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
35
82
  end
36
83
  end
37
84
  end
@@ -6,20 +6,18 @@ module ApolloFederation
6
6
  module FieldSetSerializer
7
7
  extend self
8
8
 
9
- def serialize(fields)
9
+ def serialize(fields, camelize: true)
10
10
  case fields
11
11
  when Hash
12
12
  fields.map do |field, nested_selections|
13
- "#{camelize(field)} { #{serialize(nested_selections)} }"
13
+ "#{camelize(field, camelize)} { #{serialize(nested_selections, camelize: camelize)} }"
14
14
  end.join(' ')
15
15
  when Array
16
16
  fields.map do |field|
17
- serialize(field)
17
+ serialize(field, camelize: camelize)
18
18
  end.join(' ')
19
- when String
20
- fields
21
- when Symbol
22
- camelize(fields)
19
+ when Symbol, String
20
+ camelize(fields, camelize)
23
21
  else
24
22
  raise ArgumentError, "Unexpected field set type: #{fields.class}"
25
23
  end
@@ -27,8 +25,8 @@ module ApolloFederation
27
25
 
28
26
  private
29
27
 
30
- def camelize(field)
31
- GraphQL::Schema::Member::BuildType.camelize(field.to_s)
28
+ def camelize(field, camelize)
29
+ camelize ? GraphQL::Schema::Member::BuildType.camelize(field.to_s) : field.to_s
32
30
  end
33
31
  end
34
32
  end
@@ -18,12 +18,16 @@ module ApolloFederation
18
18
  add_directive(name: 'extends')
19
19
  end
20
20
 
21
- def key(fields:)
21
+ def inaccessible
22
+ add_directive(name: 'inaccessible')
23
+ end
24
+
25
+ def key(fields:, camelize: true)
22
26
  add_directive(
23
27
  name: 'key',
24
28
  arguments: [
25
29
  name: 'fields',
26
- values: ApolloFederation::FieldSetSerializer.serialize(fields),
30
+ values: ApolloFederation::FieldSetSerializer.serialize(fields, camelize: camelize),
27
31
  ],
28
32
  )
29
33
  end
@@ -16,12 +16,20 @@ module ApolloFederation
16
16
  add_directive(name: 'extends')
17
17
  end
18
18
 
19
- def key(fields:)
19
+ def shareable
20
+ add_directive(name: 'shareable')
21
+ end
22
+
23
+ def inaccessible
24
+ add_directive(name: 'inaccessible')
25
+ end
26
+
27
+ def key(fields:, camelize: true)
20
28
  add_directive(
21
29
  name: 'key',
22
30
  arguments: [
23
31
  name: 'fields',
24
- values: ApolloFederation::FieldSetSerializer.serialize(fields),
32
+ values: ApolloFederation::FieldSetSerializer.serialize(fields, camelize: camelize),
25
33
  ],
26
34
  )
27
35
  end
@@ -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 = '2.2.3'
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: 2.2.3
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-03-22 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