apollo-tracing 1.0.0 → 1.1.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
  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