apollo-federation 2.2.4 → 3.6.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: ba95ea6ff91193e1c977c0bd3eea9a5e9d697ae8f1fdf189823eb81d0a911bc3
4
- data.tar.gz: 51e4f2204aaab9ce67b3578454ec2d33b59ed9ffb90e73331a36b93e19b73dd9
3
+ metadata.gz: b48438a1b967fedac245550c5e46d887f7c4978d54736d90ba14aef61be5d4b7
4
+ data.tar.gz: 982dd4cc577d286793e57aef3ba63cab338a48e7034bd3c65aaacd1367b64b40
5
5
  SHA512:
6
- metadata.gz: 94cb699aa812877a646ded92f8fc1a4b12e8137410fea54c470e851e402110b8bb6b2c4ab1c42bb422e09616a910da90683e38ea69bf85365f3a86e9121aa3cc
7
- data.tar.gz: a74321947eb59666acd5420df26c554d48166f1f29aeaa4d3ce30d6e4f374090c7c7c83c4585c8fdbbad585adec8de6421f90c68072d0d144e33c73a6fa37bf4
6
+ metadata.gz: 18c9bd5dbdce6fa9b526fbc1df531a6a435c00d42ea8ac097ad008d32c748b910e2145a0832a03b630190cfaad0a2a9a7121a47f45eacee253132d46255f9a89
7
+ data.tar.gz: 415f518015e14e6a833643796748edd3ae1835678d6d1cf3f13920b903061193ae8caec3ef7975fc3ab63c9df4cc1e9853211c5e5374c2466df367515ca29d20
data/CHANGELOG.md CHANGED
@@ -1,3 +1,101 @@
1
+ # [3.6.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.5.3...v3.6.0) (2023-03-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * attempt to fix missing CHANGELOG entries ([07170d3](https://github.com/Gusto/apollo-federation-ruby/commit/07170d39738bec59952b0b952431ee2aecbf8f89))
7
+
8
+ ## [3.5.3](https://github.com/Gusto/apollo-federation-ruby/compare/v3.5.2...v3.5.3) (2023-03-17)
9
+
10
+
11
+ ### Features
12
+
13
+ * Support @inaccessible on remaining types ([#230](https://github.com/Gusto/apollo-federation-ruby/pull/230)) ([b62e6d9](https://github.com/Gusto/apollo-federation-ruby/commit/b62e6d9fc5ad0acae00746a80a622ac22dfa0c1f))
14
+
15
+ ### Bug Fixes
16
+
17
+ * Inherit federation directives ([#94](https://github.com/Gusto/apollo-federation-ruby/issues/94)) ([822f92b](https://github.com/Gusto/apollo-federation-ruby/commit/822f92b229f3437c474721082033b74c8cc63553))
18
+
19
+ ## [3.5.2](https://github.com/Gusto/apollo-federation-ruby/compare/v3.5.1...v3.5.2) (2023-03-08)
20
+
21
+
22
+ ### Reverts
23
+
24
+ * Revert "chore: use Github App for authentication (#216)" (#227) ([f503704](https://github.com/Gusto/apollo-federation-ruby/commit/f5037047da67d809c3ffcbcc20318cd3996fb2e1)), closes [#216](https://github.com/Gusto/apollo-federation-ruby/issues/216) [#227](https://github.com/Gusto/apollo-federation-ruby/issues/227)
25
+
26
+ ## [3.5.1](https://github.com/Gusto/apollo-federation-ruby/compare/v3.5.0...v3.5.1) (2023-03-08)
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * semantic PR title workflow ([#225](https://github.com/Gusto/apollo-federation-ruby/issues/225)) ([ee6041f](https://github.com/Gusto/apollo-federation-ruby/commit/ee6041f169b5f21427b704f4c71ee6dd0ea5c8b2))
32
+
33
+ # [3.5.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.4.1...v3.5.0) (2023-03-08)
34
+
35
+
36
+ ### Features
37
+
38
+ * add tag directive from federation v2 ([#210](https://github.com/Gusto/apollo-federation-ruby/issues/210)) ([8c6e112](https://github.com/Gusto/apollo-federation-ruby/commit/8c6e11251b7bc7d6888a7a2b6e0feeea70171cb0))
39
+
40
+ ## [3.4.1](https://github.com/Gusto/apollo-federation-ruby/compare/v3.4.0...v3.4.1) (2023-02-21)
41
+
42
+
43
+ ### Bug Fixes
44
+
45
+ * import [@inaccessible](https://github.com/inaccessible) directive in Fed v2.3 ([#220](https://github.com/Gusto/apollo-federation-ruby/issues/220)) ([5d4d86e](https://github.com/Gusto/apollo-federation-ruby/commit/5d4d86e8155efe83470c58012454c79c910219ff))
46
+
47
+ # [3.4.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.3.1...v3.4.0) (2023-02-21)
48
+
49
+
50
+ ### Bug Fixes
51
+
52
+ * address some lint violations ([#219](https://github.com/Gusto/apollo-federation-ruby/issues/219)) ([dcd11e9](https://github.com/Gusto/apollo-federation-ruby/commit/dcd11e9384f168d125d2b60941d4bff161799824))
53
+
54
+
55
+ ### Features
56
+
57
+ * add support for the [@interface](https://github.com/interface)Object directive ([#218](https://github.com/Gusto/apollo-federation-ruby/issues/218)) ([c7b987d](https://github.com/Gusto/apollo-federation-ruby/commit/c7b987de1d2b32a4a77ceb09718373ffa5a60abb))
58
+
59
+ ## [3.3.1](https://github.com/Gusto/apollo-federation-ruby/compare/v3.3.0...v3.3.1) (2023-01-05)
60
+
61
+
62
+ ### Bug Fixes
63
+
64
+ * address SNYK-RUBY-GOOGLEPROTOBUF-3167775 ([#212](https://github.com/Gusto/apollo-federation-ruby/issues/212)) ([c36b51e](https://github.com/Gusto/apollo-federation-ruby/commit/c36b51e521a60e8186100405cf81bba1a37f5978))
65
+
66
+ # [3.3.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.2.0...v3.3.0) (2022-08-24)
67
+
68
+
69
+ ### Features
70
+
71
+ * 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))
72
+
73
+ # [3.2.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.1.0...v3.2.0) (2022-08-15)
74
+
75
+
76
+ ### Features
77
+
78
+ * allow custom namespace for linked directives ([03fdfea](https://github.com/Gusto/apollo-federation-ruby/commit/03fdfeafaaea3c98ca4b7a734ce760ea08410530))
79
+
80
+ # [3.1.0](https://github.com/Gusto/apollo-federation-ruby/compare/v3.0.0...v3.1.0) (2022-06-21)
81
+
82
+
83
+ ### Features
84
+
85
+ * 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)
86
+
87
+ # [3.0.0](https://github.com/Gusto/apollo-federation-ruby/compare/v2.2.4...v3.0.0) (2022-04-05)
88
+
89
+
90
+ ### Bug Fixes
91
+
92
+ * camelize string fields to match sym behavior ([8f0382b](https://github.com/Gusto/apollo-federation-ruby/commit/8f0382b346d2cde5be252138275d67373b36acd7))
93
+
94
+
95
+ ### BREAKING CHANGES
96
+
97
+ * string fields will be camelized by default rather than passed as is.
98
+
1
99
  ## [2.2.4](https://github.com/Gusto/apollo-federation-ruby/compare/v2.2.3...v2.2.4) (2022-04-01)
2
100
 
3
101
 
data/README.md CHANGED
@@ -22,13 +22,21 @@ Or install it yourself as:
22
22
 
23
23
  ## Getting Started
24
24
 
25
- Include the `ApolloFederation::Field` module in your base field class:
25
+ Include the `ApolloFederation::Argument` module in your base argument class:
26
26
 
27
27
  ```ruby
28
- require 'apollo-federation'
28
+ class BaseArgument < GraphQL::Schema::Argument
29
+ include ApolloFederation::Argument
30
+ end
31
+ ```
29
32
 
33
+ Include the `ApolloFederation::Field` module in your base field class:
34
+
35
+ ```ruby
30
36
  class BaseField < GraphQL::Schema::Field
31
37
  include ApolloFederation::Field
38
+
39
+ argument_class BaseArgument
32
40
  end
33
41
  ```
34
42
 
@@ -53,6 +61,50 @@ module BaseInterface
53
61
  end
54
62
  ```
55
63
 
64
+ Include the `ApolloFederation::Union` module in your base union class:
65
+
66
+ ```ruby
67
+ class BaseUnion < GraphQL::Schema::Union
68
+ include ApolloFederation::Union
69
+ end
70
+ ```
71
+
72
+ Include the `ApolloFederation::EnumValue` module in your base enum value class:
73
+
74
+ ```ruby
75
+ class BaseEnumValue < GraphQL::Schema::EnumValue
76
+ include ApolloFederation::EnumValue
77
+ end
78
+ ```
79
+
80
+ Include the `ApolloFederation::Enum` module in your base enum class:
81
+
82
+ ```ruby
83
+ class BaseEnum < GraphQL::Schema::Enum
84
+ include ApolloFederation::Enum
85
+
86
+ enum_value_class BaseEnumValue
87
+ end
88
+ ```
89
+
90
+ Include the `ApolloFederation::InputObject` module in your base input object class:
91
+
92
+ ```ruby
93
+ class BaseInputObject < GraphQL::Schema::InputObject
94
+ include ApolloFederation::InputObject
95
+
96
+ argument_class BaseArgument
97
+ end
98
+ ```
99
+
100
+ Include the `ApolloFederation::Scalar` module in your base scalar class:
101
+
102
+ ```ruby
103
+ class BaseScalar < GraphQL::Schema::Scalar
104
+ include ApolloFederation::Scalar
105
+ end
106
+ ```
107
+
56
108
  Finally, include the `ApolloFederation::Schema` module in your schema:
57
109
 
58
110
  ```ruby
@@ -61,6 +113,15 @@ class MySchema < GraphQL::Schema
61
113
  end
62
114
  ```
63
115
 
116
+ **Optional:** To opt in to Federation v2, specify the version in your schema:
117
+
118
+ ```ruby
119
+ class MySchema < GraphQL::Schema
120
+ include ApolloFederation::Schema
121
+ federation version: '2.0'
122
+ end
123
+ ```
124
+
64
125
  ## Example
65
126
 
66
127
  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 +221,79 @@ end
160
221
  ```
161
222
  See [field set syntax](#field-set-syntax) for more details on the format of the `fields` option.
162
223
 
224
+ ### The `@shareable` directive (Apollo Federation v2)
225
+
226
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#shareable)
227
+
228
+ Call `shareable` within your class definition:
229
+
230
+ ```ruby
231
+ class User < BaseObject
232
+ shareable
233
+ end
234
+ ```
235
+
236
+ Pass the `shareable: true` option to your field definition:
237
+
238
+ ```ruby
239
+ class User < BaseObject
240
+ field :id, ID, null: false, shareable: true
241
+ end
242
+ ```
243
+
244
+ ### The `@inaccessible` directive (Apollo Federation v2)
245
+
246
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#inaccessible)
247
+
248
+ Call `inaccessible` within your class definition:
249
+
250
+ ```ruby
251
+ class User < BaseObject
252
+ inaccessible
253
+ end
254
+ ```
255
+
256
+ Pass the `inaccessible: true` option to your field definition:
257
+
258
+ ```ruby
259
+ class User < BaseObject
260
+ field :id, ID, null: false, inaccessible: true
261
+ end
262
+ ```
263
+
264
+ ### The `@override` directive (Apollo Federation v2)
265
+
266
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#override)
267
+
268
+ Pass the `override:` option to your field definition:
269
+
270
+ ```ruby
271
+ class Product < BaseObject
272
+ field :id, ID, null: false
273
+ field :inStock, Boolean, null: false, override: { from: 'Products' }
274
+ end
275
+ ```
276
+
277
+ ### The `@tag` directive (Apollo Federation v2)
278
+
279
+ [Apollo documentation](https://www.apollographql.com/docs/federation/federated-types/federated-directives/#tag)
280
+
281
+ Call `tag` within your class definition:
282
+
283
+ ```ruby
284
+ class User < BaseObject
285
+ tag name: 'private'
286
+ end
287
+ ```
288
+
289
+ Pass the `tags:` option to your field definition:
290
+
291
+ ```ruby
292
+ class User < BaseObject
293
+ field :id, ID, null: false, tags: [{ name: 'private' }]
294
+ end
295
+ ```
296
+
163
297
  ### Field set syntax
164
298
 
165
299
  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:
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apollo-federation/has_directives'
4
+
5
+ module ApolloFederation
6
+ module Argument
7
+ include HasDirectives
8
+
9
+ VERSION_2_DIRECTIVES = %i[tags inaccessible].freeze
10
+
11
+ def initialize(*args, **kwargs, &block)
12
+ add_v2_directives(**kwargs)
13
+
14
+ # Remove the custom kwargs
15
+ kwargs = kwargs.delete_if do |k, _|
16
+ VERSION_2_DIRECTIVES.include?(k)
17
+ end
18
+
19
+ # Pass on the default args:
20
+ super(*args, **kwargs, &block)
21
+ end
22
+
23
+ private
24
+
25
+ def add_v2_directives(tags: [], inaccessible: nil, **_kwargs)
26
+ tags.each do |tag|
27
+ add_directive(
28
+ name: 'tag',
29
+ arguments: [
30
+ name: 'name',
31
+ values: tag[:name],
32
+ ],
33
+ )
34
+ end
35
+
36
+ add_directive(name: 'inaccessible') if inaccessible
37
+ end
38
+ end
39
+ end
@@ -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
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apollo-federation/has_directives'
4
+
5
+ module ApolloFederation
6
+ module Enum
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ include HasDirectives
13
+
14
+ def tag(name:)
15
+ add_directive(name: 'tag', arguments: [name: 'name', values: name])
16
+ end
17
+
18
+ def inaccessible
19
+ add_directive(name: 'inaccessible')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apollo-federation/has_directives'
4
+
5
+ module ApolloFederation
6
+ module EnumValue
7
+ include HasDirectives
8
+
9
+ VERSION_2_DIRECTIVES = %i[tags inaccessible].freeze
10
+
11
+ def initialize(*args, **kwargs, &block)
12
+ add_v2_directives(**kwargs)
13
+
14
+ # Remove the custom kwargs
15
+ kwargs = kwargs.delete_if do |k, _|
16
+ VERSION_2_DIRECTIVES.include?(k)
17
+ end
18
+
19
+ # Pass on the default args:
20
+ super(*args, **kwargs, &block)
21
+ end
22
+
23
+ private
24
+
25
+ def add_v2_directives(tags: [], inaccessible: nil, **_kwargs)
26
+ tags.each do |tag|
27
+ add_directive(
28
+ name: 'tag',
29
+ arguments: [
30
+ name: 'name',
31
+ values: tag[:name],
32
+ ],
33
+ )
34
+ end
35
+
36
+ add_directive(name: 'inaccessible') if inaccessible
37
+ end
38
+ end
39
+ end
@@ -27,8 +27,38 @@ module ApolloFederation
27
27
  end
28
28
 
29
29
  def build_interface_type_node(interface_type)
30
- field_node = super
31
- merge_directives(field_node, interface_type)
30
+ interface_node = super
31
+ merge_directives(interface_node, interface_type)
32
+ end
33
+
34
+ def build_union_type_node(union_type)
35
+ union_node = super
36
+ merge_directives(union_node, union_type)
37
+ end
38
+
39
+ def build_enum_type_node(enum_type)
40
+ enum_node = super
41
+ merge_directives(enum_node, enum_type)
42
+ end
43
+
44
+ def build_enum_value_node(enum_value_type)
45
+ enum_value_node = super
46
+ merge_directives(enum_value_node, enum_value_type)
47
+ end
48
+
49
+ def build_scalar_type_node(scalar_type)
50
+ scalar_node = super
51
+ merge_directives(scalar_node, scalar_type)
52
+ end
53
+
54
+ def build_input_object_node(input_object_type)
55
+ input_object_node = super
56
+ merge_directives(input_object_node, input_object_type)
57
+ end
58
+
59
+ def build_argument_node(argument_type)
60
+ argument_node = super
61
+ merge_directives(argument_node, argument_type)
32
62
  end
33
63
 
34
64
  def build_field_node(field_type)
@@ -55,20 +85,28 @@ module ApolloFederation
55
85
 
56
86
  def merge_directives(node, type)
57
87
  if type.is_a?(ApolloFederation::HasDirectives)
58
- directives = type.federation_directives
88
+ directives = type.federation_directives || []
59
89
  else
60
90
  directives = []
61
91
  end
62
92
 
63
- (directives || []).each do |directive|
93
+ directives.each do |directive|
64
94
  node = node.merge_directive(
65
- name: directive[:name],
95
+ name: directive_name(directive),
66
96
  arguments: build_arguments_node(directive[:arguments]),
67
97
  )
68
98
  end
69
99
  node
70
100
  end
71
101
 
102
+ def directive_name(directive)
103
+ if schema.federation_2? && !Schema::IMPORTED_DIRECTIVES.include?(directive[:name])
104
+ "#{schema.link_namespace}__#{directive[:name]}"
105
+ else
106
+ directive[:name]
107
+ end
108
+ end
109
+
72
110
  def build_arguments_node(arguments)
73
111
  (arguments || []).map do |arg|
74
112
  GraphQL::Language::Nodes::Argument.new(name: arg[:name], value: arg[:values])
@@ -7,31 +7,88 @@ 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 tags].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, tags: [], **_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
+ tags.each do |tag|
82
+ add_directive(
83
+ name: 'tag',
84
+ arguments: [
85
+ name: 'name',
86
+ values: tag[:name],
87
+ ],
88
+ )
89
+ end
90
+
91
+ nil
35
92
  end
36
93
  end
37
94
  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
@@ -2,11 +2,20 @@
2
2
 
3
3
  module ApolloFederation
4
4
  module HasDirectives
5
- attr_reader :federation_directives
6
-
7
5
  def add_directive(name:, arguments: nil)
8
- @federation_directives ||= []
9
- @federation_directives << { name: name, arguments: arguments }
6
+ own_federation_directives << { name: name, arguments: arguments }
7
+ end
8
+
9
+ def federation_directives
10
+ if is_a?(Class)
11
+ own_federation_directives + find_inherited_value(:federation_directives, [])
12
+ else
13
+ own_federation_directives
14
+ end
15
+ end
16
+
17
+ def own_federation_directives
18
+ @own_federation_directives ||= []
10
19
  end
11
20
  end
12
21
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apollo-federation/has_directives'
4
+
5
+ module ApolloFederation
6
+ module InputObject
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ include HasDirectives
13
+
14
+ def tag(name:)
15
+ add_directive(name: 'tag', arguments: [name: 'name', values: name])
16
+ end
17
+
18
+ def inaccessible
19
+ add_directive(name: 'inaccessible')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -18,12 +18,20 @@ 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 tag(name:)
26
+ add_directive(name: 'tag', arguments: [name: 'name', values: name])
27
+ end
28
+
29
+ def key(fields:, camelize: true)
22
30
  add_directive(
23
31
  name: 'key',
24
32
  arguments: [
25
33
  name: 'fields',
26
- values: ApolloFederation::FieldSetSerializer.serialize(fields),
34
+ values: ApolloFederation::FieldSetSerializer.serialize(fields, camelize: camelize),
27
35
  ],
28
36
  )
29
37
  end
@@ -16,12 +16,28 @@ 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 interface_object
28
+ add_directive(name: 'interfaceObject')
29
+ end
30
+
31
+ def tag(name:)
32
+ add_directive(name: 'tag', arguments: [name: 'name', values: name])
33
+ end
34
+
35
+ def key(fields:, camelize: true)
20
36
  add_directive(
21
37
  name: 'key',
22
38
  arguments: [
23
39
  name: 'fields',
24
- values: ApolloFederation::FieldSetSerializer.serialize(fields),
40
+ values: ApolloFederation::FieldSetSerializer.serialize(fields, camelize: camelize),
25
41
  ],
26
42
  )
27
43
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apollo-federation/has_directives'
4
+
5
+ module ApolloFederation
6
+ module Scalar
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ include HasDirectives
13
+
14
+ def tag(name:)
15
+ add_directive(name: 'tag', arguments: [name: 'name', values: name])
16
+ end
17
+
18
+ def inaccessible
19
+ add_directive(name: 'inaccessible')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -7,14 +7,38 @@ require 'apollo-federation/federated_document_from_schema_definition.rb'
7
7
 
8
8
  module ApolloFederation
9
9
  module Schema
10
+ IMPORTED_DIRECTIVES = ['inaccessible', 'tag'].freeze
11
+
10
12
  def self.included(klass)
11
13
  klass.extend(CommonMethods)
12
14
  end
13
15
 
14
16
  module CommonMethods
17
+ DEFAULT_LINK_NAMESPACE = 'federation'
18
+
19
+ def federation(version: '1.0', link: {})
20
+ @federation_version = version
21
+ @link = { as: DEFAULT_LINK_NAMESPACE }.merge(link)
22
+ end
23
+
24
+ def federation_version
25
+ @federation_version || find_inherited_value(:federation_version, '1.0')
26
+ end
27
+
28
+ def federation_2?
29
+ Gem::Version.new(federation_version.to_s) >= Gem::Version.new('2.0.0')
30
+ end
31
+
15
32
  def federation_sdl(context: nil)
16
33
  document_from_schema = FederatedDocumentFromSchemaDefinition.new(self, context: context)
17
- GraphQL::Language::Printer.new.print(document_from_schema.document)
34
+
35
+ output = GraphQL::Language::Printer.new.print(document_from_schema.document)
36
+ output.prepend(federation_2_prefix) if federation_2?
37
+ output
38
+ end
39
+
40
+ def link_namespace
41
+ @link ? @link[:as] : find_inherited_value(:link_namespace)
18
42
  end
19
43
 
20
44
  def query(new_query_object = nil)
@@ -34,6 +58,16 @@ module ApolloFederation
34
58
 
35
59
  private
36
60
 
61
+ def federation_2_prefix
62
+ federation_namespace = ", as: \"#{link_namespace}\"" if link_namespace != DEFAULT_LINK_NAMESPACE
63
+
64
+ <<~SCHEMA
65
+ extend schema
66
+ @link(url: "https://specs.apollo.dev/federation/v2.3"#{federation_namespace}, import: [#{(IMPORTED_DIRECTIVES.map { |directive| "\"@#{directive}\"" }).join(', ')}])
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)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apollo-federation/has_directives'
4
+
5
+ module ApolloFederation
6
+ module Union
7
+ def self.included(klass)
8
+ klass.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ include HasDirectives
13
+
14
+ def tag(name:)
15
+ add_directive(name: 'tag', arguments: [name: 'name', values: name])
16
+ end
17
+
18
+ def inaccessible
19
+ add_directive(name: 'inaccessible')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApolloFederation
4
- VERSION = '2.2.4'
4
+ VERSION = '3.6.0'
5
5
  end
@@ -4,7 +4,13 @@ require 'apollo-federation/version'
4
4
  require 'apollo-federation/schema'
5
5
  require 'apollo-federation/object'
6
6
  require 'apollo-federation/interface'
7
+ require 'apollo-federation/union'
8
+ require 'apollo-federation/enum'
9
+ require 'apollo-federation/enum_value'
7
10
  require 'apollo-federation/field'
11
+ require 'apollo-federation/scalar'
12
+ require 'apollo-federation/input_object'
13
+ require 'apollo-federation/argument'
8
14
  require 'apollo-federation/tracing/proto'
9
15
  require 'apollo-federation/tracing/node_map'
10
16
  require 'apollo-federation/tracing/tracer'
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.4
4
+ version: 3.6.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-01 00:00:00.000000000 Z
12
+ date: 2023-03-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: graphql
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '3.19'
34
+ version: 3.21.7
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '3.19'
41
+ version: 3.21.7
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: actionpack
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -194,14 +194,19 @@ files:
194
194
  - bin/rspec
195
195
  - lib/apollo-federation.rb
196
196
  - lib/apollo-federation/any.rb
197
+ - lib/apollo-federation/argument.rb
197
198
  - lib/apollo-federation/entities_field.rb
198
199
  - lib/apollo-federation/entity.rb
200
+ - lib/apollo-federation/enum.rb
201
+ - lib/apollo-federation/enum_value.rb
199
202
  - lib/apollo-federation/federated_document_from_schema_definition.rb
200
203
  - lib/apollo-federation/field.rb
201
204
  - lib/apollo-federation/field_set_serializer.rb
202
205
  - lib/apollo-federation/has_directives.rb
206
+ - lib/apollo-federation/input_object.rb
203
207
  - lib/apollo-federation/interface.rb
204
208
  - lib/apollo-federation/object.rb
209
+ - lib/apollo-federation/scalar.rb
205
210
  - lib/apollo-federation/schema.rb
206
211
  - lib/apollo-federation/service.rb
207
212
  - lib/apollo-federation/service_field.rb
@@ -211,6 +216,7 @@ files:
211
216
  - lib/apollo-federation/tracing/proto/apollo.proto
212
217
  - lib/apollo-federation/tracing/proto/apollo_pb.rb
213
218
  - lib/apollo-federation/tracing/tracer.rb
219
+ - lib/apollo-federation/union.rb
214
220
  - lib/apollo-federation/version.rb
215
221
  homepage: https://github.com/Gusto/apollo-federation-ruby
216
222
  licenses: