apollo-federation 3.10.1 → 3.10.5
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 +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +5 -2
- data/lib/apollo-federation/entities_field.rb +79 -56
- data/lib/apollo-federation/next_execution_support.rb +18 -0
- data/lib/apollo-federation/service.rb +2 -1
- data/lib/apollo-federation/service_field.rb +10 -3
- data/lib/apollo-federation/tracing/tracer.rb +10 -2
- data/lib/apollo-federation/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b6e50afd09ae68365aa079f13a4e53ad681628d8a537166985d4da3eb6ef6ce
|
|
4
|
+
data.tar.gz: 8af107ff7dd73927fd241ad852916924ffe131db58da81facb37c8f75c88c95d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 742d3beea298bcac5bad03cd49181f587735d1a42e08d4fd1fb31b2ddcb5d948c30be2c6cbd24d453d8922e5992d9a225f314fb96ad6c5260369a86d84adb5ac
|
|
7
|
+
data.tar.gz: bc802e2a591d2c902c972f566362904b156054af8afe1800c1b861c1d2d062fbe552940f69d6814950c481fe06a839ed0e787c8edc49867dab90c1b288a0b28f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
## [3.10.3](https://github.com/Gusto/apollo-federation-ruby/compare/v3.10.2...v3.10.3) (2025-10-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* random update to README.md to trigger semantic-release ([#310](https://github.com/Gusto/apollo-federation-ruby/issues/310)) ([bfaa10e](https://github.com/Gusto/apollo-federation-ruby/commit/bfaa10e85a7e85781f2ae0373d656b0fc66d13eb))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Reverts
|
|
10
|
+
|
|
11
|
+
* Revert "chore(release): 3.10.2 [skip ci]" (#309) ([a3937c6](https://github.com/Gusto/apollo-federation-ruby/commit/a3937c64f0ce9648edc10f74a80a4e825e01b4d4)), closes [#309](https://github.com/Gusto/apollo-federation-ruby/issues/309)
|
|
12
|
+
|
|
13
|
+
## [3.10.5](https://github.com/Gusto/apollo-federation-ruby/compare/v3.10.4...v3.10.5) (2026-10-01)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* add environment: release to the Publish Gem job ([#323](https://github.com/Gusto/apollo-federation-ruby/issues/323)) ([abbe9c6](https://github.com/Gusto/apollo-federation-ruby/commit/abbe9c652bf4fcfd5a23c238981a91bc5ad47da1))
|
|
19
|
+
|
|
20
|
+
## [3.10.4](https://github.com/Gusto/apollo-federation-ruby/compare/v3.10.3...v3.10.4) (2026-09-30)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* authenticate release-please-action with a GitHub App token, not GITHUB_TOKEN ([#320](https://github.com/Gusto/apollo-federation-ruby/issues/320)) ([0231a15](https://github.com/Gusto/apollo-federation-ruby/commit/0231a1573253739573fa2a8b17aeb9a124e73728))
|
|
26
|
+
* Query._entities crashing under GraphQL::Execution::Next ([#317](https://github.com/Gusto/apollo-federation-ruby/issues/317)) ([4b869ba](https://github.com/Gusto/apollo-federation-ruby/commit/4b869ba7b4e5377cef0a08c8967386f38b6b7b97))
|
|
27
|
+
* regenerate Gemfile.lock on the release PR after version bumps ([#322](https://github.com/Gusto/apollo-federation-ruby/issues/322)) ([c6122dc](https://github.com/Gusto/apollo-federation-ruby/commit/c6122dcccb8d246642bc799f5b4e7fb8b749b84c))
|
|
28
|
+
* restore environment: release on the release-please job ([#321](https://github.com/Gusto/apollo-federation-ruby/issues/321)) ([61904d8](https://github.com/Gusto/apollo-federation-ruby/commit/61904d86023f14f6fbd661417e60ecc7ce166ec4))
|
|
29
|
+
|
|
1
30
|
## [3.10.1](https://github.com/Gusto/apollo-federation-ruby/compare/v3.10.0...v3.10.1) (2025-04-02)
|
|
2
31
|
|
|
3
32
|
|
data/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# apollo-federation
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Gusto/apollo-federation-ruby/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
5
|
This gem extends the [GraphQL Ruby](http://graphql-ruby.org/) gem to add support for creating an [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction/) schema.
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
## Installation
|
|
8
9
|
|
|
9
10
|
Add this line to your application's Gemfile:
|
|
@@ -144,7 +145,9 @@ This will start up the gateway and serve it at http://localhost:5000.
|
|
|
144
145
|
|
|
145
146
|
## Usage
|
|
146
147
|
|
|
147
|
-
The API is designed to mimic the API of [Apollo's federation library](https://www.apollographql.com/docs/apollo-server/federation/introduction/).
|
|
148
|
+
The API is designed to mimic the API of [Apollo's federation library](https://www.apollographql.com/docs/apollo-server/federation/introduction/).
|
|
149
|
+
|
|
150
|
+
It's best to read and understand the way federation works, in general, before attempting to use this library.
|
|
148
151
|
|
|
149
152
|
### Extending a type
|
|
150
153
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'graphql'
|
|
4
4
|
require 'apollo-federation/any'
|
|
5
|
+
require 'apollo-federation/next_execution_support'
|
|
5
6
|
|
|
6
7
|
module ApolloFederation
|
|
7
8
|
module EntitiesField
|
|
@@ -20,80 +21,102 @@ module ApolloFederation
|
|
|
20
21
|
possible_types(*possible_entities)
|
|
21
22
|
end
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
# resolve_static so Next dispatches to resolve_entities_for_next, not root_value; a
|
|
25
|
+
# separate method from _entities since Next needs a fully-synced Array, not a Lazy.
|
|
26
|
+
if ApolloFederation::RESOLVE_STATIC_SUPPORTED
|
|
27
|
+
entities_field_options = { resolve_static: :resolve_entities_for_next }
|
|
28
|
+
else
|
|
29
|
+
entities_field_options = {}
|
|
30
|
+
end
|
|
31
|
+
field(:_entities, [entity_type, null: true], null: false, **entities_field_options) do
|
|
24
32
|
argument :representations, [Any], required: true
|
|
25
33
|
end
|
|
26
34
|
end
|
|
27
|
-
end
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
representations
|
|
33
|
-
.map
|
|
34
|
-
.with_index { |r, i| [r, i] }
|
|
35
|
-
.group_by { |(r, _i)| r[:__typename] }
|
|
36
|
-
|
|
37
|
-
maybe_lazies = grouped_references_with_indices.map do |typename, references_with_indices|
|
|
38
|
-
references = references_with_indices.map(&:first)
|
|
39
|
-
indices = references_with_indices.map(&:last)
|
|
40
|
-
|
|
41
|
-
# TODO: Use warden or schema?
|
|
42
|
-
type = context.warden.get_type(typename)
|
|
43
|
-
if type.nil? || type.kind != GraphQL::TypeKinds::OBJECT
|
|
44
|
-
# TODO: Raise a specific error class?
|
|
45
|
-
raise "The _entities resolver tried to load an entity for type \"#{typename}\"," \
|
|
46
|
-
' but no object type of that name was found in the schema'
|
|
47
|
-
end
|
|
36
|
+
def _entities(context, representations:)
|
|
37
|
+
build_entities(context, representations)
|
|
38
|
+
end
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
def resolve_entities_for_next(context, representations:)
|
|
41
|
+
final_result = build_entities(context, representations).value
|
|
42
|
+
# Resolving the outer Lazy doesn't resolve each entry: a per-reference resolve_reference
|
|
43
|
+
# lazy value comes back wrapped in its own Lazy from the after_lazy call in build_entities.
|
|
44
|
+
final_result.map { |entry| entry.is_a?(GraphQL::Execution::Lazy) ? entry.value : entry }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def build_entities(context, representations)
|
|
50
|
+
final_result = Array.new(representations.size)
|
|
51
|
+
grouped_references_with_indices =
|
|
52
|
+
representations
|
|
53
|
+
.map
|
|
54
|
+
.with_index { |r, i| [r, i] }
|
|
55
|
+
.group_by { |(r, _i)| r[:__typename] }
|
|
56
|
+
|
|
57
|
+
maybe_lazies = grouped_references_with_indices.map do |typename, references_with_indices|
|
|
58
|
+
references = references_with_indices.map(&:first)
|
|
59
|
+
indices = references_with_indices.map(&:last)
|
|
60
|
+
|
|
61
|
+
# TODO: Use warden or schema?
|
|
62
|
+
type = context.warden.get_type(typename)
|
|
63
|
+
if type.nil? || type.kind != GraphQL::TypeKinds::OBJECT
|
|
64
|
+
# TODO: Raise a specific error class?
|
|
65
|
+
raise "The _entities resolver tried to load an entity for type \"#{typename}\"," \
|
|
66
|
+
' but no object type of that name was found in the schema'
|
|
67
|
+
end
|
|
51
68
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
# TODO: What if the type is an interface?
|
|
70
|
+
type_class = class_of_type(type)
|
|
71
|
+
|
|
72
|
+
if type_class.underscore_reference_keys
|
|
73
|
+
references.map! do |reference|
|
|
74
|
+
reference.transform_keys do |key|
|
|
75
|
+
GraphQL::Schema::Member::BuildType.underscore(key.to_s).to_sym
|
|
76
|
+
end
|
|
56
77
|
end
|
|
57
78
|
end
|
|
58
|
-
end
|
|
59
79
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
80
|
+
if type_class.respond_to?(:resolve_references)
|
|
81
|
+
results = type_class.resolve_references(references, context)
|
|
82
|
+
elsif type_class.respond_to?(:resolve_reference)
|
|
83
|
+
results = references.map { |reference| type_class.resolve_reference(reference, context) }
|
|
84
|
+
else
|
|
85
|
+
results = references
|
|
86
|
+
end
|
|
67
87
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
context.schema.after_lazy(results) do |resolved_results|
|
|
89
|
+
resolved_results.zip(indices).each do |result, i|
|
|
90
|
+
final_result[i] = context.schema.after_lazy(result) do |resolved_value|
|
|
91
|
+
# TODO: This isn't 100% correct: if (for some reason) 2 different resolve_reference
|
|
92
|
+
# calls return the same object, it might not have the right type
|
|
93
|
+
# Right now, apollo-federation just adds a __typename property to the result,
|
|
94
|
+
# but I don't really like the idea of modifying the resolved object
|
|
95
|
+
context[resolved_value] = type
|
|
96
|
+
resolved_value
|
|
97
|
+
end
|
|
77
98
|
end
|
|
78
99
|
end
|
|
79
100
|
end
|
|
101
|
+
|
|
102
|
+
# Make sure we've resolved the outer level of lazies so we can return an array with a possibly lazy
|
|
103
|
+
# entry for each requested entity
|
|
104
|
+
GraphQL::Execution::Lazy.all(maybe_lazies).then do
|
|
105
|
+
final_result
|
|
106
|
+
end
|
|
80
107
|
end
|
|
81
108
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
109
|
+
def class_of_type(type)
|
|
110
|
+
if defined?(GraphQL::ObjectType) && type.is_a?(GraphQL::ObjectType)
|
|
111
|
+
type.metadata[:type_class]
|
|
112
|
+
else
|
|
113
|
+
type
|
|
114
|
+
end
|
|
86
115
|
end
|
|
87
116
|
end
|
|
88
117
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def class_of_type(type)
|
|
92
|
-
if defined?(GraphQL::ObjectType) && type.is_a?(GraphQL::ObjectType)
|
|
93
|
-
type.metadata[:type_class]
|
|
94
|
-
else
|
|
95
|
-
type
|
|
96
|
-
end
|
|
118
|
+
def _entities(representations:)
|
|
119
|
+
self.class._entities(context, representations: representations)
|
|
97
120
|
end
|
|
98
121
|
end
|
|
99
122
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'graphql'
|
|
4
|
+
|
|
5
|
+
module ApolloFederation
|
|
6
|
+
# `resolve_static:` (and GraphQL::Execution::Next generally) doesn't exist on every
|
|
7
|
+
# graphql-ruby version this gem supports, so detect it via a capability the Next-era
|
|
8
|
+
# GraphQL::Schema::Field always defines, rather than assuming a minimum graphql-ruby version.
|
|
9
|
+
# Checking Field#initialize's own parameter list (an earlier version of this check) breaks
|
|
10
|
+
# under graphql-pro, which prepends a `(*args, streamable:, **kwargs)`-shaped `initialize` onto
|
|
11
|
+
# Field -- `instance_method(:initialize)` then reports *that* signature, not the one
|
|
12
|
+
# underneath, so the check silently reads as unsupported. `execution_mode` is a plain
|
|
13
|
+
# `attr_reader`, untouched by that prepend either way. Shared by any field this gem defines
|
|
14
|
+
# directly on the Query type (currently _entities and _service), since those are resolved
|
|
15
|
+
# against the wrapped Query object under classic but the (nil, by default) Query root_value
|
|
16
|
+
# under Next's default :direct_send -- resolve_static is what makes them agree.
|
|
17
|
+
RESOLVE_STATIC_SUPPORTED = GraphQL::Schema::Field.method_defined?(:execution_mode)
|
|
18
|
+
end
|
|
@@ -9,6 +9,7 @@ module ApolloFederation
|
|
|
9
9
|
'directives, removes federation types, and includes rest of full schema after schema ' \
|
|
10
10
|
'directives have been applied'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# hash_key: so Next reads the { sdl: ... } Hash (see ServiceField#_service) directly.
|
|
13
|
+
field(:sdl, String, null: true, hash_key: :sdl)
|
|
13
14
|
end
|
|
14
15
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'graphql'
|
|
4
4
|
require 'apollo-federation/service'
|
|
5
|
+
require 'apollo-federation/next_execution_support'
|
|
5
6
|
|
|
6
7
|
module ApolloFederation
|
|
7
8
|
module ServiceField
|
|
@@ -13,13 +14,19 @@ module ApolloFederation
|
|
|
13
14
|
extend GraphQL::Schema::Member::HasFields
|
|
14
15
|
|
|
15
16
|
def define_service_field
|
|
16
|
-
|
|
17
|
+
# resolve_static so Next dispatches to the class method below, not root_value.
|
|
18
|
+
service_field_options = ApolloFederation::RESOLVE_STATIC_SUPPORTED ? { resolve_static: true } : {}
|
|
19
|
+
field(:_service, Service, null: false, **service_field_options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def _service(context)
|
|
23
|
+
schema_class = context.schema.is_a?(GraphQL::Schema) ? context.schema.class : context.schema
|
|
24
|
+
{ sdl: schema_class.federation_sdl(context: context.to_h) }
|
|
17
25
|
end
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def _service
|
|
21
|
-
|
|
22
|
-
{ sdl: schema_class.federation_sdl(context: context.to_h) }
|
|
29
|
+
self.class._service(context)
|
|
23
30
|
end
|
|
24
31
|
end
|
|
25
32
|
end
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
#
|
|
27
27
|
# </execute_query_lazy>
|
|
28
28
|
#
|
|
29
|
-
# # `execute_query_lazy` *always* fires
|
|
30
|
-
# #
|
|
29
|
+
# # In graphql-ruby < 2.5.12, `execute_query_lazy` *always* fires.
|
|
30
|
+
# # In graphql-ruby >= 2.5.12, `execute_query_lazy` only fires when there are lazy values.
|
|
31
|
+
# # We record end times in both `execute_multiplex` (as a fallback) and `execute_query_lazy`
|
|
32
|
+
# # (to capture the full execution time including lazy resolution when present).
|
|
31
33
|
#
|
|
32
34
|
# </execute_multiplex>
|
|
33
35
|
|
|
@@ -96,6 +98,12 @@ module ApolloFederation
|
|
|
96
98
|
|
|
97
99
|
results = block.call
|
|
98
100
|
|
|
101
|
+
# Step 4.5:
|
|
102
|
+
# Record end times for all queries.
|
|
103
|
+
# This acts as a fallback for queries without lazy values.
|
|
104
|
+
# execute_query_lazy will overwrite these times if lazy values are present.
|
|
105
|
+
data.fetch(:multiplex).queries.each { |query| record_trace_end_time(query) }
|
|
106
|
+
|
|
99
107
|
# Step 5
|
|
100
108
|
# Attach the trace to the 'extensions' key of each result
|
|
101
109
|
results.map { |result| attach_trace_to_result(result) }
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: apollo-federation
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.10.
|
|
4
|
+
version: 3.10.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Noa Elad
|
|
8
8
|
- Rylan Collins
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: graphql
|
|
@@ -232,6 +232,7 @@ files:
|
|
|
232
232
|
- lib/apollo-federation/has_directives.rb
|
|
233
233
|
- lib/apollo-federation/input_object.rb
|
|
234
234
|
- lib/apollo-federation/interface.rb
|
|
235
|
+
- lib/apollo-federation/next_execution_support.rb
|
|
235
236
|
- lib/apollo-federation/object.rb
|
|
236
237
|
- lib/apollo-federation/scalar.rb
|
|
237
238
|
- lib/apollo-federation/schema.rb
|
|
@@ -267,7 +268,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
267
268
|
- !ruby/object:Gem::Version
|
|
268
269
|
version: '0'
|
|
269
270
|
requirements: []
|
|
270
|
-
rubygems_version: 3.6.
|
|
271
|
+
rubygems_version: 3.6.9
|
|
271
272
|
specification_version: 4
|
|
272
273
|
summary: A Ruby implementation of Apollo Federation
|
|
273
274
|
test_files: []
|