apollo-tracing 1.0.0 → 1.1.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
  SHA1:
3
- metadata.gz: ce351de83cfe780dc152768b22274e822bd3bd02
4
- data.tar.gz: 77e54bc35f506288f4456828f7968694bea3eab3
3
+ metadata.gz: 5427c1c7ad4c390d7ea73e5089b4291cbb6012c6
4
+ data.tar.gz: 06cb235d0c59d3e0fae40e7ac7b1a09b2deb4709
5
5
  SHA512:
6
- metadata.gz: b9fb6347e03e0d62173308209d46316aa5ce5b49f0c858d11367c201a1b3d660d3bb8790f8f3e1e8d374f16d9a20004ea25f10acd65a432a89dfdbaea77c56d4
7
- data.tar.gz: d611be717f55895b345c1399ff2b39be246e46e0ae0425ce111c04924d0e1737c95943edbcba15b7b39106cf7cb6949fecd36ebafc6e15f9cc1862b2f3bceff3
6
+ metadata.gz: 8a69276f3105a1e7deff72b05e5adad500848a3160118672fb3708016a591eacea074c23b73cf8150a7781c4e501980d42ba2d5ef7dafc712463d8d9515a5fdb
7
+ data.tar.gz: f9b06d4e3d7b14a56f96a01c48640ec92f04a9042c1ebb5254a8781117f98280f5816c79ea3c8826b8748a626590deed35c2dfdbff85780c3a7a53a2e7628f64
data/.gitignore CHANGED
@@ -7,6 +7,7 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /bin/engineproxy*
10
11
 
11
12
  # rspec failure tracking
12
13
  .rspec_status
@@ -2,4 +2,4 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.3.4
5
- script: bundle exec rspec
5
+ script: make download_binaries && bundle exec rspec
@@ -8,10 +8,14 @@ one of the following labels: `Added`, `Changed`, `Deprecated`,
8
8
  to manage the versions of this gem so
9
9
  that you can set version constraints properly.
10
10
 
11
- #### [Unreleased](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.0.0...HEAD)
11
+ #### [Unreleased](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.1.0...HEAD)
12
12
 
13
13
  * WIP
14
14
 
15
+ #### [v1.1.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.0.0..v1.1.0) – 2017-10-25
16
+
17
+ * `Added`: Apollo Engine Proxy version [2017.10-408-g497e1410](https://www.apollographql.com/docs/engine/proxy-release-notes.html). ([#2](https://github.com/uniiverse/apollo-tracing-ruby/pull/2))
18
+
15
19
  #### [v1.0.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v0.1.1...v1.0.0) – 2017-10-17
16
20
 
17
21
  * `Changed`: the gem name from `graphql-tracing` to `apollo-tracing`.
@@ -0,0 +1,17 @@
1
+ download_binaries:
2
+ curl -O https://registry.npmjs.org/apollo-engine-binary-darwin/-/apollo-engine-binary-darwin-0.2017.10-408-g497e1410.tgz
3
+ curl -O https://registry.npmjs.org/apollo-engine-binary-darwin/-/apollo-engine-binary-linux-0.2017.10-408-g497e1410.tgz
4
+ curl -O https://registry.npmjs.org/apollo-engine-binary-darwin/-/apollo-engine-binary-windows-0.2017.10-408-g497e1410.tgz
5
+ tar -xzf apollo-engine-binary-darwin-0.2017.10-408-g497e1410.tgz
6
+ tar -xzf apollo-engine-binary-linux-0.2017.10-408-g497e1410.tgz
7
+ tar -xzf apollo-engine-binary-windows-0.2017.10-408-g497e1410.tgz
8
+ mv package/engineproxy_darwin_amd64 bin/
9
+ mv package/engineproxy_linux_amd64 bin/
10
+ mv package/engineproxy_windows_amd64.exe bin/
11
+ rm -r package/
12
+ rm apollo-engine-binary-darwin-0.2017.10-408-g497e1410.tgz
13
+ rm apollo-engine-binary-linux-0.2017.10-408-g497e1410.tgz
14
+ rm apollo-engine-binary-windows-0.2017.10-408-g497e1410.tgz
15
+
16
+ release: download_binaries
17
+ bundle exec rake release
data/README.md CHANGED
@@ -4,6 +4,17 @@
4
4
 
5
5
  Ruby implementation of [GraphQL](https://github.com/rmosolgo/graphql-ruby) trace data in the [Apollo Tracing](https://github.com/apollographql/apollo-tracing) format.
6
6
 
7
+
8
+ ## Contents
9
+
10
+ * [Installation](#installation)
11
+ * [Usage](#usage)
12
+ * [Tracing](#tracing)
13
+ * [Engine Proxy](#engine-proxy)
14
+ * [Development](#development)
15
+ * [Contributing](#contributing)
16
+ * [License](#license)
17
+
7
18
  ## Installation
8
19
 
9
20
  Add this line to your application's Gemfile:
@@ -49,16 +60,18 @@ Schema = GraphQL::Schema.define do
49
60
  end
50
61
 
51
62
  # Execute query
52
- query = "query($user_id: ID!) {
53
- posts(user_id: $user_id) {
54
- id
55
- title
56
- }
57
- }"
63
+ query = "
64
+ query($user_id: ID!) {
65
+ posts(user_id: $user_id) {
66
+ id
67
+ title
68
+ }
69
+ }
70
+ "
58
71
  Schema.execute(query, variables: { user_id: 1 })
59
72
  ```
60
73
 
61
- ### Setup Tracing
74
+ ### Tracing
62
75
 
63
76
  Add 'ApolloTracing' to your schema:
64
77
 
@@ -141,6 +154,89 @@ Now your response should look something like:
141
154
  }
142
155
  ```
143
156
 
157
+ ### Engine Proxy
158
+
159
+ Now you can start using the [Apollo Engine](https://www.apollographql.com/engine/) service.
160
+ Here is the general architecture overview of a sidecar mode – Proxy runs next to your application server:
161
+
162
+ ```
163
+ ----------------- request ----------------- request -----------------
164
+ | | -----------> | | -----------> | |
165
+ | Client | | Engine Proxy | | Application |
166
+ | | <----------- | | <----------- | |
167
+ ----------------- response ----------------- response -----------------
168
+ |
169
+ |
170
+ GraphQL tracing data |
171
+ |
172
+ ˅
173
+ -----------------
174
+ | |
175
+ | Apollo Engine |
176
+ | |
177
+ -----------------
178
+ ```
179
+
180
+ `ApolloTracing` gem comes with the [Apollo Engine Proxy](https://www.apollographql.com/docs/engine/index.html#engine-proxy) binary written in Go.
181
+ To configure the Proxy create a Proxy config file:
182
+
183
+ ```
184
+ # config/apollo-engine-proxy.json
185
+
186
+ {
187
+ "apiKey": "service:YOUR_ENGINE_API_KEY",
188
+ "logging": { "level": "INFO" },
189
+ "origins": [{
190
+ "http": { "url": "http://localhost:3000/graphql" }
191
+ }],
192
+ "frontends": [{
193
+ "host": "127.0.0.1", "port": 3001, "endpoint": "/graphql"
194
+ }]
195
+ }
196
+ ```
197
+
198
+ * `apiKey` – get this on your [Apollo Engine](https://engine.apollographql.com/) home page.
199
+ * `logging.level` – a log level for the Proxy ("INFO", "DEBUG" or "ERROR").
200
+ * `origins` – a list of URLs with your GraphQL endpoints in the Application.
201
+ * `frontends` – an address on which the Proxy will be listening.
202
+
203
+ To run the Proxy as a child process, which will be automatically terminated if the Application proccess stoped, add the following line to the `config.ru` file:
204
+
205
+ <pre>
206
+ # config.ru – this file is used by Rack-based servers to start the application.
207
+ require File.expand_path('../config/environment', __FILE__)
208
+
209
+ <b>ApolloTracing.start_proxy('config/apollo-engine-proxy.json')</b>
210
+ run Your::Application
211
+ </pre>
212
+
213
+ For example, if you use [rails](https://github.com/rails/rails) with [puma](https://github.com/puma/puma) application server and run it like:
214
+
215
+ ```
216
+ bundle exec puma -w 2 -t 16 -p 3001
217
+ ```
218
+
219
+ The proccess tree may look like:
220
+
221
+ ```
222
+ ---------------
223
+ | Puma Master |
224
+ | Port 3000 |
225
+ ---------------
226
+ | |
227
+ ---------- ----------
228
+ | | ----------------
229
+ ˅ -> | Puma Worker1 |
230
+ ---------------- | -----------------
231
+ | Engine Proxy | | ----------------
232
+ | Port 3001 | -> | Puma Worker2 |
233
+ ---------------- ----------------
234
+ ```
235
+
236
+ Now you can send requests to the reverse Proxy `http://localhost:3001`.
237
+ It'll proxy any (GraphQL and non-GraphQL) requests to the Application `http://localhost:3000`.
238
+ If the request matches the endpoints described in `origins`, it'll strip the `tracing` data from the response and will send it to the Apollo Engine service.
239
+
144
240
  ## Development
145
241
 
146
242
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -154,4 +250,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/uniive
154
250
  ## License
155
251
 
156
252
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
157
-
@@ -14,9 +14,14 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/uniiverse/apollo-tracing-ruby"
15
15
  spec.license = "MIT"
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features)/})
19
- end
17
+ spec.files =
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^test/}) } +
19
+ %w[
20
+ bin/engineproxy_darwin_amd64
21
+ bin/engineproxy_linux_amd64
22
+ bin/engineproxy_windows_amd64.exe
23
+ ]
24
+
20
25
  spec.bindir = "exe"
21
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
27
  spec.require_paths = ["lib"]
data/bin/setup CHANGED
@@ -4,5 +4,6 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
+ make download_binaries
7
8
 
8
9
  # Do any other automated setup that you need to do here
@@ -4,6 +4,43 @@ require "graphql"
4
4
  require "apollo_tracing/version"
5
5
 
6
6
  class ApolloTracing
7
+ def self.start_proxy(config_filepath = 'config/apollo-engine.json')
8
+ config_json = File.read(config_filepath)
9
+ binary_path =
10
+ if RUBY_PLATFORM.include?('darwin')
11
+ File.expand_path('../../bin/engineproxy_darwin_amd64', __FILE__)
12
+ elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
13
+ File.expand_path('../../bin/engineproxy_windows_amd64.exe', __FILE__)
14
+ else
15
+ File.expand_path('../../bin/engineproxy_linux_amd64', __FILE__)
16
+ end
17
+
18
+ @@proxy_pid = spawn(
19
+ {"ENGINE_CONFIG" => config_json},
20
+ "#{binary_path} -config=env -restart=true",
21
+ {out: STDOUT, err: STDERR}
22
+ )
23
+ at_exit { stop_proxy }
24
+ Process.detach(@@proxy_pid)
25
+ @@proxy_pid
26
+ end
27
+
28
+ def self.stop_proxy
29
+ Process.getpgid(@@proxy_pid)
30
+ Process.kill('TERM', @@proxy_pid)
31
+
32
+ 3.times do
33
+ Process.getpgid(@@proxy_pid)
34
+ sleep 1
35
+ end
36
+
37
+ Process.getpgid(@@proxy_pid)
38
+ puts "Couldn't cleanly terminate the Apollo Engine Proxy in 3 seconds!"
39
+ Process.kill('KILL', @@proxy_pid)
40
+ rescue Errno::ESRCH
41
+ # process does not exist
42
+ end
43
+
7
44
  def use(schema_definition)
8
45
  schema_definition.instrument(:query, self)
9
46
  schema_definition.instrument(:field, self)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ApolloTracing
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -0,0 +1,105 @@
1
+ require "spec_helper"
2
+
3
+ require 'fixtures/user'
4
+ require 'fixtures/post'
5
+ require 'fixtures/schema'
6
+
7
+ RSpec.describe ApolloTracing do
8
+ describe '.start_proxy' do
9
+ it 'runs a proxy' do
10
+ pid = ApolloTracing.start_proxy('spec/fixtures/apollo-engine-proxy.json')
11
+ expect { Process.getpgid(pid) }.not_to raise_error
12
+ ApolloTracing.stop_proxy
13
+ end
14
+ end
15
+
16
+ describe '.stop_proxy' do
17
+ it 'stops a proxy' do
18
+ pid = ApolloTracing.start_proxy('spec/fixtures/apollo-engine-proxy.json')
19
+ ApolloTracing.stop_proxy
20
+ expect { Process.getpgid(pid) }.to raise_error(Errno::ESRCH, 'No such process')
21
+ end
22
+ end
23
+
24
+ context 'introspection' do
25
+ it 'returns time in RFC 3339 format' do
26
+ query = "query($user_id: ID!) { posts(user_id: $user_id) { id title user_id } }"
27
+ now = Time.new(2017, 8, 25, 0, 0, 0, '+00:00')
28
+ allow(Time).to receive(:now).and_return(now)
29
+
30
+ result = Schema.execute(query, variables: {'user_id' => "1"})
31
+
32
+ expect(result.dig("extensions", 'tracing', 'startTime')).to eq('2017-08-25T00:00:00.000Z')
33
+ expect(result.dig("extensions", 'tracing', 'endTime')).to eq('2017-08-25T00:00:00.000Z')
34
+ end
35
+
36
+ it "resolves graphql query with tracing extension" do
37
+ query = "query($user_id: ID!) { posts(user_id: $user_id) { id title user_id } }"
38
+
39
+ result = Schema.execute(query, variables: {'user_id' => "1"})
40
+
41
+ expect(result["data"]).to eq(
42
+ "posts" => [{
43
+ "id" => "1",
44
+ "title" => "Post Title",
45
+ "user_id" => "1"
46
+ }]
47
+ )
48
+ tracing = result.dig("extensions", 'tracing')
49
+
50
+ expect(tracing['version']).to eq(1)
51
+ expect(tracing['startTime']).to be_a(String)
52
+ expect(tracing['endTime']).to be_a(String)
53
+ expect(tracing['duration']).to be >= 0
54
+
55
+ resolvers = tracing.dig('execution', 'resolvers')
56
+
57
+ expect(resolvers.dig(0, 'path')).to eq(["posts"])
58
+ expect(resolvers.dig(0, 'parentType')).to eq("Query")
59
+ expect(resolvers.dig(0, 'fieldName')).to eq("posts")
60
+ expect(resolvers.dig(0, 'returnType')).to eq("[Post!]!")
61
+ expect(resolvers.dig(0, 'startOffset')).to be >= 0
62
+ expect(resolvers.dig(0, 'duration')).to be >= 0
63
+
64
+ expect(resolvers.dig(1, 'path')).to eq(["posts", 0, "id"])
65
+ expect(resolvers.dig(1, 'parentType')).to eq("Post")
66
+ expect(resolvers.dig(1, 'fieldName')).to eq("id")
67
+ expect(resolvers.dig(1, 'returnType')).to eq("ID!")
68
+ expect(resolvers.dig(1, 'startOffset')).to be >= 0
69
+ expect(resolvers.dig(1, 'duration')).to be >= 0
70
+
71
+ expect(resolvers.dig(2, 'path')).to eq(["posts", 0, "title"])
72
+ expect(resolvers.dig(2, 'parentType')).to eq("Post")
73
+ expect(resolvers.dig(2, 'fieldName')).to eq("title")
74
+ expect(resolvers.dig(2, 'returnType')).to eq("String!")
75
+ expect(resolvers.dig(2, 'startOffset')).to be >= 0
76
+ expect(resolvers.dig(2, 'duration')).to be >= 0
77
+
78
+ expect(resolvers.dig(3, 'path')).to eq(["posts", 0, "user_id"])
79
+ expect(resolvers.dig(3, 'parentType')).to eq("Post")
80
+ expect(resolvers.dig(3, 'fieldName')).to eq("user_id")
81
+ expect(resolvers.dig(3, 'returnType')).to eq("ID!")
82
+ expect(resolvers.dig(3, 'startOffset')).to be >= 0
83
+ expect(resolvers.dig(3, 'duration')).to be >= 0
84
+ end
85
+
86
+ it "resolves without race conditions and multiple threads by sharing vars in the context" do
87
+ thread1 = Thread.new do
88
+ query1 = "query($user_id: ID!) { posts(user_id: $user_id) { id slow_id } }"
89
+ @result1 = Schema.execute(query1, variables: {'user_id' => "1"})
90
+ end
91
+
92
+ thread2 = Thread.new do
93
+ sleep 1
94
+ query2 = "query($user_id: ID!) { posts(user_id: $user_id) { title } }"
95
+ @result2 = Schema.execute(query2, variables: {'user_id' => "1"})
96
+ end
97
+
98
+ [thread1, thread2].map(&:join)
99
+
100
+ expect(@result1.dig('extensions', 'tracing', 'execution', 'resolvers', 1, 'path')).to eq(['posts', 0, 'id'])
101
+ expect(@result1.dig('extensions', 'tracing', 'execution', 'resolvers', 2, 'path')).to eq(['posts', 0, 'slow_id'])
102
+ expect(@result2.dig('extensions', 'tracing', 'execution', 'resolvers', 1, 'path')).to eq(['posts', 0, 'title'])
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,10 @@
1
+ {
2
+ "apiKey": "service:YOUR_ENGINE_API_KEY",
3
+ "logging": { "level": "DEBUG" },
4
+ "origins": [{
5
+ "http": { "url": "http://localhost:3000/graphql" }
6
+ }],
7
+ "frontends": [{
8
+ "host": "127.0.0.1", "port": 3001, "endpoint": "/graphql"
9
+ }]
10
+ }
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Post
4
+ def self.where(user_id:)
5
+ [new(user_id: user_id)]
6
+ end
7
+
8
+ attr_accessor :id, :user_id, :title
9
+
10
+ def initialize(user_id:)
11
+ self.user_id = user_id
12
+ self.id = 1
13
+ self.title = 'Post Title'
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ PostType = GraphQL::ObjectType.define do
4
+ name "Post"
5
+
6
+ field :id, !types.ID
7
+ field :title, !types.String
8
+ field :user_id, !types.ID
9
+ field :slow_id, !types.ID, resolve: ->(obj, _, _) do
10
+ sleep 2
11
+ obj.id
12
+ end
13
+ end
14
+
15
+ QueryType = GraphQL::ObjectType.define do
16
+ name "Query"
17
+
18
+ field :posts, !types[!PostType] do
19
+ argument :user_id, !types.ID
20
+ resolve ->(obj, args, ctx) { Post.where(user_id: args[:user_id]) }
21
+ end
22
+ end
23
+
24
+ Schema = GraphQL::Schema.define do
25
+ query QueryType
26
+ use ApolloTracing.new
27
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class User
4
+ attr_accessor :id, :role
5
+
6
+ def initialize(id:, role:)
7
+ self.id = id
8
+ self.role = role
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ require "bundler/setup"
2
+ require "pry"
3
+
4
+ require "apollo_tracing"
5
+
6
+ RSpec.configure do |config|
7
+ # Enable flags like --only-failures and --next-failure
8
+ config.example_status_persistence_file_path = ".rspec_status"
9
+
10
+ config.expect_with :rspec do |c|
11
+ c.syntax = :expect
12
+ end
13
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apollo-tracing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reginald Suh
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2017-10-17 00:00:00.000000000 Z
12
+ date: 2017-10-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: graphql
@@ -103,14 +103,24 @@ files:
103
103
  - CODE_OF_CONDUCT.md
104
104
  - Gemfile
105
105
  - LICENSE.txt
106
+ - Makefile
106
107
  - README.md
107
108
  - Rakefile
108
109
  - apollo-tracing.gemspec
109
110
  - bin/console
111
+ - bin/engineproxy_darwin_amd64
112
+ - bin/engineproxy_linux_amd64
113
+ - bin/engineproxy_windows_amd64.exe
110
114
  - bin/setup
111
115
  - lib/apollo/tracing.rb
112
116
  - lib/apollo_tracing.rb
113
117
  - lib/apollo_tracing/version.rb
118
+ - spec/apollo_tracing_spec.rb
119
+ - spec/fixtures/apollo-engine-proxy.json
120
+ - spec/fixtures/post.rb
121
+ - spec/fixtures/schema.rb
122
+ - spec/fixtures/user.rb
123
+ - spec/spec_helper.rb
114
124
  homepage: https://github.com/uniiverse/apollo-tracing-ruby
115
125
  licenses:
116
126
  - MIT