getninjas-apollo-tracing 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 44ca5e07e050320b24646b9a688983629f72ff8c
4
+ data.tar.gz: e0a0014bf7b5f778f8ef848db16676be3b6eac35
5
+ SHA512:
6
+ metadata.gz: 3422bba6a0fbb161bdbe226672511f018c9ab20d483ae225fd0c25ed0351b184c8ea8d512e3c53e84fd214a5a34824209da8b7ec117bfb11dfeb67d7c646daa0
7
+ data.tar.gz: dc9d87d08f3f696fd3edbfd6b4869fdd6d11d9da97e7fe466f2b150a638220fe920930146435c08d53c17370c90278df773d24ad4f5967fc5c526a7b1b7f38cf
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /bin/engineproxy*
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1 @@
1
+ 2.3.4
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.4
5
+ script: make download_binaries && bundle exec rspec
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ The following are lists of the notable changes included with each release.
4
+ This is intended to help keep people informed about notable changes between
5
+ versions, as well as provide a rough history. Each item is prefixed with
6
+ one of the following labels: `Added`, `Changed`, `Deprecated`,
7
+ `Removed`, `Fixed`, `Security`. We also use [Semantic Versioning](http://semver.org)
8
+ to manage the versions of this gem so
9
+ that you can set version constraints properly.
10
+
11
+ #### [Unreleased](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.6.0...HEAD)
12
+
13
+ * WIP
14
+
15
+
16
+ #### [v1.6.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.5.0...v1.6.0)
17
+
18
+ * `Changed`: Engine Proxy version to 1.1.0. [#11](https://github.com/uniiverse/apollo-tracing-ruby/pull/11)
19
+
20
+ #### [v1.5.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.4.0...v1.5.0) – 2018-03-12
21
+
22
+ * `Fixed`: Avoid trying to instrument failed query results. [#7](https://github.com/uniiverse/apollo-tracing-ruby/pull/7)
23
+ * `Changed`: Drop support for `graphql` version `1.6`. [c88e6bb](https://github.com/uniiverse/apollo-tracing-ruby/commit/c88e6bb4de575665df15714e23a5cd23755b2cf1)
24
+
25
+ #### [v1.4.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.3.0...v1.4.0) – 2018-02-09
26
+
27
+ * `Changed`: Apollo Engine Proxy version to [2018.02-2-g0b77ff3e3](https://www.apollographql.com/docs/engine/proxy-release-notes.html#2018.02-2-g0b77ff3e3) to fix using arrays as arguments. [#9](https://github.com/uniiverse/apollo-tracing-ruby/pull/9)
28
+
29
+ Please note, that if you use multiple origins:
30
+
31
+ ```
32
+ {
33
+ "origins": [
34
+ { "http": { "url": "http://localhost:3000/graphql/beta" } },
35
+ { "http": { "url": "http://localhost:3000/graphql" } }
36
+ ],
37
+ "frontends": [
38
+ { "host": "localhost", "port": 3001, "endpoint": "/graphql/beta" },
39
+ { "host": "localhost", "port": 3001, "endpoint": "/graphql" }
40
+ ]
41
+ ...
42
+ }
43
+ ```
44
+
45
+ Each origin should have a unique name now, was introduced in [2017.12-28-gcc16cbea7](https://www.apollographql.com/docs/engine/proxy-release-notes.html#2017.12-28-gcc16cbea7) Apollo Engine Proxy version:
46
+
47
+ ```
48
+ {
49
+ "origins": [
50
+ { "http": { "url": "http://localhost:3000/graphql/beta" }, "name": "graphql-beta" },
51
+ { "http": { "url": "http://localhost:3000/graphql" }, "name": "graphql" }
52
+ ],
53
+ "frontends": [
54
+ { "host": "localhost", "port": 3001, "endpoints": ["/graphql/beta", "/graphql"], "originName": "graphql" }
55
+ ]
56
+ ...
57
+ }
58
+ ```
59
+
60
+ #### [v1.3.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.2.1...v1.3.0) – 2017-11-09
61
+
62
+ * `Changed`: Apollo Engine Proxy version to [2017.11-40-g9585bfc6](https://www.apollographql.com/docs/engine/proxy-release-notes.html#2017-11-40-g9585bfc6).
63
+
64
+ #### [v1.2.1](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.2.0...v1.2.1) – 2017-10-26
65
+
66
+ * `Fixed`: bump Apollo Engine Proxy version to [2017.10-425-gdd4873ae](https://www.apollographql.com/docs/engine/proxy-release-notes.html) to remove empty `operationName` and `extensions`.
67
+
68
+ #### [v1.2.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.1.0...v1.2.0) – 2017-10-26
69
+
70
+ * `Added`: `ApolloTracing.start_proxy` accepts a JSON string. [#3](https://github.com/uniiverse/apollo-tracing-ruby/pull/3)
71
+
72
+ #### [v1.1.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v1.0.0...v1.1.0) – 2017-10-25
73
+
74
+ * `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)
75
+
76
+ #### [v1.0.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/v0.1.1...v1.0.0) – 2017-10-17
77
+
78
+ * `Changed`: the gem name from `graphql-tracing` to `apollo-tracing`.
79
+
80
+ ```ruby
81
+ # Before:
82
+
83
+ Schema = GraphQL::Schema.define do
84
+ use GraphQL::Tracing.new
85
+ end
86
+ ```
87
+
88
+ ```ruby
89
+ # After:
90
+
91
+ Schema = GraphQL::Schema.define do
92
+ use ApolloTracing.new
93
+ end
94
+ ```
95
+
96
+ #### [v0.1.1](https://github.com/uniiverse/apollo-tracing-ruby/compare/v0.1.0...v0.1.1) – 2017-10-17
97
+
98
+ * `Fixed`: naming conflicts with [graphql-ruby](https://github.com/rmosolgo/graphql-ruby/pull/996) by restricting the gem version.
99
+
100
+ #### [v0.1.0](https://github.com/uniiverse/apollo-tracing-ruby/compare/d346dd2...v0.1.0) – 2017-08-28
101
+
102
+ * `Added`: initial functional version.
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at rsuh@uwaterloo.ca. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in apollo-tracing.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Reginald
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ PROXY_VERSION := 2018.6-20-gc0e4bb519
2
+
3
+ download_binaries:
4
+ curl -O https://registry.npmjs.org/apollo-engine-binary-darwin/-/apollo-engine-binary-darwin-0.$(PROXY_VERSION).tgz
5
+ curl -O https://registry.npmjs.org/apollo-engine-binary-linux/-/apollo-engine-binary-linux-0.$(PROXY_VERSION).tgz
6
+ curl -O https://registry.npmjs.org/apollo-engine-binary-windows/-/apollo-engine-binary-windows-0.$(PROXY_VERSION).tgz
7
+ tar -xzf apollo-engine-binary-darwin-0.$(PROXY_VERSION).tgz
8
+ tar -xzf apollo-engine-binary-linux-0.$(PROXY_VERSION).tgz
9
+ tar -xzf apollo-engine-binary-windows-0.$(PROXY_VERSION).tgz
10
+ mv package/engineproxy_darwin_amd64 bin/
11
+ mv package/engineproxy_linux_amd64 bin/
12
+ mv package/engineproxy_windows_amd64.exe bin/
13
+ rm -r package/
14
+ rm apollo-engine-binary-darwin-0.$(PROXY_VERSION).tgz
15
+ rm apollo-engine-binary-linux-0.$(PROXY_VERSION).tgz
16
+ rm apollo-engine-binary-windows-0.$(PROXY_VERSION).tgz
17
+
18
+ release: download_binaries
19
+ bundle exec rake release
@@ -0,0 +1,258 @@
1
+ # Apollo Tracing
2
+
3
+ [![Build Status](https://travis-ci.org/uniiverse/apollo-tracing-ruby.svg?branch=master)](https://travis-ci.org/uniiverse/apollo-tracing-ruby)
4
+ [![Latest Version](https://img.shields.io/gem/v/apollo-tracing.svg)](https://rubygems.org/gems/apollo-tracing)
5
+
6
+ Ruby implementation of [GraphQL](https://github.com/rmosolgo/graphql-ruby) trace data in the [Apollo Tracing](https://github.com/apollographql/apollo-tracing) format.
7
+
8
+
9
+ ## Contents
10
+
11
+ * [Installation](#installation)
12
+ * [Usage](#usage)
13
+ * [Tracing](#tracing)
14
+ * [Engine Proxy](#engine-proxy)
15
+ * [Development](#development)
16
+ * [Contributing](#contributing)
17
+ * [License](#license)
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'apollo-tracing'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install apollo-tracing
34
+
35
+ ## Usage
36
+
37
+ Define a GraphQL schema:
38
+
39
+ ```ruby
40
+ # Define a type
41
+ PostType = GraphQL::ObjectType.define do
42
+ name "Post"
43
+
44
+ field :id, !types.ID
45
+ field :title, !types.String
46
+ end
47
+
48
+ # Define a query
49
+ QueryType = GraphQL::ObjectType.define do
50
+ name "Query"
51
+
52
+ field :posts, !types[PostType] do
53
+ argument :user_id, !types.ID
54
+ resolve ->(obj, args, ctx) { Post.where(user_id: args[:user_id]) }
55
+ end
56
+ end
57
+
58
+ # Define a schema
59
+ Schema = GraphQL::Schema.define do
60
+ query QueryType
61
+ end
62
+
63
+ # Execute query
64
+ query = "
65
+ query($user_id: ID!) {
66
+ posts(user_id: $user_id) {
67
+ id
68
+ title
69
+ }
70
+ }
71
+ "
72
+ Schema.execute(query, variables: { user_id: 1 })
73
+ ```
74
+
75
+ ### Tracing
76
+
77
+ Add 'ApolloTracing' to your schema:
78
+
79
+ <pre>
80
+ <b>require "apollo/tracing"</b>
81
+
82
+ Schema = GraphQL::Schema.define do
83
+ query QueryType
84
+ <b>use ApolloTracing.new</b>
85
+ end
86
+ </pre>
87
+
88
+ Now your response should look something like:
89
+ ```
90
+ {
91
+ "data":{
92
+ "posts":[
93
+ {
94
+ "id":"1",
95
+ "title":"Post Title"
96
+ }
97
+ ]
98
+ },
99
+ "extensions":{
100
+ "tracing":{
101
+ "version":1,
102
+ "startTime":"2017-08-25T19:55:04.821Z",
103
+ "endTime":"2017-08-25T19:55:04.823Z",
104
+ "duration":1702785,
105
+ "execution":{
106
+ "resolvers":[
107
+ {
108
+ "path":[
109
+ "posts"
110
+ ],
111
+ "parentType":"Query",
112
+ "fieldName":"posts",
113
+ "returnType":"[Post!]!",
114
+ "startOffset":1451015,
115
+ "duration":15735
116
+ },
117
+ {
118
+ "path":[
119
+ "posts",
120
+ 0,
121
+ "id"
122
+ ],
123
+ "parentType":"Post",
124
+ "fieldName":"id",
125
+ "returnType":"ID!",
126
+ "startOffset":1556873,
127
+ "duration":6914
128
+ },
129
+ {
130
+ "path":[
131
+ "posts",
132
+ 0,
133
+ "title"
134
+ ],
135
+ "parentType":"Post",
136
+ "fieldName":"title",
137
+ "returnType":"String!",
138
+ "startOffset":1604795,
139
+ "duration":4053
140
+ },
141
+ {
142
+ "path":[
143
+ "posts",
144
+ 0,
145
+ "user_id"
146
+ ],
147
+ "parentType":"Post",
148
+ "fieldName":"user_id",
149
+ "returnType":"ID!",
150
+ "startOffset":1642942,
151
+ "duration":3814
152
+ }
153
+ ]
154
+ }
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ### Engine Proxy
161
+
162
+ Now you can start using the [Apollo Engine](https://www.apollographql.com/engine/) service.
163
+ Here is the general architecture overview of a sidecar mode – Proxy runs next to your application server:
164
+
165
+ ```
166
+ ----------------- request ----------------- request -----------------
167
+ | | -----------> | | -----------> | |
168
+ | Client | | Engine Proxy | | Application |
169
+ | | <----------- | | <----------- | |
170
+ ----------------- response ----------------- response -----------------
171
+ |
172
+ |
173
+ GraphQL tracing | from response
174
+ |
175
+ ˅
176
+ -----------------
177
+ | |
178
+ | Apollo Engine |
179
+ | |
180
+ -----------------
181
+ ```
182
+
183
+ `ApolloTracing` gem comes with the [Apollo Engine Proxy](https://www.apollographql.com/docs/engine/index.html#engine-proxy) binary written in Go.
184
+ To configure the Proxy create a Proxy config file:
185
+
186
+ ```
187
+ # config/apollo-engine-proxy.json
188
+
189
+ {
190
+ "apiKey": "service:YOUR_ENGINE_API_KEY",
191
+ "logging": { "level": "INFO" },
192
+ "origins": [{
193
+ "http": { "url": "http://localhost:3000/graphql" }
194
+ }],
195
+ "frontends": [{
196
+ "host": "localhost", "port": 3001, "endpoints": ["/graphql"]
197
+ }]
198
+ }
199
+ ```
200
+
201
+ * `apiKey` – get this on your [Apollo Engine](https://engine.apollographql.com/) home page.
202
+ * `logging.level` – a log level for the Proxy ("INFO", "DEBUG" or "ERROR").
203
+ * `origins` – a list of URLs with your GraphQL endpoints in the Application.
204
+ * `frontends` – an address on which the Proxy will be listening.
205
+
206
+ 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:
207
+
208
+ <pre>
209
+ # config.ru – this file is used by Rack-based servers to start the application
210
+ require File.expand_path('../config/environment', __FILE__)
211
+
212
+ <b>ApolloTracing.start_proxy('config/apollo-engine-proxy.json')</b>
213
+ # or pass a JSON string:
214
+ # ApolloTracing.start_proxy('{"apiKey": "KEY", ...}')
215
+
216
+ run Your::Application
217
+ </pre>
218
+
219
+ For example, if you use [rails](https://github.com/rails/rails) with [puma](https://github.com/puma/puma) application server and run it like:
220
+
221
+ ```
222
+ bundle exec puma -w 2 -t 16 -p 3000
223
+ ```
224
+
225
+ The proccess tree may look like:
226
+
227
+ ```
228
+ ---------------
229
+ | Puma Master |
230
+ | Port 3000 |
231
+ ---------------
232
+ | |
233
+ ---------- ----------
234
+ | | ----------------
235
+ ˅ -> | Puma Worker1 |
236
+ ---------------- | -----------------
237
+ | Engine Proxy | | ----------------
238
+ | Port 3001 | -> | Puma Worker2 |
239
+ ---------------- ----------------
240
+ ```
241
+
242
+ Now you can send requests to the reverse Proxy `http://localhost:3001`.
243
+ It'll proxy any (GraphQL and non-GraphQL) requests to the Application `http://localhost:3000`.
244
+ 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.
245
+
246
+ ## Development
247
+
248
+ 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.
249
+
250
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
251
+
252
+ ## Contributing
253
+
254
+ Bug reports and pull requests are welcome on GitHub at https://github.com/uniiverse/apollo-tracing-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
255
+
256
+ ## License
257
+
258
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'apollo_tracing/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "getninjas-apollo-tracing"
8
+ spec.version = ApolloTracing::VERSION
9
+ spec.authors = ["Reginald Suh", "Evgeny Li"]
10
+ spec.email = ["tech@getninjas.com.br"]
11
+
12
+ spec.summary = %q{Ruby implementation of GraphQL trace data in the Apollo Tracing format.}
13
+ spec.description = %q{Ruby implementation of GraphQL trace data in the Apollo Tracing format.}
14
+ spec.homepage = "https://github.com/getninjas/apollo-tracing-ruby"
15
+ spec.license = "MIT"
16
+
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
+
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.required_ruby_version = '>= 2.1.0' # keyword args
30
+
31
+ spec.add_runtime_dependency "graphql", ">= 1.7.0", "< 2"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.14"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_development_dependency "pry"
37
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "apollo_tracing"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+ make download_binaries
8
+
9
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../apollo_tracing'
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql"
4
+ require "apollo_tracing/version"
5
+
6
+ class ApolloTracing
7
+ def self.start_proxy(config_filepath_or_json = 'config/apollo-engine.json')
8
+ config_json =
9
+ if File.exist?(config_filepath_or_json)
10
+ File.read(config_filepath_or_json)
11
+ else
12
+ config_filepath_or_json
13
+ end
14
+ binary_path =
15
+ if RUBY_PLATFORM.include?('darwin')
16
+ File.expand_path('../../bin/engineproxy_darwin_amd64', __FILE__)
17
+ elsif /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
18
+ File.expand_path('../../bin/engineproxy_windows_amd64.exe', __FILE__)
19
+ else
20
+ File.expand_path('../../bin/engineproxy_linux_amd64', __FILE__)
21
+ end
22
+
23
+ @@proxy_pid = spawn(
24
+ {"ENGINE_CONFIG" => config_json},
25
+ "#{binary_path} -config=env",
26
+ {out: STDOUT, err: STDERR}
27
+ )
28
+ at_exit { stop_proxy }
29
+ Process.detach(@@proxy_pid)
30
+ @@proxy_pid
31
+ end
32
+
33
+ def self.stop_proxy
34
+ Process.getpgid(@@proxy_pid)
35
+ Process.kill('TERM', @@proxy_pid)
36
+
37
+ 3.times do
38
+ Process.getpgid(@@proxy_pid)
39
+ sleep 1
40
+ end
41
+
42
+ Process.getpgid(@@proxy_pid)
43
+ puts "Couldn't cleanly terminate the Apollo Engine Proxy in 3 seconds!"
44
+ Process.kill('KILL', @@proxy_pid)
45
+ rescue Errno::ESRCH
46
+ # process does not exist
47
+ end
48
+
49
+ def use(schema_definition)
50
+ schema_definition.instrument(:query, self)
51
+ schema_definition.instrument(:field, self)
52
+ end
53
+
54
+ def before_query(query)
55
+ query.context['apollo-tracing'] = {
56
+ 'start_time' => Time.now.utc,
57
+ 'resolvers' => []
58
+ }
59
+ end
60
+
61
+ def after_query(query)
62
+ result = query.result
63
+ return if result.nil? || result.to_h.nil?
64
+ end_time = Time.now.utc
65
+ duration_nanos = duration_nanos(start_time: query.context['apollo-tracing']['start_time'], end_time: end_time)
66
+
67
+ result["extensions"] ||= {}
68
+ result["extensions"]["tracing"] = {
69
+ "version" => 1,
70
+ "startTime" => query.context['apollo-tracing']['start_time'].strftime('%FT%T.%3NZ'),
71
+ "endTime" => end_time.strftime('%FT%T.%3NZ'),
72
+ "duration" => duration_nanos,
73
+ "execution" => {
74
+ "resolvers" => query.context['apollo-tracing']['resolvers']
75
+ }
76
+ }
77
+ end
78
+
79
+ def instrument(type, field)
80
+ old_resolve_proc = field.resolve_proc
81
+
82
+ new_resolve_proc = ->(obj, args, ctx) do
83
+ resolve_start_time = Time.now.utc
84
+ result = old_resolve_proc.call(obj, args, ctx)
85
+ resolve_end_time = Time.now.utc
86
+
87
+ ctx['apollo-tracing']['resolvers'] << {
88
+ 'path' => ctx.path,
89
+ 'parentType' => type.name,
90
+ 'fieldName' => field.name,
91
+ 'returnType' => field.type.to_s,
92
+ 'startOffset' => duration_nanos(start_time: ctx['apollo-tracing']['start_time'], end_time: resolve_start_time),
93
+ 'duration' => duration_nanos(start_time: resolve_start_time, end_time: resolve_end_time)
94
+ }
95
+
96
+ result
97
+ end
98
+
99
+ field.redefine { resolve(new_resolve_proc) }
100
+ end
101
+
102
+ private
103
+
104
+ def duration_nanos(start_time:, end_time:)
105
+ ((end_time.to_f - start_time.to_f) * 1e9).to_i
106
+ end
107
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApolloTracing
4
+ VERSION = "1.6.0"
5
+ end
@@ -0,0 +1,121 @@
1
+ require "spec_helper"
2
+
3
+ require 'fixtures/user'
4
+ require 'fixtures/post'
5
+ require 'fixtures/schema'
6
+ require 'fixtures/broken_schema'
7
+
8
+ RSpec.describe ApolloTracing do
9
+ describe '.start_proxy' do
10
+ it 'runs a proxy' do
11
+ pid = ApolloTracing.start_proxy('spec/fixtures/apollo-engine-proxy.json')
12
+ expect { Process.getpgid(pid) }.not_to raise_error
13
+ ApolloTracing.stop_proxy
14
+ end
15
+
16
+ it 'runs a proxy with a given JSON instead of a file path' do
17
+ config_json = File.read('spec/fixtures/apollo-engine-proxy.json')
18
+ pid = ApolloTracing.start_proxy(config_json)
19
+ expect { Process.getpgid(pid) }.not_to raise_error
20
+ ApolloTracing.stop_proxy
21
+ end
22
+ end
23
+
24
+ describe '.stop_proxy' do
25
+ it 'stops a proxy' do
26
+ pid = ApolloTracing.start_proxy('spec/fixtures/apollo-engine-proxy.json')
27
+ ApolloTracing.stop_proxy
28
+ expect { Process.getpgid(pid) }.to raise_error(Errno::ESRCH, 'No such process')
29
+ end
30
+ end
31
+
32
+ context 'introspection' do
33
+ it 'supports a nil result for failures' do
34
+ query = 'query($user_id: ID!) { posts(user_id: $user_id) { id title user_id } }'
35
+
36
+ expect {
37
+ BrokenSchema.execute(query, variables: { 'user_id' => '1' })
38
+ }.to raise_error(NoMethodError, /undefined method `title' for/)
39
+ end
40
+
41
+ it 'returns time in RFC 3339 format' do
42
+ query = "query($user_id: ID!) { posts(user_id: $user_id) { id title user_id } }"
43
+ now = Time.new(2017, 8, 25, 0, 0, 0, '+00:00')
44
+ allow(Time).to receive(:now).and_return(now)
45
+
46
+ result = Schema.execute(query, variables: {'user_id' => "1"})
47
+
48
+ expect(result.dig("extensions", 'tracing', 'startTime')).to eq('2017-08-25T00:00:00.000Z')
49
+ expect(result.dig("extensions", 'tracing', 'endTime')).to eq('2017-08-25T00:00:00.000Z')
50
+ end
51
+
52
+ it "resolves graphql query with tracing extension" do
53
+ query = "query($user_id: ID!) { posts(user_id: $user_id) { id title user_id } }"
54
+
55
+ result = Schema.execute(query, variables: {'user_id' => "1"})
56
+
57
+ expect(result["data"]).to eq(
58
+ "posts" => [{
59
+ "id" => "1",
60
+ "title" => "Post Title",
61
+ "user_id" => "1"
62
+ }]
63
+ )
64
+ tracing = result.dig("extensions", 'tracing')
65
+
66
+ expect(tracing['version']).to eq(1)
67
+ expect(tracing['startTime']).to be_a(String)
68
+ expect(tracing['endTime']).to be_a(String)
69
+ expect(tracing['duration']).to be >= 0
70
+
71
+ resolvers = tracing.dig('execution', 'resolvers')
72
+
73
+ expect(resolvers.dig(0, 'path')).to eq(["posts"])
74
+ expect(resolvers.dig(0, 'parentType')).to eq("Query")
75
+ expect(resolvers.dig(0, 'fieldName')).to eq("posts")
76
+ expect(resolvers.dig(0, 'returnType')).to eq("[Post!]!")
77
+ expect(resolvers.dig(0, 'startOffset')).to be >= 0
78
+ expect(resolvers.dig(0, 'duration')).to be >= 0
79
+
80
+ expect(resolvers.dig(1, 'path')).to eq(["posts", 0, "id"])
81
+ expect(resolvers.dig(1, 'parentType')).to eq("Post")
82
+ expect(resolvers.dig(1, 'fieldName')).to eq("id")
83
+ expect(resolvers.dig(1, 'returnType')).to eq("ID!")
84
+ expect(resolvers.dig(1, 'startOffset')).to be >= 0
85
+ expect(resolvers.dig(1, 'duration')).to be >= 0
86
+
87
+ expect(resolvers.dig(2, 'path')).to eq(["posts", 0, "title"])
88
+ expect(resolvers.dig(2, 'parentType')).to eq("Post")
89
+ expect(resolvers.dig(2, 'fieldName')).to eq("title")
90
+ expect(resolvers.dig(2, 'returnType')).to eq("String!")
91
+ expect(resolvers.dig(2, 'startOffset')).to be >= 0
92
+ expect(resolvers.dig(2, 'duration')).to be >= 0
93
+
94
+ expect(resolvers.dig(3, 'path')).to eq(["posts", 0, "user_id"])
95
+ expect(resolvers.dig(3, 'parentType')).to eq("Post")
96
+ expect(resolvers.dig(3, 'fieldName')).to eq("user_id")
97
+ expect(resolvers.dig(3, 'returnType')).to eq("ID!")
98
+ expect(resolvers.dig(3, 'startOffset')).to be >= 0
99
+ expect(resolvers.dig(3, 'duration')).to be >= 0
100
+ end
101
+
102
+ it "resolves without race conditions and multiple threads by sharing vars in the context" do
103
+ thread1 = Thread.new do
104
+ query1 = "query($user_id: ID!) { posts(user_id: $user_id) { id slow_id } }"
105
+ @result1 = Schema.execute(query1, variables: {'user_id' => "1"})
106
+ end
107
+
108
+ thread2 = Thread.new do
109
+ sleep 1
110
+ query2 = "query($user_id: ID!) { posts(user_id: $user_id) { title } }"
111
+ @result2 = Schema.execute(query2, variables: {'user_id' => "1"})
112
+ end
113
+
114
+ [thread1, thread2].map(&:join)
115
+
116
+ expect(@result1.dig('extensions', 'tracing', 'execution', 'resolvers', 1, 'path')).to eq(['posts', 0, 'id'])
117
+ expect(@result1.dig('extensions', 'tracing', 'execution', 'resolvers', 2, 'path')).to eq(['posts', 0, 'slow_id'])
118
+ expect(@result2.dig('extensions', 'tracing', 'execution', 'resolvers', 1, 'path')).to eq(['posts', 0, 'title'])
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,10 @@
1
+ {
2
+ "apiKey": "service:YOUR_ENGINE_API_KEY",
3
+ "logging": { "level": "INFO" },
4
+ "origins": [{
5
+ "http": { "url": "http://localhost:3000/graphql" }
6
+ }],
7
+ "frontends": [{
8
+ "host": "localhost", "port": 3001, "endpoint": "/graphql"
9
+ }]
10
+ }
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ BadPostType = GraphQL::ObjectType.define do
6
+ name 'Foo'
7
+ description 'See also PostType, for a working version of this'
8
+
9
+ field :id, !types.String, hash_key: :id
10
+ field :user_id, !types.String, hash_key: :user_id
11
+ field :title, !types.String # This is the intended broken-ness: missing a hash_key setting
12
+ end
13
+
14
+ BrokenQueryType = GraphQL::ObjectType.define do
15
+ name 'BrokenQuery'
16
+ description 'See also QueryType, for a working version of this'
17
+ field :posts, !types[!BadPostType] do
18
+ argument :user_id, !types.ID
19
+ resolve ->(_obj, _args, _ctx) {
20
+ [ { id: 'foo1', title: 'titel1', user_id: 'Sven'} ]
21
+ }
22
+ end
23
+ end
24
+
25
+ BrokenSchema = GraphQL::Schema.define do
26
+ query BrokenQueryType
27
+ use ApolloTracing.new
28
+ end
@@ -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 ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: getninjas-apollo-tracing
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.6.0
5
+ platform: ruby
6
+ authors:
7
+ - Reginald Suh
8
+ - Evgeny Li
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2018-06-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: graphql
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 1.7.0
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '2'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: 1.7.0
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ - !ruby/object:Gem::Dependency
35
+ name: bundler
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ type: :development
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.14'
48
+ - !ruby/object:Gem::Dependency
49
+ name: rake
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ - !ruby/object:Gem::Dependency
77
+ name: pry
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ description: Ruby implementation of GraphQL trace data in the Apollo Tracing format.
91
+ email:
92
+ - tech@getninjas.com.br
93
+ executables: []
94
+ extensions: []
95
+ extra_rdoc_files: []
96
+ files:
97
+ - ".gitignore"
98
+ - ".rspec"
99
+ - ".ruby-version"
100
+ - ".travis.yml"
101
+ - CHANGELOG.md
102
+ - CODE_OF_CONDUCT.md
103
+ - Gemfile
104
+ - LICENSE.txt
105
+ - Makefile
106
+ - README.md
107
+ - Rakefile
108
+ - apollo-tracing.gemspec
109
+ - bin/console
110
+ - bin/engineproxy_darwin_amd64
111
+ - bin/engineproxy_linux_amd64
112
+ - bin/engineproxy_windows_amd64.exe
113
+ - bin/setup
114
+ - lib/apollo/tracing.rb
115
+ - lib/apollo_tracing.rb
116
+ - lib/apollo_tracing/version.rb
117
+ - spec/apollo_tracing_spec.rb
118
+ - spec/fixtures/apollo-engine-proxy.json
119
+ - spec/fixtures/broken_schema.rb
120
+ - spec/fixtures/post.rb
121
+ - spec/fixtures/schema.rb
122
+ - spec/fixtures/user.rb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/getninjas/apollo-tracing-ruby
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 2.1.0
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.5.2
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: Ruby implementation of GraphQL trace data in the Apollo Tracing format.
148
+ test_files: []