apollo-federation 1.1.1 → 1.1.5

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: a520e4e58f160aa1029b433dc2b35cb3026a7b78a976510632fcfa6c85535208
4
- data.tar.gz: cc672c543e40bdc06d20635bf9ebd41b5832ec97868931c8759ae01bc5be6d80
3
+ metadata.gz: c257933d713beb9810af1a42d58a20ad693fa9c37b826bd8e78c45ede30eede6
4
+ data.tar.gz: 38226cd1a90d90cb92275768f5f012ee7e018fd80e5fd1fe78f02431aa235a1c
5
5
  SHA512:
6
- metadata.gz: 8f5ecaf9d65c0440b2a6ff9f7211a318ff8ef34daa48192615f7d9f33ee9b00362043249a221606b118cc1909c382fb150fd8a75ec92eedd50e816a61343a56f
7
- data.tar.gz: b39f9288f6928f1998647b63495127c98180f0b4287dcd6528c0b25b9f66c26df617405ac30b484d768df3022f341b491058dde22b33d78f217ffffeb16936fb
6
+ metadata.gz: b733f8f8f9cdcfc36484e0132d5cb515fa47b208e9ab54a60086b7a1f7b37a3cc4477dcee8e36092de99e5be2169e04be232f33142eb1bc7a8830f13e7c4fb66
7
+ data.tar.gz: 98da906cc451cc60d91acc35226e24dbcc97d2259af4267552c01ace5a2c4b4452edb979496b1ea94c5131d9ba11bbe76d17bea41ff00fbae5281d24318acf08
@@ -1,3 +1,38 @@
1
+ ## [1.1.5](https://github.com/Gusto/apollo-federation-ruby/compare/v1.1.4...v1.1.5) (2020-10-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * pass context as a Hash to GraphQL::Schema.federation_sdl ([c13a94e](https://github.com/Gusto/apollo-federation-ruby/commit/c13a94e6487471b47f05907bd4f83c03fa7e6af7))
7
+
8
+ ## [1.1.4](https://github.com/Gusto/apollo-federation-ruby/compare/v1.1.3...v1.1.4) (2020-09-25)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **tracing:** properly handle parsing and validation errors ([#102](https://github.com/Gusto/apollo-federation-ruby/issues/102)) ([a1c2a41](https://github.com/Gusto/apollo-federation-ruby/commit/a1c2a41d3d01f06364d439cdcc273f4678fed7bd)), closes [#101](https://github.com/Gusto/apollo-federation-ruby/issues/101) [#101](https://github.com/Gusto/apollo-federation-ruby/issues/101)
14
+
15
+ ## [1.1.4-beta.1](https://github.com/Gusto/apollo-federation-ruby/compare/v1.1.3...v1.1.4-beta.1) (2020-09-21)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **tracing:** properly handle parsing and validation errors ([#101](https://github.com/Gusto/apollo-federation-ruby/issues/101)) ([6cf8202](https://github.com/Gusto/apollo-federation-ruby/commit/6cf820281dd85bd358c6bf4c176b9a73a9280d54))
21
+
22
+ ## [1.1.3](https://github.com/Gusto/apollo-federation-ruby/compare/v1.1.2...v1.1.3) (2020-07-16)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * **tracing:** Properly handle tracing fields that resolve an array of lazy values ([#87](https://github.com/Gusto/apollo-federation-ruby/issues/87)) ([a9eed77](https://github.com/Gusto/apollo-federation-ruby/commit/a9eed77bbe5859456f93be00fbcafa02142ad5ed))
28
+
29
+ ## [1.1.2](https://github.com/Gusto/apollo-federation-ruby/compare/v1.1.1...v1.1.2) (2020-06-09)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * Fix _service field type owner ([#70](https://github.com/Gusto/apollo-federation-ruby/issues/70)) ([364e54f](https://github.com/Gusto/apollo-federation-ruby/commit/364e54fbb333b7cd4fe30f04bf72733b0e18d3f4))
35
+
1
36
  ## [1.1.1](https://github.com/Gusto/apollo-federation-ruby/compare/v1.1.0...v1.1.1) (2020-05-29)
2
37
 
3
38
 
data/README.md CHANGED
@@ -175,14 +175,6 @@ To support [federated tracing](https://www.apollographql.com/docs/apollo-server/
175
175
  # ...
176
176
  end
177
177
  ```
178
- 3. Change your controller to attach the traces to the response:
179
- ```ruby
180
- def execute
181
- # ...
182
- result = YourSchema.execute(query, ...)
183
- render json: ApolloFederation::Tracing.attach_trace_to_result(result)
184
- end
185
- ```
186
178
 
187
179
  ## Known Issues and Limitations
188
180
  - Only works with class-based schemas, the legacy `.define` API will not be supported
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath,)
14
+
15
+ bundle_binstub = File.expand_path('bundle', __dir__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'graphql'
4
4
  require 'apollo-federation/any'
5
- require 'apollo-federation/entity_type_resolution_extension'
6
5
 
7
6
  module ApolloFederation
8
7
  module EntitiesField
@@ -23,7 +22,6 @@ module ApolloFederation
23
22
 
24
23
  field(:_entities, [entity_type, null: true], null: false) do
25
24
  argument :representations, [Any], required: true
26
- extension(EntityTypeResolutionExtension)
27
25
  end
28
26
  end
29
27
  end
@@ -47,7 +45,14 @@ module ApolloFederation
47
45
  result = reference
48
46
  end
49
47
 
50
- [type, result]
48
+ context.schema.after_lazy(result) do |resolved_value|
49
+ # TODO: This isn't 100% correct: if (for some reason) 2 different resolve_reference calls
50
+ # return the same object, it might not have the right type
51
+ # Right now, apollo-federation just adds a __typename property to the result,
52
+ # but I don't really like the idea of modifying the resolved object
53
+ context[resolved_value] = type
54
+ resolved_value
55
+ end
51
56
  end
52
57
  end
53
58
  end
@@ -33,7 +33,7 @@ module ApolloFederation
33
33
  base = query_obj.metadata[:type_class]
34
34
  end
35
35
 
36
- Class.new(base) do
36
+ klass = Class.new(base) do
37
37
  # TODO: Maybe the name should inherit from the original Query name
38
38
  # Or MAYBE we should just modify the original class?
39
39
  graphql_name 'Query'
@@ -41,6 +41,9 @@ module ApolloFederation
41
41
  include EntitiesField
42
42
  include ServiceField
43
43
  end
44
+
45
+ klass.define_service_field
46
+ klass
44
47
  end
45
48
  end
46
49
 
@@ -5,13 +5,21 @@ require 'apollo-federation/service'
5
5
 
6
6
  module ApolloFederation
7
7
  module ServiceField
8
- extend GraphQL::Schema::Member::HasFields
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ extend GraphQL::Schema::Member::HasFields
9
14
 
10
- field(:_service, Service, null: false)
15
+ def define_service_field
16
+ field(:_service, Service, null: false)
17
+ end
18
+ end
11
19
 
12
20
  def _service
13
21
  schema_class = context.schema.is_a?(GraphQL::Schema) ? context.schema.class : context.schema
14
- { sdl: schema_class.federation_sdl(context: context) }
22
+ { sdl: schema_class.federation_sdl(context: context.to_h) }
15
23
  end
16
24
  end
17
25
  end
@@ -4,14 +4,6 @@ module ApolloFederation
4
4
  module Tracing
5
5
  KEY = :ftv1
6
6
  DEBUG_KEY = "#{KEY}_debug".to_sym
7
- class NotInstalledError < StandardError
8
- MESSAGE = 'Apollo Federation Tracing not installed. \
9
- Add `use ApolloFederation::Tracing` to your schema.'
10
-
11
- def message
12
- MESSAGE
13
- end
14
- end
15
7
 
16
8
  module_function
17
9
 
@@ -23,35 +15,10 @@ Add `use ApolloFederation::Tracing` to your schema.'
23
15
  headers && headers['apollo-federation-include-trace'] == KEY.to_s
24
16
  end
25
17
 
26
- def attach_trace_to_result(result)
27
- return result unless result.context[:tracing_enabled]
28
-
29
- trace = result.context.namespace(KEY)
30
- raise NotInstalledError unless trace[:start_time]
31
-
32
- result['errors']&.each do |error|
33
- trace[:node_map].add_error(error)
34
- end
35
-
36
- proto = ApolloFederation::Tracing::Trace.new(
37
- start_time: to_proto_timestamp(trace[:start_time]),
38
- end_time: to_proto_timestamp(trace[:end_time]),
39
- duration_ns: trace[:end_time_nanos] - trace[:start_time_nanos],
40
- root: trace[:node_map].root,
41
- )
42
-
43
- result[:extensions] ||= {}
44
- result[:extensions][KEY] = Base64.encode64(proto.class.encode(proto))
45
-
46
- if result.context[:debug_tracing]
47
- result[:extensions][DEBUG_KEY] = proto.to_h
48
- end
49
-
50
- result.to_h
51
- end
52
-
53
- def to_proto_timestamp(time)
54
- Google::Protobuf::Timestamp.new(seconds: time.to_i, nanos: time.nsec)
18
+ # @deprecated There is no need to call this method. Traces are added to the result automatically
19
+ def attach_trace_to_result(_result)
20
+ warn '[DEPRECATION] `attach_trace_to_result` is deprecated. There is no need to call it, as '\
21
+ 'traces are added to the result automatically'
55
22
  end
56
23
  end
57
24
  end
@@ -35,15 +35,15 @@ module ApolloFederation
35
35
  module Tracing
36
36
  module Tracer
37
37
  # store string constants to avoid creating new strings for each call to .trace
38
- EXECUTE_QUERY = 'execute_query'
38
+ EXECUTE_MULTIPLEX = 'execute_multiplex'
39
39
  EXECUTE_QUERY_LAZY = 'execute_query_lazy'
40
40
  EXECUTE_FIELD = 'execute_field'
41
41
  EXECUTE_FIELD_LAZY = 'execute_field_lazy'
42
42
 
43
43
  def self.trace(key, data, &block)
44
44
  case key
45
- when EXECUTE_QUERY
46
- execute_query(data, &block)
45
+ when EXECUTE_MULTIPLEX
46
+ execute_multiplex(data, &block)
47
47
  when EXECUTE_QUERY_LAZY
48
48
  execute_query_lazy(data, &block)
49
49
  when EXECUTE_FIELD
@@ -55,19 +55,26 @@ module ApolloFederation
55
55
  end
56
56
  end
57
57
 
58
- # Step 1:
59
- # Create a trace hash on the query context and record start times.
60
- def self.execute_query(data, &block)
61
- query = data.fetch(:query)
62
- return block.call unless query.context && query.context[:tracing_enabled]
58
+ def self.execute_multiplex(data, &block)
59
+ # Step 1:
60
+ # Create a trace hash on each query's context and record start times.
61
+ data.fetch(:multiplex).queries.each { |query| start_trace(query) }
62
+
63
+ results = block.call
64
+
65
+ # Step 5
66
+ # Attach the trace to the 'extensions' key of each result
67
+ results.map { |result| attach_trace_to_result(result) }
68
+ end
69
+
70
+ def self.start_trace(query)
71
+ return unless query.context && query.context[:tracing_enabled]
63
72
 
64
73
  query.context.namespace(ApolloFederation::Tracing::KEY).merge!(
65
74
  start_time: Time.now.utc,
66
75
  start_time_nanos: Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond),
67
76
  node_map: NodeMap.new,
68
77
  )
69
-
70
- block.call
71
78
  end
72
79
 
73
80
  # Step 4:
@@ -159,17 +166,27 @@ module ApolloFederation
159
166
 
160
167
  end_time_nanos = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
161
168
 
162
- # interpreter runtime
169
+ # legacy runtime
163
170
  if data.include?(:context)
164
- context = data.fetch(:context)
165
171
  path = context.path
166
- else # legacy runtime
167
- context = data.fetch(:query).context
172
+ field = context.field
173
+ else # interpreter runtime
168
174
  path = data.fetch(:path)
175
+ field = data.fetch(:field)
169
176
  end
170
177
 
171
178
  trace = context.namespace(ApolloFederation::Tracing::KEY)
172
179
 
180
+ # When a field is resolved with an array of lazy values, the interpreter fires an
181
+ # `execute_field` for the resolution of the field and then a `execute_field_lazy` event for
182
+ # each lazy value in the array. Since the path here will contain an index (indicating which
183
+ # lazy value we're executing: e.g. ['arrayOfLazies', 0]), we won't have a node for the path.
184
+ # We only care about the end of the parent field (e.g. ['arrayOfLazies']), so we get the
185
+ # node for that path. What ends up happening is we update the end_time for the parent node
186
+ # for each of the lazy values. The last one that's executed becomes the final end time.
187
+ if field.type.list? && path.last.is_a?(Integer)
188
+ path = path[0...-1]
189
+ end
173
190
  node = trace[:node_map].node_for_path(path)
174
191
  node.end_time = end_time_nanos - trace[:start_time_nanos]
175
192
 
@@ -178,6 +195,37 @@ module ApolloFederation
178
195
  result
179
196
  end
180
197
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
198
+
199
+ def self.attach_trace_to_result(result)
200
+ return result unless result.context[:tracing_enabled]
201
+
202
+ trace = result.context.namespace(ApolloFederation::Tracing::KEY)
203
+
204
+ result['errors']&.each do |error|
205
+ trace[:node_map].add_error(error)
206
+ end
207
+
208
+ proto = ApolloFederation::Tracing::Trace.new(
209
+ start_time: to_proto_timestamp(trace[:start_time]),
210
+ end_time: to_proto_timestamp(trace[:end_time]),
211
+ duration_ns: trace[:end_time_nanos] - trace[:start_time_nanos],
212
+ root: trace[:node_map].root,
213
+ )
214
+
215
+ result[:extensions] ||= {}
216
+ result[:extensions][ApolloFederation::Tracing::KEY] =
217
+ Base64.encode64(proto.class.encode(proto))
218
+
219
+ if result.context[:debug_tracing]
220
+ result[:extensions][ApolloFederation::Tracing::DEBUG_KEY] = proto.to_h
221
+ end
222
+
223
+ result
224
+ end
225
+
226
+ def self.to_proto_timestamp(time)
227
+ Google::Protobuf::Timestamp.new(seconds: time.to_i, nanos: time.nsec)
228
+ end
181
229
  end
182
230
  end
183
231
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApolloFederation
4
- VERSION = '1.1.1'
4
+ VERSION = '1.1.5'
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: 1.1.1
4
+ version: 1.1.5
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: 2020-05-29 00:00:00.000000000 Z
12
+ date: 2020-10-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: graphql
@@ -53,6 +53,34 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: appraisal
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: debase
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
56
84
  - !ruby/object:Gem::Dependency
57
85
  name: pry-byebug
58
86
  requirement: !ruby/object:Gem::Requirement
@@ -137,6 +165,20 @@ dependencies:
137
165
  - - ">="
138
166
  - !ruby/object:Gem::Version
139
167
  version: '0'
168
+ - !ruby/object:Gem::Dependency
169
+ name: ruby-debug-ide
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
140
182
  description: A Ruby implementation of Apollo Federation
141
183
  email:
142
184
  - noa.elad@gusto.com
@@ -149,13 +191,11 @@ files:
149
191
  - LICENSE
150
192
  - README.md
151
193
  - bin/generate-protos.sh
152
- - bin/prepare.rb
153
- - bin/publish.rb
194
+ - bin/rspec
154
195
  - lib/apollo-federation.rb
155
196
  - lib/apollo-federation/any.rb
156
197
  - lib/apollo-federation/entities_field.rb
157
198
  - lib/apollo-federation/entity.rb
158
- - lib/apollo-federation/entity_type_resolution_extension.rb
159
199
  - lib/apollo-federation/federated_document_from_schema_definition.rb
160
200
  - lib/apollo-federation/field.rb
161
201
  - lib/apollo-federation/has_directives.rb
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def set_version
4
- new_version = ARGV[0]
5
-
6
- contents = File.read('lib/apollo-federation/version.rb')
7
-
8
- new_contents = contents.gsub(/VERSION = '[0-9.]*'/, "VERSION = '#{new_version}'")
9
- File.write('lib/apollo-federation/version.rb', new_contents)
10
- end
11
-
12
- def bundle_install
13
- system('bundle install')
14
- end
15
-
16
- def build_gem
17
- system('gem build apollo-federation.gemspec')
18
- end
19
-
20
- set_version
21
- bundle_install
22
- build_gem
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- new_version = ARGV[0]
4
- gem_name = "apollo-federation-#{new_version}.gem"
5
- system("gem push #{gem_name}")
6
- File.delete(gem_name)
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class EntityTypeResolutionExtension < GraphQL::Schema::FieldExtension
4
- def after_resolve(value:, context:, **_rest)
5
- value.map do |type, result|
6
- context.schema.after_lazy(result) do |resolved_value|
7
- # TODO: This isn't 100% correct: if (for some reason) 2 different resolve_reference calls
8
- # return the same object, it might not have the right type
9
- # Right now, apollo-federation just adds a __typename property to the result,
10
- # but I don't really like the idea of modifying the resolved object
11
- context[resolved_value] = type
12
- resolved_value
13
- end
14
- end
15
- end
16
- end